Skip to content
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

improve contracts section for Tokens & NFTs (LSP7 + LSP8) #1156

Merged
merged 8 commits into from
Oct 21, 2024
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
149 changes: 0 additions & 149 deletions docs/contracts/overview/DigitalAssets.md

This file was deleted.

4 changes: 4 additions & 0 deletions docs/contracts/overview/NFT/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
label: 🖼️ Identifiable Digital Asset (NFT)
position: 5
collapsible: true
collapsed: true
52 changes: 52 additions & 0 deletions docs/contracts/overview/NFT/create-nft-collection.md
Original file line number Diff line number Diff line change
@@ -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. |
8 changes: 8 additions & 0 deletions docs/contracts/overview/NFT/customise-transfer-behaviour.md
Original file line number Diff line number Diff line change
@@ -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).
Copy link
Member

Choose a reason for hiding this comment

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

Should we give code examples for those functions?

Copy link
Member Author

Choose a reason for hiding this comment

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

I will but haven't found a good example so far to showcase like in LSP7. I will do in a separate PR

91 changes: 91 additions & 0 deletions docs/contracts/overview/NFT/index.md
Original file line number Diff line number Diff line change
@@ -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.

<Tabs groupId="provider-lib">
<TabItem value="npm" label="npm" default>

```
npm install @lukso/lsp8-contracts
```

</TabItem>
<TabItem value="yarn" label="yarn" default>

```
yarn add @lukso/lsp8-contracts
```

</TabItem>
<TabItem value="pnpm" label="pnpm" default>

```
pnpm add @lukso/lsp8-contracts
```

</TabItem>
</Tabs>

`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

<table>
<tr>
<th>Description</th>
<th>ERC721</th>
<th>LSP8</th>
</tr>
<tr>
<td>Transferring tokens as an owner.</td>
<td rowspan="2">
<code>transferFrom(address,address,uint256)</code><br/>
<code>safeTransferFrom(address,address,uint256)</code><br/>
<code>safeTransferFrom(address,address,uint256,bytes)</code>
</td>
<td rowspan="2"><code>transfer(address,address,bytes32,bool,bytes)</code></td>
</tr>
<tr>
<td>Transferring tokens as an operator.</td>
</tr>
<tr>
<td>Approving an operator to spend tokens on behalf of the owner.</td>
<td><code>approve(address,uint256)</code></td>
<td><code>authorizeOperator(address,bytes32)</code></td>
</tr>
</table>

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.
Original file line number Diff line number Diff line change
@@ -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:

<Tabs>

Expand Down Expand Up @@ -123,9 +116,9 @@ async function setMultipleDataForSingleTokenId(

</Tabs>

#### 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:

<Tabs>

Expand Down Expand Up @@ -227,10 +220,10 @@ async function setMultipleDataForSingleTokenId(

</Tabs>

### 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).
4 changes: 4 additions & 0 deletions docs/contracts/overview/Token/_category_.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
label: 🪙 Digital Asset (Token)
position: 4
collapsible: true
collapsed: true
Loading