Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
3 changes: 3 additions & 0 deletions docs/learn/assets/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
label: '🖼️ Working with Assets'
collapsed: true
position: 5
189 changes: 189 additions & 0 deletions docs/learn/assets/deploy-token-with-metadata.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,189 @@
---
sidebar_label: '💽 Deploy Token with Metadata'
description: How to set LSP4 Metadata of digital assets on contract deployment.
sidebar_position: 3
---

import Tabs from '@theme/Tabs';
import TabItem from '@theme/TabItem';

# Deploy Token with Metadata

:::tip Code repository

All the contracts, sample metadata, and scripts of this guide are available in the [`lukso-playground`](https://github.com/lukso-network/lukso-playground/tree/main/smart-contracts-hardhat) repository.

:::

:::info Info

For demonstration purpose, the code in this guide deploys `LSP7Mintable`, a preset token contract available in the `@lukso/lsp-smart-contracts` package.

Feel free to use the bytecode of any custom or extended token contract you have created. You can then adjust any extra constructor parameters as per your needs in step 2.1.

:::

In this guide, you will learn how to:

1. Use your Universal Profile to deploy an [LSP7 Digital Asset](../../standards/tokens/LSP7-Digital-Asset.md) contract.
2. Set the [metadata](../../standards/tokens/LSP4-Digital-Asset-Metadata.md) of the token while deploying it.

We will achieve this in a single transaction using the [`executeBatch`](../../contracts/contracts/LSP0ERC725Account/LSP0ERC725Account.md#executebatch) function on the Universal Profile. The batch will contain the two actions defined above that will run successively.

## 1 - Connect to your Universal Profile

First, connect your script to the Universal Profile Browser Extension:

```ts title="scripts/deployTokenWithMetadataAsUP.ts"
import { ethers, Contract } from 'ethers';
import UniversalProfileArtifact from '@lukso/lsp-smart-contracts/artifacts/UniversalProfile.json';

// Connect to your Universal Profile from your dApp
const provider = new ethers.BrowserProvider(window.lukso);
const universalProfile = await provider.getSigner();
console.log('Connected to 🆙 at address: ', universalProfile.address);

// Create an instance of the Universal Profile
const universalProfile = new Contract(
universalProfile,
UniversalProfileArtifact.abi,
signer,
);
```

## 2 - Prepare the transactions payloads

Next, we will prepare the payloads to:

1. deploy the token contract with some deployment parameters (token name, symbol, etc...).
2. set the metadata on the token contract.

Once we have ABI encoded these payloads, we will pass each of them to the `executeBatch` function below.

### 2.1 - Generate the bytecode of the Token contract to deploy

To achieve this, we will:

1. Encode the constructor parameters used when deploying the token contract.
2. Generate the bytecode for the contract deployment.

```ts title="scripts/deployTokenWithMetadataAsUP.ts"
import { ethers, Contract } from 'ethers';
import LSP7Mintable from '@lukso/lsp-smart-contracts/artifacts/LSP7Mintable.json';

// Create custom bytecode for the token deployment
const tokenBytecode = LSP7Mintable.bytecode;

// 1. Encode constructor parameters
const abiEncoder = new ethers.AbiCoder();

const encodedConstructorParams = abiEncoder.encode(
['string', 'string', 'address', 'uint256', 'bool'],
[
'My Custom Token', // token name
'MCT', // token symbol
universalProfile.address, // token owner (this variable is from the previous code snippet)
0, // token type = TOKEN
false, // isNonDivisible?
],
);

// 2. Generate the bytecode for the token contract deployment.
// This is done by appending the constructor parameters to the token bytecode.
const tokenBytecodeWithConstructor = ethers.concat([
tokenBytecode,
encodedConstructorParams,
]);
```

### 2.2 - Retrieve the future token address

The next step in the batch is to set metadata on the token contract. But you might be wondering:

> _"How can we set the token metadata since we don't know at which address the token contract has been deployed yet?"_

To know the future token contract address, we have to mimic deploying the contract from the UP but use `staticCall` to not dispatch the transaction and obtain the returned value: **this will be the future token contract address**.

Call the [`execute()`](../../contracts/contracts/ERC725/ERC725.md#execute) function on the Universal Profile with `staticCall` to obtain the address the token contract will be deployed at. To do so, we will pass the contract's bytecode (including its deployment parameters) as the 4th parameter.

```ts
// Get the address of the custom token contract that will be created
const customTokenAddress = await universalProfile.execute.staticCall(
1, // Operation type: CREATE
ethers.ZeroAddress, // Target: use zero address for contract deployment
0, // Value: used to fund any contract on deployment
tokenBytecodeWithConstructor, // Payload: the creation bytecode of the contract to deploy
);
```

### 2.3 - Prepare the payload to set the token metadata

The next steps are:

1. Encode the token metadata.
2. Generate the payload to call `setData` on the token contract.

We will use the [`erc725.js`](../../tools/erc725js/getting-started.md) library to encode easily the value we want to set for the `LSP4Metadata` on the token contract.

```ts
import { ethers } from 'ethers';
import { ERC725 } from '@erc725/erc725.js';
import LSP4DigitalAssetSchema from '@erc725/erc725js/schemas/LSP4DigitalAssetMetadata.json';
import LSP7Mintable from '@lukso/lsp-smart-contracts/artifacts/LSP7Mintable.json';

import lsp4SampleMetadata from './lsp4SampleMetadata.json';

// Encode the metadata for deployment
const encodedLSP4Metadata = ERC725.encodeData([
{
keyName: 'LSP4Metadata',
value: {
json: lsp4SampleMetadata,
url: 'ipfs://...',
},
},
]);

// Set up the token contract
const token = new ethers.Interface(LSP7Mintable.abi);

// Create the transaction payload for setting storage data
const setLSP4MetadataPayload = token.interface.encodeFunctionData('setData', [
encodedLSP4Metadata.keys[0], // LSP4Metadata data key
encodedLSP4Metadata.values[0], // value as an encoded VerifiableURI
]);
```

## 3 - Deploy + Set Metadata in one transaction with `executeBatch`

After having encoded both payloads, you can execute each of them through the [`executeBatch()`](../../contracts/contracts/ERC725/ERC725.md#executebatch) function on the Universal Profile.

On the first call, you have to set the transaction target to the **zero address**, to deploy the token contract. The second call will then use the [previously generated contract address](#prepare-the-transaction-payloads) from the `staticCall` in order to set the metadata.

```ts title="scripts/deployTokenWithMetadataAsUP.ts"
// Previous code ...

// Deploy the contract by the Universal Profile
const tx = await universalProfile.executeBatch(
[
// Array of Operation types
1, // Operation type: CREATE (Contract deployment)
0, // Operation type: CALL (Set storage key on contract)
],
[
ethers.ZeroAddress, // 0x0000...0000 for contract deployment
customTokenAddress, // (Step 2.2) Address of the token contract to call after it was deployed
],
[0, 0], // Value is empty for both operations
[
tokenBytecodeWithConstructor, // (Step 2.1) Payload for contract deployment
setLSP4MetadataPayload, // (Step 2.3) Payload for setting a data key on the deployed contract
],
);

// Wait for the transaction to be included in a block
await tx.wait();
console.log('Token deployed at: ', customTokenAddress);
```

You can then run the deployment script from your dApp interface. After the deployment, you will be able to check the contract on the [Execution Explorer](https://explorer.execution.testnet.lukso.network/).
171 changes: 171 additions & 0 deletions docs/learn/assets/metadata-preparation.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
---
sidebar_label: '🧰 Metadata Preparation'
sidebar_position: 1
description: Learn how to prepare and use assets for LUKSO Universal Profiles and digital assets (LSP7 / LSP8).
---

# Metadata Preparation

:::info LSP4 Metadata

When creating or editing Universal Profiles or Digital Assets, you will first need to upload assets such as images, icons, videos, etc. These assets can then be linked within the [Metadata JSON File](../../standards/tokens/LSP4-Digital-Asset-Metadata.md) that will be attached to the smart contract.

:::

This guide will walk you through all necessary steps to prepare the asset data.

## Metadata Preparation Flow

To add metadata to your contract, you have to follow these steps:

1. Upload each media file (icon, picture, image, video, etc) and get it's `URLs` or `IPFS CID`.
2. Use the file hashes and URLs to generate the final [LSP4 Metadata JSON File](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-4-DigitalAsset-Metadata.md)
3. Upload the [LSP4 Metadata JSON File](https://github.com/lukso-network/LIPs/blob/main/LSPs/LSP-4-DigitalAsset-Metadata.md) to get its `URL` or `IPFS CID`.
4. Encode the LSP4 Metadata JSON file URL as a `VerifiableURI`.
5. Write the reference to the JSON file on the contract.

## 1 - File Uploads

:::tip Storage Solution

LSPs do not restrict you to a specific storage solution.

:::

To upload the files (assets and metadata JSON file), you have mainly two options:

| Solution | Description | Examples |
| --------------------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------------------- |
| Centralized Storage | Centralized storage solutions are user-friendly and can be a good starting point for those new to the ecosystem or participating in hackathons. However, they rely on a single point of control, which may not align with the decentralized ethos of blockchain technology. | [AWS S3](https://aws.amazon.com/s3/), [Google Cloud](https://cloud.google.com/storage?hl=en), [Dropbox](https://www.dropbox.com/), etc. |
| Decentralized Storage | Decentralized storage solutions align with the decentralized nature of blockchain and offer benefits like _redundancy_, _censorship resistance_, and _permanent storage_. However, they might be more complex to interact with. | [IPFS](https://ipfs.tech/), [Arweave](https://www.arweave.org/), [Filecoin](https://filecoin.io/), etc. |

## 2 - Generate the JSON File

After uploading the media files, you can attach their links to a JSON File in the following structure:

<details>
<summary>LSP4 Example JSON File</summary>

```js
{
"LSP4Metadata": {
"name": "My Token Name",
"description": "Sample Description",
"links": [{ "title": "My Website", "url": "https://my.website.com" }],
"icon": [
{
"width": 60,
"height": 60,
"url": "https://mycentralised-storage.com/filename.png"
}
],
"images": [
[
{
"width": 1000,
"height": 1000,
"url": "https://centralised-cloud-storage.com/image.jpg",
"verification": {
"method": "keccak256(bytes)",
"data": "0x<hashOfTheUploadedFile>"
}

}
{
"width": 500,
"height": 500,
"url": "ipfs://[IPFS-CID]",
"verification": {
"method": "keccak256(bytes)",
"data": "0x<hashOfTheUploadedFile>"
}

}
]
],
"assets": [],
"attributes": [
{
"key": "Standard type",
"value": "LSP",
"type": "string"
},
{
"key": "Standard number",
"value": 4,
"type": "number"
}
]
}
}
```

</details>

The JSON content is then used as input for encoding the metadata according to the [LSP4](../../standards/tokens/LSP4-Digital-Asset-Metadata.md#lsp4---digital-asset-metadata) standard. After you filled your metadata file with content, upload it to a preferred storage solution.

:::info Data Verification

To ensure the authenticity of the images, please **generate the hash** of the uploaded files and set them within the `"verification"` field of the JSON Metadata:

- Define **the used hash function** within the `"method"` element
- Add and **generated hexadecimal hash** within the `"data"` element

Comment on lines +109 to +113
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We should provide a code snippet on how to do that what do you think?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Creating an issue for this to do that in a separate PR. This PR has been opened for quite some time

:::

## 3 - Encode the LSP4 Metadata

:::tip Convenience Tool

You can use the 🛠️ [`erc725.js`](../../tools/erc725js/getting-started.md) library
to encode the LSP4 Metadata. The tool provides all necessary LSP schemas as well as the `encodeData()` function.

:::

```js
import { ERC725 } from '@erc725/erc725.js';
import LSP4DigitalAssetSchema from '@erc725/erc725.js/schemas/LSP4DigitalAsset.json';

const LSP4JSON = {
/* Your JSON */
};

const encodedLSP4Metadata = ERC725.encodeData(
{
keyName: 'LSP4Metadata',
value: {
json: LSP4JSON,
url: 'https://my-file-provider.com/my-file-link.json', // It can also be: ipfs://[CID]
},
},
LSP4DigitalAssetSchema,
);
```

The encoded content will then look like the following:

<details>
<summary>LSP4 Encoded Contract Metadata</summary>

```js
{
keys: [
'0x9afb95cacc9f95858ec44aa8c3b685511002e30ae54415823f406128b85b238e'
],
values: [
'0x00006f357c6a0020610be5a5ebf25a8323ed5a9d8735f78aaf97c7e3529da7249f17e1b4129636f3697066733a2f2f516d5154716865424c5a466e5155787535524473387441394a746b78665a714d42636d47643973756b587877526d'
]
}
```

</details>

## 4 - Adding the Metadata

After you retrieved the key and value of your [ERC725Y](../../standards/lsp-background/erc725#erc725y-generic-data-keyvalue-store) data key, you can call the [`setData()`](../../contracts/contracts/ERC725/ERC725.md#setdata) function of the asset to update it's metadata on the contract.

:::tip Sample Implementation

You can follow our [Deploy Token With Metadata](./deploy-token-with-metadata.md) and [Set NFT Metadata](../dapp-developer/set-nft-metadata.md) Guides to set or update contract metadata.

:::
Loading