diff --git a/docs/contracts/overview/DigitalAssets.md b/docs/contracts/overview/DigitalAssets.md deleted file mode 100644 index 4290b8ae5..000000000 --- a/docs/contracts/overview/DigitalAssets.md +++ /dev/null @@ -1,149 +0,0 @@ ---- -title: 🪙 Digital Asset (Token) -sidebar_position: 4 ---- - -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# Digital Assets - -The **Digital Asset (Token and NFT 2.0)** contracts are the newest advanced version of the existing token standards. They come with many features that enhance the security and the overall user experience and compatibility with [ERC725Accounts](/standards/accounts/lsp0-erc725account.md) and [Universal Receivers](/standards/accounts/lsp1-universal-receiver.md). - -## Comparisons with ERC20 / ERC721 - -:::danger beware - -The LSP7 compatible with ERC20 contracts and LSP8 compatible with ERC721 contracts are being deprecated and will be deleted from [`@lukso/lsp-smart-contracts`](https://github.com/lukso-network/lsp-smart-contracts) repository. However if you want to still use/maintain them, they will remain available in the version [`0.14.0`](https://github.com/lukso-network/lsp-smart-contracts/releases/tag/lsp-smart-contracts-v0.14.0). - -::: - -The interfaces of LSP7 and LSP8 have some differences compared to ERC20 and ERC721. Their functions are simpler, more straight forward and unified. - -**Similar function names** - -Both functions in LSP7 and LSP8 have the same name (`transfer`) to transfer assets. This is easier compared to ERC20 and ERC721 that use different naming (`transfer` for ERC20 vs `transferFrom` in ERC721 to transfer tokens as the token owner). - -The table below highlights these differences: - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
DescriptionERC20LSP7
Transferring tokens as an owner.transfer(address,uint256)transfer(address,address,uint256,bool,bytes)
Transferring tokens as an operator.transferFrom(address,address,uint256)
Approving an operator to spend tokens on behalf of the owner.approve(address,uint256)authorizeOperator(address,uint256)
DescriptionERC721LSP8
Transferring tokens as an owner. - transferFrom(address,address,uint256)
- safeTransferFrom(address,address,uint256)
- safeTransferFrom(address,address,uint256,bytes) -
transfer(address,address,bytes32,bool,bytes)
Transferring tokens as an operator.
Approving an operator to spend tokens on behalf of the owner.approve(address,uint256)authorizeOperator(address,bytes32)
- -In ERC20 the function `transfer(address,uint256)` is used to transfer ERC20 tokens from the caller, this can only be used by the holder of the ERC20 tokens. There is also `transferFrom(address,address,uint256)` which can also be used by the ERC20 tokens operator. - -In comparison ERC721 has: - -- `safeTransferFrom(address,address,uint256,bytes)` -- `safeTransferFrom(address,address,uint256)` -- `transferFrom(address,address,uint256)` - -All of the above functions can be used by both the owner of the token id or by the operator of the token id in order to transfer the ERC721 token. To be mentioned, both functions `safeTransferFrom(...)` have a hook that calls the recipient contract. - -Looking at LSP7 & LSP8 we have unified `transfer(...)` & `transferBatch(...)` functions in both contracts. Those functions contain a hook which is executed conditionally and can be used in any of the above cases. - -## LSP4 Digital Asset Metadata - -The **LSP4DigitalAssetMetadata** is a contract that sets the **Token-Metadata** (name and symbol) for the **LSP7DigitalAsset** and **LSP8IdentifiableDigitalAsset** token contracts. - -Since it uses **[ERC725Y General Data Key/Value Store](https://eips.ethereum.org/EIPS/eip-725)** to set the metadata, any information can be added (_e.g: **list of creators, JSON files**, etc_). - -## LSP7 Digital Asset - -The **LSP7DigitalAsset** contract represents digital assets for fungible tokens where minting and transferring are specified with an amount of tokens. Their functions were inspired from **[ERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)** and **[ERC1155](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/ERC1155.sol)** with more upgraded features. - -An LSP7 can serves as: - -- a **Divisible Token Contract** when `isNonDivisible` bool is set to `false` in the [`constructor(...)`](#constructor) -- otherwise serves as a **Non-Divisible Token Contract**. - -This can be useful to set `isNonDivisible` to `true`, rather than deploying a LSP8 contract to achieve the same goal. - -### Create a Fungible Token - -```solidity -// MyToken.sol -// SPDX-License-Identifier: MIT -pragma solidity ^0.8.0; - -import "@lukso/lsp-smart-contracts/contracts/LSP7DigitalAsset/LSP7DigitalAsset.sol"; - -contract MyToken is LSP7DigitalAsset { - // 4th argument (false) marks that this contract serves as a Fungible Token and not as a NFT. - constructor() LSP7DigitalAsset("MyToken","MTKN",msg.sender,false) { - // .. - } - - function mint() public { - _mint(...); - } -} -``` - -## Extensions - -The smart contracts packages for `@lukso/lsp7-contracts` and `@lukso/lsp8-contracts` include token extensions (similarly to OpenZeppelin contracts) that enables to include functionalities for building your token through inheritance. - -**LSP7 Tokens extensions:** - -- [`LSP7Burnable.sol`](../contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md): exposes a public `burn(...)` function that allows any token holder or operator to burn any amount of tokens. -- [`LSP7CappedSupply.sol`](../contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md): enable to specify a maximum supply on deployment / initialization, which cap the maximum amount of tokens that can be minted. - -**LSP8 NFTs extensions:** - -- [`LSP8Burnable.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md)`: exposes a public `burn(...)` function that allows any NFT holder or operator to burn a specific NFT tokenId. -- [`LSP8CappedSupply.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md)`: enable to specify a maximum supply on deployment / initialization, which cap the maximum amount of NFT that can be minted in the collection. -- [`LSP8Enumerable.sol](../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md)`: functionality to enumerate the list of NFTs in a collection. - -If your token contract uses the proxy pattern with initialize functions, use the `InitAbstract` version of these extension contracts (\_e.g: `LSP7Burnable` -> `LSP7BurnableInitAbstract`). - -## Custom logic for transfers - -The LSP7 and LSP8 implementations provide the `_beforeTokenTransfer` and `_afterTokenTransfer` function that offer the ability to specify custom logic that can run before or after the token transfer has happen (= before or after the balances in the contract state have been updated). - -## Note on LSP7 and LSP8 implementations - -`LSP7DigitalAsset.sol` and `LSP8IdentifiableDigitalAsset.sol` are `abstract` contracts that are not deployable as they are, because they do not contain any public functions by default to manage token supply (_e.g: no public `mint(...)` or `burn(...)` functions_). You can either: - -- use `LSP7Mintable` or `LSP8Mintable`, a preset contract that contains a public `mint(...)` function callable only by the contract's owner. -- or extend the `LSP7DigitalAsset` / `LSP8IdentifiableDigitalAsset` contract and create your own supply mechanism by defining public methods that use the internal `_mint(...)` and `_burn(...)` functions. diff --git a/docs/contracts/overview/NFT/_category_.yml b/docs/contracts/overview/NFT/_category_.yml new file mode 100644 index 000000000..57092f5c8 --- /dev/null +++ b/docs/contracts/overview/NFT/_category_.yml @@ -0,0 +1,4 @@ +label: 🖼️ Identifiable Digital Asset (NFT) +position: 5 +collapsible: true +collapsed: true diff --git a/docs/contracts/overview/NFT/create-nft-collection.md b/docs/contracts/overview/NFT/create-nft-collection.md new file mode 100644 index 000000000..bb6285352 --- /dev/null +++ b/docs/contracts/overview/NFT/create-nft-collection.md @@ -0,0 +1,52 @@ +--- +title: Create a Non Fungible Token +sidebar_position: 1 +--- + +# Create a Non Fungible Token + +```solidity +// SPDX-License-Identifier: MIT +pragma solidity ^0.8.4; + +// modules +import { + LSP8IdentifiableDigitalAsset +} from "@lukso/lsp8-contracts/contracts/LSP8IdentifiableDigitalAsset.sol"; + +// constants +import { + _LSP8_TOKENID_FORMAT_NUMBER +} from "@lukso/lsp8-contracts/contracts/LSP8Constants.sol"; +import { + _LSP4_TOKEN_TYPE_COLLECTION +} from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; + +contract BasicNFTCollection is LSP8IdentifiableDigitalAsset { + constructor( + string memory nftCollectionName, + string memory nftCollectionSymbol, + address contractOwner + ) + LSP8IdentifiableDigitalAsset( + nftCollectionName, + nftCollectionSymbol, + contractOwner, + _LSP4_TOKEN_TYPE_COLLECTION, + _LSP8_TOKENID_FORMAT_NUMBER + ) + { + // contract logic goes here... + } +} +``` + +## LSP8 NFT extensions + +The `@lukso/lsp8-contracts` package includes token extensions (similarly to OpenZeppelin contracts) that can be added through inheritance. This enables to include specific functionalities for building your token. + +| Extension contract | Description | +| :---------------------------------------------------------------------------------------------------- | :-------------------------------------------------------------------------------------------------------------------------------------------- | +| [`LSP8Burnable.sol`](../../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Burnable.md) | exposes a public `burn(...)` function that allows any NFT holder or operator to burn a specific NFT tokenId. | +| [`LSP8CappedSupply.sol`](../../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8CappedSupply.md) | enable to specify a maximum supply on deployment / initialization, which caps the maximum amount of NFT that can be minted in the collection. | +| [`LSP8Enumerable.sol`](../../contracts/LSP8IdentifiableDigitalAsset/extensions/LSP8Enumerable.md) | functionality to enumerate the list of NFTs in a collection. | diff --git a/docs/contracts/overview/NFT/customise-transfer-behaviour.md b/docs/contracts/overview/NFT/customise-transfer-behaviour.md new file mode 100644 index 000000000..5ba93b91e --- /dev/null +++ b/docs/contracts/overview/NFT/customise-transfer-behaviour.md @@ -0,0 +1,8 @@ +--- +title: Customize transfer behaviour +sidebar_position: 2 +--- + +# Customize transfer behaviour + +The `LSP8IdenfitiableDigitalAsset` contract implementation includes the `_beforeTokenTransfer` and `_afterTokenTransfer` functions that offer the ability to specify custom logic that can run before or after the token transfer has happen (= before or after the balances in the contract state have been updated). diff --git a/docs/contracts/overview/NFT/index.md b/docs/contracts/overview/NFT/index.md new file mode 100644 index 000000000..1c4628172 --- /dev/null +++ b/docs/contracts/overview/NFT/index.md @@ -0,0 +1,91 @@ +--- +title: 🖼️ Identifiable Digital Asset (NFT) +sidebar_position: 5 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# LSP8 Identifiable Digital Asset + +:::danger Deprecation of `LSP8CompatibleERC721` + +The `LSP8CompatibleERC721` contracts have been deprecated and deleted from the [`@lukso/lsp-smart-contracts`](https://github.com/lukso-network/lsp-smart-contracts) package since version `0.15.0`, because of their unsafe nature and [security considerations (See PR #845 for more details)](https://github.com/lukso-network/lsp-smart-contracts/pull/845#issuecomment-1888671461). + +They are not recommended to be used. However, if you want to still use them, they remain available in the version [`0.14.0`](https://github.com/lukso-network/lsp-smart-contracts/releases/tag/lsp-smart-contracts-v0.14.0). + +::: + +The **LSP8 Identifiable Digital Asset** contract is the newest advanced version of the existing ERC NFT standards, such as ERC721. LSP8 identifiable digital assets represent **N**on **F**ungible **T**okens (NFTs) that can be uniquely traded. Each NFT is identified with a tokenId, based on **[ERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)** and can also have its own metadata set using the **[`setDataForTokenId(...)`](../../contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#setdatafortokenid)** function. + +A **bytes32** value is used for tokenId to allow many uses of token identification, including numbers, contract addresses, and hashed values (i.e., serial numbers). + +## Installation & Usage + +The LSP8 smart contracts and their ABIs are available are available in their own individual package. To use them, install `@lukso/lsp8-contracts` as a dependency in your project. + + + + +``` +npm install @lukso/lsp8-contracts +``` + + + + +``` +yarn add @lukso/lsp8-contracts +``` + + + + +``` +pnpm add @lukso/lsp8-contracts +``` + + + + +`LSP8IdentifiableDigitalAsset.sol` is an `abstract` contract that is not deployable as is, because it does not contain any public functions by default to manage token supply (_e.g: no public `mint(...)` or `burn(...)` functions_). You can either: + +- use the `LSP8Mintable` preset contract that contains a public `mint(...)` function callable only by the contract's owner. +- or extend the `LSP8IdentifiableDigitalAsset` contract (_see below_) and create your own supply mechanism by defining public methods that use the internal `_mint(...)` and `_burn(...)` functions. + +## Comparisons with ERC721 + + + + + + + + + + + + + + + + + + + + +
DescriptionERC721LSP8
Transferring tokens as an owner. + transferFrom(address,address,uint256)
+ safeTransferFrom(address,address,uint256)
+ safeTransferFrom(address,address,uint256,bytes) +
transfer(address,address,bytes32,bool,bytes)
Transferring tokens as an operator.
Approving an operator to spend tokens on behalf of the owner.approve(address,uint256)authorizeOperator(address,bytes32)
+ +In comparison ERC721 has: + +- `safeTransferFrom(address,address,uint256,bytes)` +- `safeTransferFrom(address,address,uint256)` +- `transferFrom(address,address,uint256)` + +All of the above functions can be used by both the owner of the token id or by the operator of the token id in order to transfer the ERC721 token. To be mentioned, both functions `safeTransferFrom(...)` have a hook that calls the recipient contract. + +Looking at LSP7 & LSP8 we have unified `transfer(...)` & `transferBatch(...)` functions in both contracts. Those functions contain a hook which is executed conditionally and can be used in any of the above cases. diff --git a/docs/contracts/overview/IdentifiableDigitalAssets.md b/docs/contracts/overview/NFT/set-nft-metadata.md similarity index 77% rename from docs/contracts/overview/IdentifiableDigitalAssets.md rename to docs/contracts/overview/NFT/set-nft-metadata.md index de6d4a2d6..fee3c11f6 100644 --- a/docs/contracts/overview/IdentifiableDigitalAssets.md +++ b/docs/contracts/overview/NFT/set-nft-metadata.md @@ -1,27 +1,20 @@ --- -title: 🖼️ Identifiable Digital Asset (NFT) -sidebar_position: 5 +title: Set NFT Metadata +sidebar_position: 3 --- import Tabs from '@theme/Tabs'; import TabItem from '@theme/TabItem'; -## LSP8 Identifiable Digital Asset +# Set NFT Metadata -The **LSP8IdentifiableDigitalAsset** contract represents identifiable digital assets (NFTs) that can be uniquely traded and given metadata using the **[ERC725Y Standard](https://github.com/ethereum/EIPs/blob/master/EIPS/eip-725.md#erc725y)**. -Each NFT is identified with a tokenId, based on **[ERC721](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC721/ERC721.sol)**. +The function [`setDataBatchForTokenIds(...)`](../../contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#setdatabatchfortokenids) can be used to set multiple [data key-value](/standards/erc725.md#erc725y-generic-data-keyvalue-store) pairs at once for one or multiple tokenIds. -A **bytes32** value is used for tokenId to allow many uses of token identification, including numbers, contract addresses, and hashed values (i.e., serial numbers). +## Examples -### Setting metadata for one or multiple tokenIds +### Set multiple metadata at once on the same tokenId -The function [`setDataBatchForTokenIds(...)`](../../contracts/contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#setdatabatchfortokenids) can be used to set multiple data key-value pairs at once for one or multiple tokenIds. - -This function is flexible enough to enable to set one or multiple [data key-value](/standards/erc725).md#erc725y-generic-data-keyvalue-store) pairs for: - -#### case 1: a single tokenId - -To set for instance 3 x data key-value pairs for the same `tokenId`, the parameters of `setDataBatchForTokenIds(bytes32[],bytes32[],bytes[])` will be as follow: +To set for instance 3 x data key-value pairs for the same `tokenId`, the parameters of `setDataBatchForTokenIds(bytes32[],bytes32[],bytes[])` can be used as follow: @@ -123,9 +116,9 @@ async function setMultipleDataForSingleTokenId( -#### Case 2: different tokenIds +### Set metadata on different tokenIds -To set for instance the same data key-value pair (_e.g: `LSP4Metadata`_) for 3 x different `tokenId`s, the parameters of `setDataBatchForTokenIds(bytes32[],bytes32[],bytes[])` will be as follow: +To set for instance the same data key-value pair (_e.g: `LSP4Metadata`_) for 3 x different `tokenId`s, the parameters of `setDataBatchForTokenIds(bytes32[],bytes32[],bytes[])` can be used as follow: @@ -227,10 +220,10 @@ async function setMultipleDataForSingleTokenId( -### Checking if the Metadata of a tokenId changed +## Check if the metadata of a specific NFT (tokenId) has changed Since LSP8 uses [ERC725Y](/standards/erc725#erc725y-generic-data-keyvalue-store) under the hood, the URI pointing to the metadata of a specific tokenId can be changed inside the ERC725Y storage of the LSP8 contract. We have seen in the previous section [**how to set metadata for one or multiple tokenIds**](#setting-metadata-for-one-or-multiple-tokenids). -The two functions `setDataForTokenId(...)` and `setDataBatchForTokenIds(...)` emit a [`TokenIdDataChanged`](../contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#tokeniddatachanged) event. You can listen for this event in the LSP8 contract from your dApp, filtering for the `LSP4Metadata` data key to check if the metadata of a tokenId has been changed. You can do so by filtering the first parameter with the `tokenId` and the second parameter with the [bytes32 value of the `LSP4Metadata` data key](../../standards/tokens/LSP4-Digital-Asset-Metadata.md#lsp4metadata). +The two functions `setDataForTokenId(...)` and `setDataBatchForTokenIds(...)` emit a [`TokenIdDataChanged`](../../contracts/LSP8IdentifiableDigitalAsset/LSP8IdentifiableDigitalAsset.md#tokeniddatachanged) event. You can listen for this event in the LSP8 contract from your dApp, filtering for the `LSP4Metadata` data key to check if the metadata of a tokenId has been changed. You can do so by filtering the first parameter with the `tokenId` and the second parameter with the [bytes32 value of the `LSP4Metadata` data key](../../../standards/tokens/LSP4-Digital-Asset-Metadata.md#lsp4metadata). diff --git a/docs/contracts/overview/Token/_category_.yml b/docs/contracts/overview/Token/_category_.yml new file mode 100644 index 000000000..67672d02e --- /dev/null +++ b/docs/contracts/overview/Token/_category_.yml @@ -0,0 +1,4 @@ +label: 🪙 Digital Asset (Token) +position: 4 +collapsible: true +collapsed: true diff --git a/docs/contracts/overview/Token/create-token.md b/docs/contracts/overview/Token/create-token.md new file mode 100644 index 000000000..1e47c3672 --- /dev/null +++ b/docs/contracts/overview/Token/create-token.md @@ -0,0 +1,178 @@ +--- +title: Create a Fungible Token +sidebar_position: 1 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# Create a Fungible Token + + + + +```solidity title="MyToken.sol" {8-14} showLineNumbers +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {_LSP4_TOKEN_TYPE_TOKEN} from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; +import {LSP7DigitalAsset} from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + +contract MyToken is + LSP7DigitalAsset( + "MyToken", // token name + "MTKN", // token symbol + msg.sender, // contract owner + _LSP4_TOKEN_TYPE_TOKEN, // token type as uint256 (0 for Token, 1 for NFT, 2 for Collection) + false // make the token non divisible (true = 0 decimals, false = 18 decimals) + ) +{ + // Custom logic for your token... +} +``` + + + + +```solidity title="MyToken.sol" {18-24} showLineNumbers +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import {_LSP4_TOKEN_TYPE_TOKEN} from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; +import {LSP7DigitalAsset} from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + +contract MyToken is LSP7DigitalAsset { + /// @dev Available options for 4th parameter token type (uint256) + /// - 0 for Token + /// - 1 for NFT + /// - 2 for Collection + constructor( + string memory name_, + string memory symbol_, + address contractOwner_, + bool isNonDivisible_ + ) + LSP7DigitalAsset( + name_, + symbol_, + contractOwner_, + _LSP4_TOKEN_TYPE_TOKEN, + isNonDivisible_ + ) + { + // constructor logic ... + } + + // Custom logic for your token... +} + +``` + + + + +## LSP7 Tokens extensions + +The `@lukso/lsp7-contracts` package includes token extensions (similarly to OpenZeppelin contracts) that can be added through inheritance. This enables to include specific functionalities for building your token. + +| Extension contract | Description | +| :---------------------------------------------------------------------------------------- | :----------------------------------------------------------------------------------------------------------------------------- | +| [`LSP7Burnable.sol`](../../contracts/LSP7DigitalAsset/extensions/LSP7Burnable.md) | Exposes a public `burn(...)` function that allows any token holder or operator to burn any amount of tokens. | +| [`LSP7CappedSupply.sol`](../../contracts/LSP7DigitalAsset/extensions/LSP7CappedSupply.md) | Enable to specify a maximum supply on deployment / initialization, which caps the maximum amount of tokens that can be minted. | + +If your token contract uses the proxy pattern with initialize functions, use the `InitAbstract` version of these extension contracts (_e.g: `LSP7Burnable` -> `LSP7BurnableInitAbstract`_). + + + + +```solidity title="MyToken.sol" {19-20} showLineNumbers +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import { _LSP4_TOKEN_TYPE_TOKEN } from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; +import { LSP7DigitalAsset } from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + +// extensions +import { LSP7Burnable } from "@lukso/lsp7-contracts/contracts/extensions/LSP7Burnable.sol"; +import { LSP7CappedSupply } from "@lukso/lsp7-contracts/contracts/extensions/LSP7CappedSupply.sol"; + +contract MyToken is + LSP7DigitalAsset( + "MyToken", // token name + "MTKN", // token symbol + msg.sender, // contract owner + _LSP4_TOKEN_TYPE_TOKEN, // token type as uint256 (0 for Token, 1 for NFT, 2 for Collection) + false // make the token non divisible (true = 0 decimals, false = 18 decimals) + ), + LSP7Burnable, + LSP7CappedSupply(42_000_000 * 10 ** super.decimals()) +{ + function _mint( + address to, + uint256 amount, + bool force, + bytes memory data + ) + internal + virtual + override(LSP7CappedSupply, LSP7DigitalAsset) + { + LSP7CappedSupply._mint(to, amount, force, data); + } + + // Custom logic for your token... +} +``` + + + + +```solidity title="MyToken.sol" {23-25} showLineNumbers +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +import { _LSP4_TOKEN_TYPE_TOKEN } from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; +import { LSP7DigitalAsset } from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + +// extensions +import { LSP7Burnable } from "@lukso/lsp7-contracts/contracts/extensions/LSP7Burnable.sol"; +import { LSP7CappedSupply } from "@lukso/lsp7-contracts/contracts/extensions/LSP7CappedSupply.sol"; + +contract MyToken is LSP7DigitalAsset, LSP7Burnable, LSP7CappedSupply { + /// @dev Available options for 4th parameter token type (uint256) + /// - 0 for Token + /// - 1 for NFT + /// - 2 for Collection + constructor( + string memory name_, + string memory symbol_, + address contractOwner_, + bool isNonDivisible_, + uint256 maxSupply_ + ) + LSP7DigitalAsset(name_, symbol_, contractOwner_, _LSP4_TOKEN_TYPE_TOKEN, isNonDivisible_) + LSP7Burnable() + LSP7CappedSupply(maxSupply_) + { + // constructor logic ... + } + + function _mint( + address to, + uint256 amount, + bool force, + bytes memory data + ) + internal + virtual + override(LSP7CappedSupply, LSP7DigitalAsset) + { + LSP7CappedSupply._mint(to, amount, force, data); + } + + // Custom logic for your token... +} +``` + + + diff --git a/docs/contracts/overview/Token/customise-transfer-behaviour.md b/docs/contracts/overview/Token/customise-transfer-behaviour.md new file mode 100644 index 000000000..ad42a760c --- /dev/null +++ b/docs/contracts/overview/Token/customise-transfer-behaviour.md @@ -0,0 +1,97 @@ +--- +title: Customize transfer behaviour +sidebar_position: 2 +--- + +# Customize transfer behaviour + +## `_beforeTokenTransfer` and `_afterTokenTransfer` hooks + +`The LSP7DigitalAsset contract` implementation includes two hooks to add custom behaviour to run logic before or after the total supply of tokens has been updated in the contract's storage. This can be done via the [`_beforeTokenTransfer(...)`](../../contracts/LSP7DigitalAsset/LSP7DigitalAsset.md#_beforetokentransfer) and [`_afterTokenTransfer(...)`](../../contracts/LSP7DigitalAsset/LSP7DigitalAsset.md#_aftertokentransfer) functions. + +### Solidity example + +Below is a simple pseudo-code example in Solidity where the `_afterTokenTransfer(...)` internal hook **registers the number of token transactions sent and received by any address**. Since a LSP7 token uses ERC725Y as storage under the hood, it provides a flexible way to add metadata. + +This example leverages this feature and provides a way to get a simple count of token transactions performed by each user in terms of tokens sent and received. It uses the following functions and libraries: + +- `_afterTokenTransfer(...)` hook to register the token transfer after it occurred. +- The [ERC725Y](../../../standards/erc725.md#erc725y-data-representation) storage of the token contract where the transfer count will be stored (reading via [`_getData(...)`](../../contracts/LSP7DigitalAsset/LSP7DigitalAsset.md#_getdata), updating it via [`_setData(...)`](../../contracts/LSP7DigitalAsset/LSP7DigitalAsset.md#_setdata)). +- We defined a specific data key where to store this info and used the [`LSP2Utils`](../../contracts/../libraries/LSP2Utils.md#generatemappingkey-2) library to encode this as a [`Mapping`](../../../standards/metadata/lsp2-json-schema.md#mapping) data key easily. + +```solidity +// SPDX-License-Identifier: UNLICENSED +pragma solidity ^0.8.20; + +// tokens +import { _LSP4_TOKEN_TYPE_TOKEN } from "@lukso/lsp4-contracts/contracts/LSP4Constants.sol"; +import { LSP7DigitalAsset } from "@lukso/lsp7-contracts/contracts/LSP7DigitalAsset.sol"; + +// libraries +import { LSP2Utils } from "@lukso/lsp2-contracts/contracts/LSP2Utils.sol"; + +contract MyToken is LSP7DigitalAsset { + /// @dev Available options for 4th parameter token type (uint256) + /// - 0 for Token + /// - 1 for NFT + /// - 2 for Collection + constructor( + string memory name_, + string memory symbol_, + address contractOwner_, + bool isNonDivisible_ + ) + LSP7DigitalAsset(name_, symbol_, contractOwner_, _LSP4_TOKEN_TYPE_TOKEN, isNonDivisible_) + { + // constructor logic ... + } + + function _afterTokenTransfer( + address from, + address to, + uint256, /* amount */ + bytes memory /* data */ + ) + internal + virtual + override + { + // TokensSentTx:
+ bytes32 tokensSentTxDataKey = + LSP2Utils.generateMappingKey({ keyPrefix: bytes10(keccak256("TokensSentTx")), bytes20Value: bytes20(from) }); + + // TokensReceivedTx:
+ bytes32 tokensReceivedTxDataKey = LSP2Utils.generateMappingKey({ + keyPrefix: bytes10(keccak256("TokensReceivedTx")), + bytes20Value: bytes20(to) + }); + + bytes memory tokensSentTxValue = _getData(tokensSentTxDataKey); + bytes memory tokensReceivedTxValue = _getData(tokensReceivedTxDataKey); + + // sanity check to ensure we can abi-decode correctly + require(tokensSentTxValue.length == 32, "Invalid uint256 encoded value under `TokensSentTx:
` data key"); + require( + tokensReceivedTxValue.length == 32, + "Invalid uint256 encoded value under `TokensReceivedTx:
data key" + ); + + uint256 tokensSentTxCount = abi.decode(tokensSentTxValue, (uint256)); + uint256 tokensReceivedTxCount = abi.decode(tokensReceivedTxValue, (uint256)); + + // increment the counter + set data in the storage + tokensSentTxCount++; + tokensReceivedTxCount++; + + _setData(tokensSentTxDataKey, abi.encode(tokensSentTxCount)); + _setData(tokensReceivedTxDataKey, abi.encode(tokensReceivedTxCount)); + } +} + +``` + +This example is minimalist and only stores a counter as a number. Still, any info related to the token transfer could be stored during the transfer (_e.g: the amount, the data passed, the gas price, the balance before and after, etc..._). This way, the storage of the token contract can act, for instance, as: + +- A _"mini explorer"_ for the token contract (without relying on a block explorer and reviewing the complete list of transactions). +- To query the transactions for a user and provide an analytical view of their balance changes, demonstrating its data analysis capabilities. +- Showcase which user, smart contract address or protocol are the most active users and traders for this token (being the ones with the higher count under the `TokensSentTx:
` data key). diff --git a/docs/contracts/overview/Token/index.md b/docs/contracts/overview/Token/index.md new file mode 100644 index 000000000..938589115 --- /dev/null +++ b/docs/contracts/overview/Token/index.md @@ -0,0 +1,103 @@ +--- +title: 🪙 Digital Asset (Token) +sidebar_position: 4 +--- + +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# LSP7 Digital Asset + +:::danger Deprecation of `LSP7CompatibleERC20` + +The `LSP7CompatibleERC20` contracts have been deprecated and deleted from the [`@lukso/lsp-smart-contracts`](https://github.com/lukso-network/lsp-smart-contracts) package since version `0.15.0`, because of their [unsafe nature and security considerations](https://github.com/lukso-network/lsp-smart-contracts/pull/845#issuecomment-1888671461). They are not recommended to be used. However, if you want to still use them, they remain available up to the version [`0.14.0`](https://github.com/lukso-network/lsp-smart-contracts/releases/tag/lsp-smart-contracts-v0.14.0). + +::: + +The **LSP7 Digital Asset** contract is the newest advanced version of the existing ERC token standards, such as ERC20 and ERC1155. It comes with many features that enhance the security and the overall user experience and compatibility with [ERC725Accounts](/standards/accounts/lsp0-erc725account.md) and [Universal Receivers](/standards/accounts/lsp1-universal-receiver.md). + +The **LSP7DigitalAsset** contract represents digital assets for fungible tokens where minting and transferring are specified with an amount of tokens. Their functions were inspired from **[ERC20](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC20/ERC20.sol)** and **[ERC1155](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/master/contracts/token/ERC1155/ERC1155.sol)** with more upgraded features. + +## Installation & Usage + +The LSP7 smart contracts and their ABIs are available are available in their own individual package. To use them, install `@lukso/lsp7-contracts` as a dependency in your project. + + + + +``` +npm install @lukso/lsp7-contracts +``` + + + + +``` +yarn add @lukso/lsp7-contracts +``` + + + + +``` +pnpm add @lukso/lsp7-contracts +``` + + + + +`LSP7DigitalAsset.sol` is an `abstract` contract that is not deployable as is, because it does not contain any public functions by default to manage token supply (_e.g: no public `mint(...)` or `burn(...)` functions_). You can either: + +- the `LSP7Mintable` preset contract that contains a public `mint(...)` function callable only by the contract's owner. +- or extend the `LSP7DigitalAsset` contract (_see below_) and create your own supply mechanism by defining public methods that use the internal `_mint(...)` and `_burn(...)` functions. + +## Token Metadata + +LSP7 uses the **LSP4DigitalAssetMetadata** standard under the hood. Since LSP4 uses an **[ERC725Y General Data Key/Value Store](https://eips.ethereum.org/EIPS/eip-725)**, it allows any form of metadata to be defined and set. This could include things such as **list of creators, JSON files, exchanges where the token is listed, etc...** + +It is within this contract that the **Token-Metadata** (name and symbol) is set for the **LSP7DigitalAsset** on deployment / initialization. + +## Example use cases + +An LSP7 can serves as: + +- a **Divisible Token Contract** when `isNonDivisible` bool is set to `false` in the [`constructor(...)`](#constructor) +- otherwise serves as a **Non-Divisible Token Contract**. + +This can be useful to set `isNonDivisible` to `true`, rather than deploying a LSP8 contract to achieve the same goal. + +## Comparisons with ERC20 + + + + + + + + + + + + + + + + + + + + + +
DescriptionERC20LSP7
Transferring tokens as an owner.transfer(address,uint256)transfer(address,address,uint256,bool,bytes)
Transferring tokens as an operator.transferFrom(address,address,uint256)
Approving an operator to spend tokens on behalf of the owner.approve(address,uint256)authorizeOperator(address,uint256)
+ +In ERC20 the following functions can be used to transfer tokens from the token holder: + +- `transfer(address,uint256)` can only be used by the token holder. Therefore, the caller must be token holder. +- `transferFrom(address,address,uint256)` can be used by operator to transfer tokens on behalf of a token holder (as long as this token holder has been approved and given an allowance). + +In comparison in LSP7, a single function `transfer(address,address,uint256,bool,bytes)` can be used by both operator and token owner. + +- if a token holder want to transfer its own token, it can call directly the function (be the caller) and specify its address for the first parameter `from`. +- if an operator want to transfer tokens for a token holder that it has been approved for (via the `authorizeOperator(...)` function), it can call the function and specify the address of the token holder as the `from` address as well. + +Therefore as you can see from the table above, the only thing that changes when transferring token as a token owner or an operator is **the caller** of the function. **The parameters remain the same**. diff --git a/docs/learn/migrate/migrate-erc20-to-lsp7.md b/docs/learn/migrate/migrate-erc20-to-lsp7.md index fccf82d91..726e90f1e 100644 --- a/docs/learn/migrate/migrate-erc20-to-lsp7.md +++ b/docs/learn/migrate/migrate-erc20-to-lsp7.md @@ -21,7 +21,7 @@ import Erc20LSP7Table from '@site/src/components/Erc20LSP7Table'; :::info Resources -See the [contract overview](../../contracts/overview/DigitalAssets#comparisons-with-erc20--erc721) page for the interface differences between ERC20 and LSP7. +See the [contract overview](../../contracts/overview/Token/index.md#comparisons-with-erc20--erc721) page for the interface differences between ERC20 and LSP7. ::: diff --git a/docs/learn/migrate/migrate-erc721-to-lsp8.md b/docs/learn/migrate/migrate-erc721-to-lsp8.md index f3a68da0d..a61e608ef 100644 --- a/docs/learn/migrate/migrate-erc721-to-lsp8.md +++ b/docs/learn/migrate/migrate-erc721-to-lsp8.md @@ -21,7 +21,7 @@ import Erc721LSP8Table from '@site/src/components/Erc721LSP8Table'; :::info Resources -See the [contract overview](../../contracts/overview/DigitalAssets#comparisons-with-erc20--erc721) page for the interface differences between ERC721 and LSP8. +See the [contract overview](../../contracts/overview/NFT/index.md#comparisons-with-erc20--erc721) page for the interface differences between ERC721 and LSP8. ::: diff --git a/docs/learn/migrate/migrate-to-lukso.md b/docs/learn/migrate/migrate-to-lukso.md index 4daed41b4..0d51a8623 100644 --- a/docs/learn/migrate/migrate-to-lukso.md +++ b/docs/learn/migrate/migrate-to-lukso.md @@ -62,7 +62,7 @@ Some real-life examples for a user's Universal Profile could be: :::info -For more details on the different functions and features between ERC20 and LSP7 or ERC721 and LSP8, check the [**Contracts > Digital Assets**](../../contracts/overview/DigitalAssets.md) section. +For more details on the different functions and features between ERC20 and LSP7 or ERC721 and LSP8, check the [**Contracts > Token**](../../contracts/overview/Token/index.md)or [**Contracts > NFT**](../../contracts/overview/NFT/index.md) sections. ::: diff --git a/docs/standards/tokens/introduction.md b/docs/standards/tokens/introduction.md index a55f04c34..51dac1990 100644 --- a/docs/standards/tokens/introduction.md +++ b/docs/standards/tokens/introduction.md @@ -34,6 +34,12 @@ As for features, these standards are just representing **incremental tokenIds** ![Tokens & NFT Standards LSP4, LSP7 and LSP8](/img/standards/LUKSO-Tokens-NFT-Standards.jpeg) +The functions of the LSP7 and LSP8 standards are overall simpler, more straightforward and unified, with a **similar function names** for both LSP7 and LSP8.yarn + +Both functions in LSP7 and LSP8 have the same name (`transfer`) to transfer assets. This is easier compared to ERC20 and ERC721 that use different naming (`transfer` for ERC20 vs `transferFrom` in ERC721 to transfer tokens as the token owner). + +Looking at LSP7 & LSP8 we have unified `transfer(...)` & `transferBatch(...)` functions in both contracts. Those functions contain a hook which is executed conditionally and can be used in any of the above cases. + ## References - [NFT NYC - Building Blocks for the New Creative Economy (Fabian Vogelsteller, Youtube)](https://www.youtube.com/watch?v=skA4Y-vvt5s) diff --git a/src/components/ContractCardsGallery/index.tsx b/src/components/ContractCardsGallery/index.tsx index 3eac101b4..f65f9c45b 100644 --- a/src/components/ContractCardsGallery/index.tsx +++ b/src/components/ContractCardsGallery/index.tsx @@ -58,21 +58,21 @@ const Contracts_AccountsInteraction = [ const Contracts_DigitalAssets = [ { name: '🔍 LSP4 Digital Asset Metadata', - url: '/contracts/overview/DigitalAssets/#lsp4-digital-asset-metadata', - urlABI: '/contracts/contracts/LSP6KeyManager', + url: '', + urlABI: '/contracts/contracts/LSP4DigitalAssetMetadata', description: 'Enables to add metadata to any digital asset contract (token or NFT).', }, { name: '🪙 LSP7 Digital Asset', - url: '/contracts/overview/DigitalAssets/#lsp7-digital-asset', + url: '/contracts/overview/Token', urlABI: '/contracts/contracts/LSP7DigitalAsset', description: 'Creates your own fungible token with the possibility to make it divisible or not.', }, { name: '🎨 LSP8 Identifiable Digital Asset', - url: '/contracts/overview/DigitalAssets/#lsp8-identifiable-digital-asset', + url: '/contracts/overview/NFT', urlABI: '/contracts/contracts/LSP8IdentifiableDigitalAsset', description: 'Creates a non-fungible token where each NFT can be represented differently (numbers, serial numbers, an ERC725Y contract per NFT...).',