-
Notifications
You must be signed in to change notification settings - Fork 68
Update Asset Preparation + Token Type Guides #992
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
2e6c80b
f1edb46
ba9cc88
783ed5f
3657b50
97201b7
5444fa1
6ba3d6f
c26bbcd
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,3 @@ | ||
| label: '🖼️ Working with Assets' | ||
| collapsed: true | ||
| position: 5 |
| 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/). | ||
| 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
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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?
Member
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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. | ||
|
|
||
| ::: | ||
Uh oh!
There was an error while loading. Please reload this page.