From 3db6551c13e1919fb58d525091eed8556723ef64 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Fri, 16 Aug 2024 15:51:44 +0400 Subject: [PATCH] sdk-2 methods (#170) * add property mutation docs * add more methods --- docs/build/sdk/v2/balances.md | 2 +- docs/build/sdk/v2/collections.md | 103 ++++++++++++------- docs/build/sdk/v2/quick-start.md | 22 ++--- docs/build/sdk/v2/tokens.md | 148 ++++++++++++++++++++++++---- docs/tutorials/mass-transactions.md | 16 +-- 5 files changed, 219 insertions(+), 72 deletions(-) diff --git a/docs/build/sdk/v2/balances.md b/docs/build/sdk/v2/balances.md index b12c0c3..d6b3268 100644 --- a/docs/build/sdk/v2/balances.md +++ b/docs/build/sdk/v2/balances.md @@ -1,4 +1,4 @@ -# Working with balances +# Balances ::: warning The Unique SDK v2 is in alpha and may contain bugs or incomplete features. For production use or to access more stable functionality, please refer to the [documentation for the previous version](../getting-started.md) of the SDK. diff --git a/docs/build/sdk/v2/collections.md b/docs/build/sdk/v2/collections.md index 795bf7c..d3cf6ed 100644 --- a/docs/build/sdk/v2/collections.md +++ b/docs/build/sdk/v2/collections.md @@ -1,4 +1,4 @@ -# Working with collections +# Collections ::: warning The Unique SDK v2 is in alpha and may contain bugs or incomplete features. For production use or to access more stable functionality, please refer to the [documentation for the previous version](../getting-started.md) of the SDK. @@ -8,7 +8,7 @@ The Unique SDK v2 is in alpha and may contain bugs or incomplete features. For p ## Intro -NFTs have become much easier to issue, and we’re seeing increasing amounts minted daily, mostly through NFT collections. This article will dive into NFT collection minting and some popular examples of how to work with NFT collections in Unique Network. +NFTs have become much easier to issue, and we're seeing increasing amounts minted daily, mainly through NFT collections. This article will dive into NFT collection minting and some popular examples of how to work with NFT collections in Unique Network. As the name implies, an NFT collection is a unique collection of NFTs. NFT artworks are generally created on a smaller scale with the involvement of concerned content creators or digital artists. In addition, you would also notice that individual NFT artworks are available for sale on different NFT marketplaces. @@ -18,7 +18,7 @@ In Unique Network, the collection has the following entities: - **description** - some additional details about the collection; - **token prefix** - short string value that will be added to token IDs. - **properties** - a unique set of keys and values which defines collection specifics; -- **limits** - a set that defines the rules for a collection, e.g. whether it can be transferred, or how many tokens you can mint in it; +- **limits** - a set that defines the rules for a collection, e.g., whether it can be transferred or how many tokens you can mint in it; - **owner** - an address that created a collection (or if the collection was transferred, the address that owns the collection at the moment); - **admins** - a collection can be controlled by multiple admin addresses. Admins can issue and burn NFTs, as well as add and remove other admins, but they cannot change NFT or collection ownership; - **allow list** - a list of addresses collected that allow certain community members a guaranteed spot for minting a new NFT. @@ -35,10 +35,10 @@ Collection coverage is a part of collection metadata and is not a mandatory fiel ```ts:no-line-numbers const collectionTx = await sdk.collection.create({ - name: "Test", - description: "Test collection", - symbol: "TST", - info: {cover_image: {url: 'https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m'}}, + name: "Test", + description: "Test collection", + symbol: "TST", + info: {cover_image: {url: 'https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m'}}, }); console.log("Collection ID:", collectionTx.result.collectionId); @@ -46,31 +46,31 @@ console.log("Collection ID:", collectionTx.result.collectionId); In Unique Network every collection has a unique collection ID. You will need it later to [mint NFTs](./tokens.md). -At this point, you can check your collection successfully created on [Unique Scan](https://uniquescan.io/opal/collections/). +You can check your collection successfully created on [Unique Scan](https://uniquescan.io/opal/collections/). -## Understanding collection modes +## Collection modes There are three different collection modes in Unique Network: - NFT (default) - Fungible - ReFungible -You can specify the collection mode during the minting. +You can specify the mode of collection during minting. ```ts:no-line-numbers await sdk.collection.create({ - name: "Test", - description: "Test collection", - symbol: "TST", - info: {cover_image: {url: coverImage}}, - mode: 'Fungible' // <--- set collection mode here + name: "Test", + description: "Test collection", + symbol: "TST", + info: {cover_image: {url: coverImage}}, + mode: 'Fungible' // <--- set collection mode here }); ``` -## Understanding collection properties +## Collection properties - -Every collection in Unique Network can have up to 64 properties - a unique set of keys and values that defines collection specifics. Some of them relate to NFT metadata and are set automatically by SDK. + +Every collection in Unique Network can have up to 64 properties—a unique set of keys and values that define collection specifics. Some of them relate to NFT metadata and are set automatically by SDK. ### Setting collection properties @@ -83,8 +83,8 @@ const {result} = await sdk.collection.create({ symbol: "TST", info: {cover_image: {url: "https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m"}}, properties: [ // <--- set collection properties here - {key: "A", value: "value A"}, - {key: "B", value: "value B"}, + {key: "A", value: "value A"}, + {key: "B", value: "value B"}, ] }); ``` @@ -100,7 +100,7 @@ await sdk.collection.setProperties({ }); ``` -### Now let's query our collection and check its properties +### Now, let's query our collection and check its properties ```ts:no-line-numbers const collection = await sdk.collection.get({idOrAddress: result.collectionId}); @@ -128,7 +128,7 @@ The result will be as follows, let's break it down. valueHex: "0x76616c75652043", }, { - key: "collectionInfo", + key: "collectionInfo", value: "{\"schemaName\":\"unique\",\"schemaVersion\":\"2.0.0\",\"cover_image\":{\"url\":\"https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m\"}}", valueHex: "0x7b22736368656d614e616d65223a22756e69717565222c22736368656d6156657273696f6e223a22322e302e30222c22636f7665725f696d616765223a7b2275726c223a2268747470733a2f2f676174657761792e70696e6174612e636c6f75642f697066732f516d546b6854673553357a72714a4c3355734b74796946693866634d543343616f39754b7461647033436b68376d227d7d", }, @@ -146,10 +146,10 @@ The result will be as follows, let's break it down. ``` - Properties `A`, `B`, and `C` has been manually set during the collection creation and modifying collection with `setCollectionLimits` -- Properties `schemaName`, `schemaVersion`, and `collectionInfo` are set by the SDK and relate to the Unique metadata. You can read more about [Unique Schema](../../../reference/schemas/2.0.0.md) in the reference section. +- The SDK sets' properties `schemaName`, `schemaVersion`, and `collectionInfo` and relate to the Unique metadata. You can read more about [Unique Schema](../../../reference/schemas/2.0.0.md) in the reference section. -## Understanding token property permissions +## Token property permissions Every NFT token inside the collection can have properties. The list of allowed properties and their mutability permissions are handled on the collection level. @@ -169,28 +169,47 @@ await sdk.collection.create({ }); ``` -Every NFT token in the collection could have two properties: +Every NFT token in the collection above could have three properties: -- `A`: this property is mutable, it could be set during the NFT minting. Later it could be rewritten by the collection admin or NFT owner -- `B`: this property is immutable, and can be set only once during the minting -- `C`: this property is immutable, and can be set only once during the minting or later by the token owner +- `A`: this property is mutable; it could be set during the NFT minting. Later, it could be rewritten by the collection admin or NFT owner +- `B`: this property is immutable and can be set only once during the minting +- `C`: this property is immutable and can be set only once during the minting or later by the token owner The SDK also specifies some additional token properties related to Unique Schema. Let's check them. ```ts:no-line-numbers -const colleciton = await sdk.collection.get({idOrAddress: collectionId}) +const collection = await sdk.collection.get({idOrAddress: collectionId}) -console.log(colleciton.tokenPropertyPermissions); +console.log(collection.tokenPropertyPermissions); ``` There are a lot of additional token properties, like `URI`, `customizing_overrides`, and so on. You can check more information about them in the [reference section](../../../reference/schemas/2.0.0.md). One of the most important token properties is `tokenData`, which will be a container for all token attributes. You will learn more about `attributes` in the [NFT section](./tokens.md). -## Understanding collection limits +## Nesting configuration + +In Unique Network, tokens can own other tokens through a mechanism known as nesting. Nesting permissions must be configured at the collection level to enable tokens within a collection to serve as parent tokens. Additionally, it is possible to limit which collections are allowed to participate in nesting and define the roles that can execute this action. Let's explore an example of how nesting can be set on a collection level. + +```ts:no-line-numbers +const { result } = await sdk.collection.create({ + name: "Name", + description: "Description", + symbol: "SYM", + permissions: { // <--- set nesting in the permissions section + nesting: { + collectionAdmin: true, + tokenOwner: true, + restricted: [1, 2], // <--- restrict nesting for specific collections + }, + }, +}); +``` + +## Collection limits -It is possible to set some limitations such as: -- maximum number of tokens in the collection, or +It is possible to set some limitations, such as: +- maximum number of tokens in the collection or - how many tokens a single account can hold - whether the token can be transferred or not - and many more @@ -204,7 +223,6 @@ await sdk.collection.create({ name: "Test", description: "Test collection", symbol: "TST", - info: {cover_image: {url: coverImage}}, limits: { // <--- set collection limits here accountTokenOwnershipLimit: 1, ownerCanDestroy: true, @@ -218,3 +236,20 @@ await sdk.collection.create({ }, }); ``` + +## Sponsoring + +In Unique Network, transactions with collections can be sponsored, allowing for a gasless experience where the sponsor covers the transaction fees. This enables seamless and cost-free transfers of NFTs and the execution of smart contracts, even for accounts without native tokens. + +Setting sponsorship of a collection should be done in several steps. + +### Setting collection sponsor + +First, we need to specify an account that will pay transaction fees. + +```ts:no-line-numbers +await sdk.collection.setSponsor({ collectionId, sponsor: alice.address }); +``` + + + \ No newline at end of file diff --git a/docs/build/sdk/v2/quick-start.md b/docs/build/sdk/v2/quick-start.md index 9a1de26..c7d2aa1 100644 --- a/docs/build/sdk/v2/quick-start.md +++ b/docs/build/sdk/v2/quick-start.md @@ -49,7 +49,7 @@ const account = Sr25519Account.fromUri(mnemonic); // set "account" as a default signer const sdk = UniqueChain({ - baseUrl: "https://rest.uniquenetwork.dev/v2/opal", + baseUrl: "https://rest.unique.network/v2/opal", account, }); ``` @@ -76,20 +76,20 @@ The Unique SDK currently supports the following modules: Instead of using public SDK endpoints, you can easily run your own HTTP proxy. Create a docker-compose.yml with the following content, and run `docker compose up`. ```yml:no-line-numbers -version: '3.8' +version: "3.8" services: substrate-proxy: - image: uniquenetwork/substrate-proxy:http-proxy-latest + image: uniquenetwork/substrate-proxy-http-proxy:master ports: - - "3000:3000" + - "3000:3000" environment: - - PORT=3000 - - CHAIN=unique - - MIN_LOG_LEVEL=info - - EXTRINSIC_MORTAL_BLOCK_LENGTH=32 - - OPENAPI_SERVER_URL=http://localhost:3000 - - OPENAPI_SERVER_DESCRIPTION="Local development server" - - EXTRINSICS_STORAGE_MAX_BLOCKS_COUNT=100 + - PORT=3000 + - CHAIN=unique + - MIN_LOG_LEVEL=info + - EXTRINSIC_MORTAL_BLOCK_LENGTH=32 + - OPENAPI_SERVER_URL=http://localhost:3000 + - OPENAPI_SERVER_DESCRIPTION="Local development server" + - EXTRINSICS_STORAGE_MAX_BLOCKS_COUNT=100 ``` diff --git a/docs/build/sdk/v2/tokens.md b/docs/build/sdk/v2/tokens.md index 87eb1c4..38d1c4e 100644 --- a/docs/build/sdk/v2/tokens.md +++ b/docs/build/sdk/v2/tokens.md @@ -1,16 +1,14 @@ -# Working with NFTs +# NFTs ::: warning The Unique SDK v2 is in alpha and may contain bugs or incomplete features. For production use or to access more stable functionality, please refer to the [documentation for the previous version](../getting-started.md) of the SDK. ::: -## Intro +[[toc]] -NFT means non-fungible token and non-fungible means that this token is unique and can’t be replaced. +## Intro -NFTs can be everything. They can be a jpg image, music, or digital art. You can be an artist, and create a 3-minute short YouTube video by adding your images and music. -Cool! Now, you can sell this video as an NFT at an excellent price if it brings value to the rest of the community. -It’s like getting paintings at an art gallery but much easier and more convenient. +An NFT, or non-fungible token, represents a unique digital asset that cannot be replaced or exchanged on a one-to-one basis. NFTs can take many forms, such as JPEG images, music, or digital artwork. For example, as an artist, you could create a 3-minute YouTube video by combining your images and music. Once created, you can sell this video as an NFT, potentially at a great price if it offers value to the community. It's similar to purchasing art at a gallery, but it's a much simpler and more accessible process. ## Prerequisite @@ -20,7 +18,7 @@ At this point, you need to know how to manage collections. Learn how to do this ## Getting started -Let's start with a minimum example. At this point, we assume you already minted your NFT collection and you have its `collectionId`. +Let's start with a minimum example. At this point, we assume you already minted your NFT collection, and you have its `collection`. ```ts:no-line-numbers const mintNftTx = await sdk.token.mintNFTs({ @@ -40,12 +38,16 @@ Check your newly created tokens on [Unique Scan](https://uniquescan.io/opal/toke ## Token properties and attributes -In the collections section, we've learned [basics about token properties](./collections.md#understanding-token-property-permissions). Let's do a quick recap. +In the collections section, we've learned [basics about token properties](./collections.md#token-property-permissions). Let's do a quick recap. 1. Token property is a key/value pair -2. The list of possible keys as well as their mutability are set on the collection level +2. The list of possible keys, as well as their mutability, are set on the collection level + + + +### Properties -Now let's create a token and set its properties. +Now, let's create a token and set its properties. ```ts:no-line-numbers // This is an example of a collection created in the collection section @@ -71,9 +73,9 @@ const mintNftTx = await sdk.token.mintNFTs({ }); ``` -In the example above, we've created only one NFT and set only one property - `A`. +In the example above, we created only one NFT and set only one property—`A`. -Later, the NFT owner can specify property `C`. +Later, the NFT owner can specify property `C`. ```ts:no-line-numbers await sdk.token.setProperties( @@ -87,9 +89,9 @@ await sdk.token.setProperties( But because of permissions of property `B` it could have been set only during the minting. So it will remain unset forever. -### Understanding the difference between token properties and attributes +### Attributes -Properties are a part of a token on a core blockchain level. They can be set with arbitrary metadata, i.e. schema name and version, royalties, and so on. +Properties are a part of a token on a core blockchain level. They can be set with arbitrary metadata, i.e., schema name and version, royalties, etc. Attributes define token traits and are not a part of a blockchain core. Examples of attributes could be a `power` or `experience` for a gaming character. In Unique Schema, attributes are stored in `tokenData` property of an NFT. @@ -112,7 +114,7 @@ const mintNftTx = await sdk.token.mintNFTs({ }); ``` -And now let's have a look at the newly created token. +Now, let's have a look at the newly created token. ```ts:no-line-numbers const nft = await sdk.token.get({ @@ -136,11 +138,57 @@ And that is how your token will be displayed on [Unique Scan](https://uniquescan Token attributes +### Properties and attributes mutation + +In the collection section, we've learned that [token properties can be set as mutable](./collections.md#token-property-permissions) by the collection admin or token owner. + +Let's make a quick recap of how it can be done. Below, we set mutability for token property `A`: + +```ts:no-line-numbers +await sdk.collection.create({ + ... + tokenPropertyPermissions: [ + // This is how we specify token properties mutability during the collection creation + {key: 'A', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + + ... +``` + +If the property is specified as mutable, it can be set after the token has been created. + +```ts:no-line-numbers +await sdk.token.setProperties({ + collectionId, + tokenId, + properties: [{ key: "A", value: "New value" }], +}); +``` + +Attributes are part of `tokenData` property which is by default mutable for collection admin. You can override it during the collection creation. + +The SDK provides the following method for attribute mutation: + +```ts:no-line-numbers +await sdk.token.updateNft({ + collectionId, + tokenId, + data: { + attributes: [ + { + trait_type: "Power", + value: 42, + }, + ], + image: "https://your-new-image.here/image.png", + }, +}); +``` + ## Transfer -The token owner can transfer its token if the [collection limits](./collections.md#understanding-collection-limits) do not restrict token transfer. +The token owner can transfer its token if the [collection limits](./collections.md#collection-limits) do not restrict token transfer. -```ts +```ts:no-line-numbers await sdk.token.transfer({ collectionId, tokenId, @@ -148,14 +196,76 @@ await sdk.token.transfer({ }); ``` +It is also possible to approve the transfer for another account. + +```ts:no-line-numbers +// SDK's default account approves NFT for Alice +const approvalTx = await sdk.token.approve({ + collectionId, + tokenId, + spender: alice.address, +}); + +// Now, Alice can transfer approved token +const transferFromTx = await sdk.token.transfer( + { + to: alice.address, + collectionId, + tokenId: token1.tokenId, + from: account.address, + }, + { + signerAddress: alice.address, + }, + // This transaction performed by Alice + alice, +); +``` + ## Burn -The token owner can destroy its token if the [collection limits](./collections.md#understanding-collection-limits) do not restrict token burn. +The token owner can destroy its token if the [collection limits](./collections.md#collection-limits) do not restrict token burn. -```ts +```ts:no-line-numbers await sdk.token.burn({ collectionId, tokenId, }); ``` + + +## Nesting + +In Unique Network token can own other tokens, this mechanism called nesting. In the [collection section](./collections.md#nesting-configuration) you've learned how nesting can be configured on the collection level. + +Now, let's see how tokens can be nested. + +```ts:no-line-numbers +await sdk.token.nest({ + nested: { collectionId, tokenId: token1.tokenId }, + parent: { collectionId, tokenId: token2.tokenId }, +}); +``` + +In the example above, `token1` will be nested to `token2`. This means: + +- `token2` is the owner of `token1`. +- Topmost token owner (real owner) of `token2` will be the owner of `token1` +- if `token2` is transferred to a different account, this new account becomes the topmost owner for `token1` + +The topmost token owner can `unnest` tokens. In the example below, `token1` will be transferred from the `token2` address back to the topmost owner. + +```ts:no-line-numbers +await sdk.token.unnest({ nested: { collectionId, tokenId: token1.tokenId } }); +``` + +:::tip Understanding token address + +In Unique Network every collection and token have unique ID. At the same time these IDs can be mapped to EVM address. So nesting is a simple transfer of a token to the address of other token. + +The concept of collections and token addresses is particularly useful when working with [smart contracts](../../evm/index.md). +::: + + + diff --git a/docs/tutorials/mass-transactions.md b/docs/tutorials/mass-transactions.md index 5877672..330eb76 100644 --- a/docs/tutorials/mass-transactions.md +++ b/docs/tutorials/mass-transactions.md @@ -35,10 +35,12 @@ With this code, only one transaction will succeed, while the second will fail wi The `Priority is too low` error occurs because both transactions are sent with the same nonce. -> In blockchain networks, particularly in transaction validation, a nonce serves several critical purposes: -> -> Preventing Replay Attacks: By ensuring that each transaction has a unique nonce, the system prevents attackers from reusing a previously valid transaction to deceive the network. -> Ensuring Transaction Uniqueness and Order: Nonces ensure that each transaction is unique and help maintain the correct sequence of transactions, preserving the integrity and order of the blockchain. +:::tip +In blockchain networks, particularly in transaction validation, a nonce serves several critical purposes: + +Preventing Replay Attacks: By ensuring that each transaction has a unique nonce, the system prevents attackers from reusing a previously valid transaction to deceive the network. +Ensuring Transaction Uniqueness and Order: Nonces ensure that each transaction is unique and help maintain the correct sequence of transactions, preserving the integrity and order of the blockchain. +::: Each account in the network maintains a nonce. The nonce value in a transaction increments by 1 upon the transaction's execution, regardless of whether the transaction succeeds or fails. If a transaction is executed without specifying a nonce, it will be requested before signing the transaction. In the above example, since both transactions are executed almost simultaneously, the network doesn't have time to increment the nonce, resulting in both transactions being sent with the same nonce. @@ -56,10 +58,10 @@ const [token1, token2, ...other] = await Promise.all([ ]); ``` -> **Why doesn't the SDK handle the nonce automatically?** -> -> There is no universally reliable way to manage the nonce implicitly. Such attempts can lead to unintended consequences and still may not fully resolve the issue. +:::tip Why doesn't the SDK handle the nonce automatically? +There is no universally reliable way to manage the nonce implicitly. Such attempts can lead to unintended consequences and still may not fully resolve the issue. +::: ### Alternative methods