diff --git a/docs/.vuepress/components/Highlight.vue b/docs/.vuepress/components/Highlight.vue new file mode 100644 index 0000000..9bb15d9 --- /dev/null +++ b/docs/.vuepress/components/Highlight.vue @@ -0,0 +1,50 @@ + + + + + diff --git a/docs/.vuepress/configs/sidebar.ts b/docs/.vuepress/configs/sidebar.ts index 4e7357a..f28cc67 100644 --- a/docs/.vuepress/configs/sidebar.ts +++ b/docs/.vuepress/configs/sidebar.ts @@ -76,6 +76,16 @@ export const sidebar: Record = { '/build/sdk/sponsoring.md', ] }, + { + text: 'SDK V2 [Alpha]', + children: [ + '/build/sdk/v2/quick-start.md', + '/build/sdk/v2/balances.md', + '/build/sdk/v2/collections.md', + '/build/sdk/v2/tokens.md', + '/build/sdk/v2/sponsoring.md', + ] + }, { text: 'EVM', children: [ diff --git a/docs/build/sdk/images/token-attributes.png b/docs/build/sdk/images/token-attributes.png new file mode 100644 index 0000000..7e0eafb Binary files /dev/null and b/docs/build/sdk/images/token-attributes.png differ diff --git a/docs/build/sdk/tokens.md b/docs/build/sdk/tokens.md index 34719b2..bfe032b 100644 --- a/docs/build/sdk/tokens.md +++ b/docs/build/sdk/tokens.md @@ -4,8 +4,7 @@ NFT means non-fungible token, and non-fungible means that this token is unique and can’t be replaced. -NFTs can be everything. They can be a jpg image, music, or digital art. You can be an artist, -create a 3 minute short YouTube video by adding your images and music. +NFTs can be everything. They can be a jpg image, music, or digital art. You can be an artist, 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 just like getting paintings at an art gallery but much easier and more convenient. diff --git a/docs/build/sdk/v2/balances.md b/docs/build/sdk/v2/balances.md new file mode 100644 index 0000000..963f2fc --- /dev/null +++ b/docs/build/sdk/v2/balances.md @@ -0,0 +1,72 @@ +# Working with balances + +## Prerequisite + +Follow the [Getting started guide](./quick-start.md) to install required libraries, receive test network OPL tokens, and initialize SDK. + +## Get balance + +After receiving OPL tokens you can check your account's balance using SDK. + +```ts:no-line-numbers +const balances = await unique.balance.get({ + address: account.address +}); + +console.log(balances); +``` + +The output will resemble the following: + +```ts:no-line-numbers +{ + available: '270171775322286038926', + locked: '0', + free: '270171775322286038926', + total: '270171775322286038926', + reserved: '0', + staked: '0', + unstaked: '0', + canstake: '270171775322286038926', + vested: [], + decimals: 18, + tokenSymbol: 'OPL' +} +``` + +Let's understand balance meaning: + +- `available`: the balance user can transfer. Most application should operate with this balance +- `locked`: the balance locked by staking or vesting +- `free`: the sum of `available` and `locked` +- `reserved`: the balance reserved by collator selection pallet +- `total`: the sum of `free` and `reserved` balance +- `staked`: the balance locked by staking +- `unstaked`: the balance that has been unstaked and awaiting unlocking period (~ 7 days) +- `canstake`: the balance user can stake +- `vested`: the balance locked by vesting pallet +- `decimals`: all balances are in the wei. This field shows what is the decimals part +- `tokenSymbol`: token symbol + +## Transfer + +The account can transfer tokens in `available` status. + +```ts:no-line-numbers +await unique.balance.transfer({ + amount: '100', + to: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" +}); +``` + +By default `amount` is set in wei. + +It is also possible to specify transfer in coins. + +```ts:no-line-numbers +await unique.balance.transfer({ + amount: '100.44', + to: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY", + isAmountInCoins: true // <--- in this case the transfer amount will be 100.44e18 +}); +``` \ No newline at end of file diff --git a/docs/build/sdk/v2/collections.md b/docs/build/sdk/v2/collections.md new file mode 100644 index 0000000..25cd72a --- /dev/null +++ b/docs/build/sdk/v2/collections.md @@ -0,0 +1,220 @@ +# Working with collections + + +The Unique SDK v2 is currently 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 of the SDK. + + +[[toc]] + +## 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. + +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. + +In Unique Network, the collection has the following entities: + +- **name** - a collection name that defines it in the global scope; +- **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; +- **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. + +## Prerequisite + +Follow the [Getting started guide](./quick-start.md) to install required libraries, receive test network OPL tokens, and initialize SDK. + +## Creating a collection + +Below is an (almost) minimum example. Only collection `name`, `description`, and `symbol` are mandatory fields. They exist at the collection level and cannot be overridden. + +Collection coverage is a part of collection metadata and is not a mandatory field. + +```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'}}, +}); + +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/). + +## Understanding collection modes + +There are three different collection modes in Unique Network: +- NFT (default) +- Fungible +- ReFungible + +You can specify the collection mode during the 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 +}); +``` + +## Understanding 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. + +### Setting collection properties + +During the collection creation, you can set collection limits as follows: + +```ts:no-line-numbers +const {result} = await sdk.collection.create({ + name: "Test", + description: "Test collection", + 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"}, + ] +}); +``` + +Later you can set new properties or modify previously created ones. + +```ts:no-line-numbers +... + +await sdk.collection.setProperties({ + collectionId: result.collectionId, + properties: [{key: "C", value: "value C"}] +}); +``` + +### Now let's query our collection and check its properties + +```ts:no-line-numbers +const collection = await sdk.collection.get({idOrAddress: result.collectionId}); + +console.log(collection.properties); +``` + +The result will be as follows, let's break it down. + +```ts:no-line-numbers +[ + { + key: "A", + value: "value A", + valueHex: "0x76616c75652041", + }, + { + key: "B", + value: "value B", + valueHex: "0x76616c75652042", + }, + { + key: "C", + value: "value C", + valueHex: "0x76616c75652043", + }, + { + key: "collectionInfo", + value: "{\"schemaName\":\"unique\",\"schemaVersion\":\"2.0.0\",\"cover_image\":{\"url\":\"https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m\"}}", + valueHex: "0x7b22736368656d614e616d65223a22756e69717565222c22736368656d6156657273696f6e223a22322e302e30222c22636f7665725f696d616765223a7b2275726c223a2268747470733a2f2f676174657761792e70696e6174612e636c6f75642f697066732f516d546b6854673553357a72714a4c3355734b74796946693866634d543343616f39754b7461647033436b68376d227d7d", + }, + { + key: "schemaName", + value: "unique", + valueHex: "0x756e69717565", + }, + { + key: "schemaVersion", + value: "2.0.0", + valueHex: "0x322e302e30", + }, +] +``` + +- 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. + + +## Understanding 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. + +Let's look at how to specify them. + +```ts:no-line-numbers +await sdk.collection.create({ + name: "Test", + description: "Test collection", + symbol: "TST", + info: {cover_image: {url: coverImage}}, + tokenPropertyPermissions: [ // <--- set token property permissions here + {key: 'A', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + {key: 'B', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}, + {key: 'C', permission: {mutable: false, collectionAdmin: false, tokenOwner: true}}, + ] +}); +``` + +Every NFT token in the collection could have two 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 + +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}) + +console.log(colleciton.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 + +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 + +You can read more about collection limits in the [reference section](../../../reference/blockchain/collections.md#limits). + +And that is how you can set such limits: + +```ts:no-line-numbers +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, + ownerCanTransfer: false, + sponsorApproveTimeout: 100, + sponsoredDataRateLimit: 100, + sponsoredDataSize: 2048, + sponsorTransferTimeout: 10, + tokenLimit: 300, + transfersEnabled: false + }, +}); +``` diff --git a/docs/build/sdk/v2/quick-start.md b/docs/build/sdk/v2/quick-start.md new file mode 100644 index 0000000..0739634 --- /dev/null +++ b/docs/build/sdk/v2/quick-start.md @@ -0,0 +1,95 @@ +# Unique SDK quick start + + +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 of the SDK. + + +The SDK facilitates seamless integration of Unique Network's capabilities into the web3 application, bypassing the need for direct low-level API interaction. It enables you to effortlessly mint collections and tokens, manage account balances, and more. + +All transactions require a fee so that you can use Opal tokens for test purposes. You can get them free in [Telegram faucet bot](https://t.me/unique2faucet_opal_bot). + +[[toc]] + +## Getting started + +### Installation + +Install `@unique-nft/sdk` for Unique Network interaction and `@unique-nft/sr25519` for account management. + + + + + +```bash:no-line-numbers +npm install @unique-nft/sdk@alpha @unique-nft/sr25519 +``` + + + + +```bash:no-line-numbers +yarn add @unique-nft/sdk@alpha @unique-nft/sr25519 +``` + + + + +### Import and initialize the SDK + +To begin using the Unique SDK, you need to import the required modules, set the base URL for the API, and optionally configure the default signer account. + + +```typescript:no-line-numbers +import { UniqueChain } from "@unique-nft/sdk"; +import { Sr25519Account } from "@unique-nft/sr25519"; + + +const mnemonic = "SET THE MNEMONIC SEED PHRASE FOR THE DEFAULT SIGNER HERE"; +const account = Sr25519Account.fromUri(mnemonic); + +// set "account" as a default signer +const sdk = UniqueChain({ + baseUrl: "https://rest.uniquenetwork.dev/v2/opal", + account, +}); +``` + +### Make some requests + +```typescript:no-line-numbers +... + +const balanceQuery = await sdk.balance.get({address: account.address}); +console.log("Account's total balance:", balanceRequest.total); +``` + +The Unique SDK currently supports the following modules: + +- `collection`: create, update, and manage NFT collections. +- `token`: mint, transfer, and manage individual NFTs. +- `balance`: manage and query account balances. +- `extrinsic`: build, sign, submit any extrinsic. +- `options`: configure SDK options. + +## Run your own HTTP proxy + +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' + +services: + substrate-proxy: + image: uniquenetwork/substrate-proxy:http-proxy-latest + ports: + - "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 +``` + diff --git a/docs/build/sdk/v2/sponsoring.md b/docs/build/sdk/v2/sponsoring.md new file mode 100644 index 0000000..59f2b6a --- /dev/null +++ b/docs/build/sdk/v2/sponsoring.md @@ -0,0 +1,48 @@ +# Collection sponsoring + +In Unique Network, transactions 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. + +[[toc]] + + +## Prerequisite + +Follow the [Getting started guide](./quick-start.md) to install required libraries, receive test network OPL tokens, and initialize SDK. + +At this point, you need to know how to manage collections. Learn how to do this in the [Working with collections](./collections.md) guide. + +You also need to know how to [mint and transfer NFTs](./tokens.md). + +## Setting collection sponsoring + +The process consists of two steps: + +1. Set collection sponsor – only the collection owner or admin can do +2. Confirm sponsorship. The sponsor should confirm willingness to sponsor collection + +```ts:no-line-numbers +// At this point we assume you already have a minted collection and NFT +const collectionId = ... +const tokenId = ... + +await sdk.collection.setSponsor({collectionId, sponsor: account.address}) +await sdk.collection.confirmSponsorship({collectionId}); +``` + +At this point, every action with tokens of this collection, such as transfer or minting, will be sponsored by `account`. + \ No newline at end of file diff --git a/docs/build/sdk/v2/tokens.md b/docs/build/sdk/v2/tokens.md new file mode 100644 index 0000000..d3a0749 --- /dev/null +++ b/docs/build/sdk/v2/tokens.md @@ -0,0 +1,161 @@ +# Working with NFTs + + +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 of the SDK. + + +## Intro + +NFT means non-fungible token and non-fungible means that this token is unique and can’t be replaced. + +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. + +## Prerequisite + +Follow the [Getting started guide](./quick-start.md) to install required libraries, receive test network OPL tokens, and initialize SDK. + +At this point, you need to know how to manage collections. Learn how to do this in the [Working with collections](./collections.md) guide. + +## 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`. + +```ts:no-line-numbers +const mintNftTx = await sdk.token.mintNFTs({ + collectionId, + tokens: [ + {data: {image: 'https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m'}}, + {data: {image: 'https://gateway.pinata.cloud/ipfs/QmQRUMbyfvioTcYiJYorEK6vNT3iN4pM6Sci9A2gQBuwuA'}}, + ] +}); + +const [nft1, nft2] = mintNftTx.result; + +console.log('Minted tokens:', nft1.tokenId, nft2.tokenId); +``` + +Check your newly created tokens on [Unique Scan](https://uniquescan.io/opal/tokens/nfts) + +## 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. + +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 + +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 +const {result} = await sdk.collection.create({ + name: "Test", + description: "Test collection", + symbol: "TST", + info: {cover_image: {url: coverImage}}, + tokenPropertyPermissions: [ // <--- set token property permissions here + {key: 'A', permission: {mutable: true, collectionAdmin: true, tokenOwner: true}}, + {key: 'B', permission: {mutable: false, collectionAdmin: false, tokenOwner: false}}, + {key: 'C', permission: {mutable: false, collectionAdmin: false, tokenOwner: true}}, + ] +}); + +const nftImage = "https://gateway.pinata.cloud/ipfs/QmTkhTg5S5zrqJL3UsKtyiFi8fcMT3Cao9uKtadp3Ckh7m"; + +const mintNftTx = await sdk.token.mintNFTs({ + collectionId: result.collectionId, + tokens: [ + {data: {image: nftImage}, properties: [{key: "A", value: "value A"}]}, + ] +}); +``` + +In the example above, we've created only one NFT and set only one property - `A`. + +Later, the NFT owner can specify property `C`. + +```ts:no-line-numbers +await sdk.token.setProperties( + { + collectionId, + tokenId: mintNftTx.result[0].tokenId, + properties: [{key: "C", value: "value C"}] + } +); +``` + +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 + +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. + +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. + +Let's mint some extra tokens with attributes. + +```ts:no-line-numbers +const mintNftTx = await sdk.token.mintNFTs({ + collectionId: result.collectionId, + tokens: [ + { + data: { + image: nftImage, + attributes: [ // <--- setting attributes + {trait_type: "power", value: 50}, + {trait_type: "experience", value: 300} + ] + } + }, + ] +}); +``` + +And now let's have a look at the newly created token. + +```ts:no-line-numbers +const nft = await sdk.token.get({ + collectionIdOrAddress: result.collectionId, + tokenId: 1 +}); + +console.log(nft.attributes) +``` + +The output: + +```ts:no-line-numbers +attributes: [ + { trait_type: 'power', value: 50 }, + { trait_type: 'experience', value: 300 } +], +``` + +And that is how your token will be displayed on [Unique Scan](https://uniquescan.io/opal/tokens) and other wallets. + +Token attributes + +## Transfer + +The token owner can transfer its token if the [collection limits](./collections.md#understanding-collection-limits) do not restrict token transfer. + +```ts +await sdk.token.transfer({ + collectionId, + tokenId, + to: "5GrwvaEF5zXb26Fz9rcQpDWS57CtERHpNehXCPcNoHGKutQY" +}); +``` + +## Burn + +The token owner can destroy its token if the [collection limits](./collections.md#understanding-collection-limits) do not restrict token burn. + +```ts +await sdk.token.burn({ + collectionId, + tokenId, +}); +``` +