From feb469bfcdd4250e4097a6a21ceed73b02aa63e3 Mon Sep 17 00:00:00 2001 From: Max Andreev Date: Thu, 24 Oct 2024 18:18:49 +0400 Subject: [PATCH] remove outdated tutorials (#177) --- docs/.vuepress/configs/sidebar.ts | 45 +- docs/tutorials/accounts/create-account.md | 195 ------- .../accounts/integrate-creating-into-UI.md | 74 --- docs/tutorials/burnNFT.md | 111 ---- docs/tutorials/create-collection-token.md | 87 --- docs/tutorials/createAccount.md | 21 - docs/tutorials/createCollectionV2/index.md | 188 ------- docs/tutorials/destroyCollection.md | 92 ---- docs/tutorials/easy-market.md | 382 ------------- docs/tutorials/examplesREST.md | 207 ------- docs/tutorials/getAccountAndBalance.md | 47 -- docs/tutorials/getNFTsAndTokens.md | 294 ---------- docs/tutorials/graph-node-docker.md | 101 ---- .../how-to-get-collections-tokens.md | 108 ---- .../how-to-use-nesting-and-bundling.md | 255 --------- docs/tutorials/live-nft-example.md | 171 ------ docs/tutorials/nfts-fetching.md | 212 ------- .../nfts-how-to-create-and-tune-collection.md | 139 ----- docs/tutorials/nfts-how-to-mint.md | 191 ------- docs/tutorials/nfts-ways-to-create.md | 166 ------ docs/tutorials/store-files.md | 93 ---- docs/tutorials/subquery-indexer.md | 21 - docs/tutorials/wallet-integration.md | 79 --- docs/tutorials/websocket-subscriptions.md | 227 -------- docs/tutorials/work-with-accounts.md | 520 ------------------ docs/tutorials/work-with-evm-via-sdk.md | 352 ------------ 26 files changed, 5 insertions(+), 4373 deletions(-) delete mode 100644 docs/tutorials/accounts/create-account.md delete mode 100644 docs/tutorials/accounts/integrate-creating-into-UI.md delete mode 100644 docs/tutorials/burnNFT.md delete mode 100644 docs/tutorials/create-collection-token.md delete mode 100644 docs/tutorials/createAccount.md delete mode 100644 docs/tutorials/createCollectionV2/index.md delete mode 100644 docs/tutorials/destroyCollection.md delete mode 100644 docs/tutorials/easy-market.md delete mode 100644 docs/tutorials/examplesREST.md delete mode 100644 docs/tutorials/getAccountAndBalance.md delete mode 100644 docs/tutorials/getNFTsAndTokens.md delete mode 100644 docs/tutorials/graph-node-docker.md delete mode 100644 docs/tutorials/how-to-get-collections-tokens.md delete mode 100644 docs/tutorials/how-to-use-nesting-and-bundling.md delete mode 100644 docs/tutorials/live-nft-example.md delete mode 100644 docs/tutorials/nfts-fetching.md delete mode 100644 docs/tutorials/nfts-how-to-create-and-tune-collection.md delete mode 100644 docs/tutorials/nfts-how-to-mint.md delete mode 100644 docs/tutorials/nfts-ways-to-create.md delete mode 100644 docs/tutorials/store-files.md delete mode 100644 docs/tutorials/subquery-indexer.md delete mode 100644 docs/tutorials/wallet-integration.md delete mode 100644 docs/tutorials/websocket-subscriptions.md delete mode 100644 docs/tutorials/work-with-accounts.md delete mode 100644 docs/tutorials/work-with-evm-via-sdk.md diff --git a/docs/.vuepress/configs/sidebar.ts b/docs/.vuepress/configs/sidebar.ts index a1c0287..eafd6a4 100644 --- a/docs/.vuepress/configs/sidebar.ts +++ b/docs/.vuepress/configs/sidebar.ts @@ -115,26 +115,6 @@ export const sidebar: Record = { '/tutorials' ] }, - { - text: 'SDK guides', - children: [ - '/tutorials/work-with-accounts.md', - // '/tutorials/accounts/create-account.md', - how-to-accounts.md - // '/tutorials/getAccountAndBalance.md', - how-to-accounts.md - // '/tutorials/create-collection-token.md', - exists in how-to-collections.md + how-to-tokens.md - // '/tutorials/accounts/integrate-creating-into-UI.md', - how-to-accounts.md - '/tutorials/nfts-how-to-create-and-tune-collection.md', - '/tutorials/nfts-how-to-mint.md', - '/tutorials/createCollectionV2', - // '/tutorials/store-files.md', - duplicate - // '/tutorials/nfts-ways-to-create.md', - duplicate - // '/tutorials/destroyCollection.md', - how-to-collections.md - // '/tutorials/burnNFT.md', - how-to-tokens.md - '/tutorials/work-with-evm-via-sdk.md', - // '/tutorials/live-nft-example.md', -> moved to SDK life nft - '/tutorials/websocket-subscriptions.md', - ] - }, { text: 'Minting guides', children: [ @@ -158,30 +138,15 @@ export const sidebar: Record = { // '/tutorials/evm/how-to-ethereum.md', - disassembled ], }, - { - text: 'REST API', - children: [ - '/tutorials/examplesREST.md', - ] - }, - { - text: 'GraphQL', - children: [ - '/tutorials/graph-node-docker.md', - '/tutorials/subquery-indexer.md', - '/tutorials/wallet-integration.md', - '/tutorials/how-to-get-collections-tokens.md', - '/tutorials/nfts-fetching.md', - ], - }, + // { + // text: 'REST API', + // children: [ + // ] + // }, { text: 'How to', children: [ - // '/tutorials/createAccount.md', - how-to-accounts.md '/tutorials/mass-transactions.md', - '/tutorials/getNFTsAndTokens.md', - '/tutorials/easy-market.md', - // '/tutorials/how-to-use-nesting-and-bundling.md', -> moved to SDK nesting ] }, { diff --git a/docs/tutorials/accounts/create-account.md b/docs/tutorials/accounts/create-account.md deleted file mode 100644 index 3f8e8c4..0000000 --- a/docs/tutorials/accounts/create-account.md +++ /dev/null @@ -1,195 +0,0 @@ -# Create an account - -In this tutorial, we will go through the entire process of creating an account using the Unique Network SDK. - -Consider using how you can create or get an account using the [Accounts](https://www.npmjs.com/package/@unique-nft/accounts) package. - -You will need to come up with or generate a mnemonic phrase (this is a set of words that can be used to create and restore your wallet). - -:warning: Never share your mnemonic phrase with anyone. If someone gets access to your mnemonic phrase, they can steal your funds. - -### Generate a new account - -An easy way to create a new account is to use the `generateAccount` function from the `Accounts` package: - -```typescript:no-line-numbers -import { generateAccount, SignatureType } from "@unique-nft/accounts"; - -const account = await generateAccount({ - pairType: SignatureType.Sr25519, - meta: { - name: 'my_test_account' - } -}) - -console.log(account); - -``` - -
-
- -### Get an account from mnemoni - -If you already have a mnemonic phrase, you can use it to get an account. Here is how the phrase looks like: - -`` -affair spoon other impact target solve extra range cute myself float panda -`` - -Here is how we can use it to get an account. - -```typescript:no-line-numbers -import { getAccountFromMnemonic } from '@unique-nft/accounts'; - -const account = await getAccountFromMnemonic({ - mnemonic: 'affair spoon other impact target solve extra range cute myself float panda', -}); -console.log(account); -``` - -
-
- -Or, we can generate a mnemonic phrase and then get an account using it: - -```typescript:no-line-numbers -import { getAccountFromMnemonic } from '@unique-nft/accounts'; -import { mnemonicGenerate } from '@polkadot/util-crypto'; - -const mnemonicPhrase = mnemonicGenerate(); -const account = await getAccountFromMnemonic({ - mnemonic: mnemonicPhrase, -}); -``` - -### Providers - -If you need to get an account from one specific provider, then it is not necessary to create an Accounts object, you can contact the provider directly: - -```typescript:no-line-numbers -import { Account } from '@unique-nft/accounts'; -import { KeyringProvider } from '@unique-nft/accounts/keyring'; -import { KeyringOptions } from '@polkadot/keyring/types'; - -const options: KeyringOptions = { - type: 'sr25519', -}; -const provider = new KeyringProvider(options); -await provider.init(); -const signer = provider.addSeed(''); -``` - -The following providers are supported: - - - - -```typescript:no-line-numbers -// The provider works directly with the chain using `KeyringPair` from the `@polkadotkeyring` package. -import { Account } from '@unique-nft/accounts'; -import { KeyringProvider } from '@unique-nft/accounts/keyring'; -import { KeyringOptions } from '@polkadot/keyring/types'; - -const options: KeyringOptions = { - type: 'sr25519', - }; - const provider = new KeyringProvider(options); - await provider.init(); - -const signer1 = provider.addSeed(''); -const signer2 = provider.addKeyfile(''); -``` - - - -```typescript:no-line-numbers -import { Account } from '@unique-nft/accounts'; -import { KeyringPair } from '@polkadot/keyring/types'; -import { - KeyringLocalOptions, - KeyringLocalProvider, - } from '@unique-nft/accounts/keyring-local'; - -const options: KeyringLocalOptions = { - type: 'sr25519', - passwordCallback: async (keyring: KeyringPair) => { - return ''; - }, - }; - const provider = new KeyringLocalProvider(options); - await provider.init(); - -const signer = provider.addUri('', ''); -``` - - - - -```typescript:no-line-numbers -// The provider uses the Polkadot extension (https://polkadot.js.org/extension) for the browser. -import { Web3AccountsOptions } from '@polkadot/extension-inject/types'; -import { Account } from '@unique-nft/accounts'; -import { PolkadotProvider } from '@unique-nft/accounts/polkadot'; - -const options: Web3AccountsOptions = { - accountType: ['sr25519'], - }; - const provider = new PolkadotProvider(options); - await provider.init(); - -const signer = await provider.first(); -``` - - - - -```typescript:no-line-numbers -// The provider uses the Metamask extension (https://metamask.io/download) for the browser. -import { Account } from '@unique-nft/accounts'; -import { MetamaskProvider } from '@unique-nft/accounts/metamask'; - -const provider = new MetamaskProvider(); -await provider.init(); - -const signer = await provider.first(); -``` - - - diff --git a/docs/tutorials/accounts/integrate-creating-into-UI.md b/docs/tutorials/accounts/integrate-creating-into-UI.md deleted file mode 100644 index 398fa53..0000000 --- a/docs/tutorials/accounts/integrate-creating-into-UI.md +++ /dev/null @@ -1,74 +0,0 @@ -# Create an account via web form - -In this tutorial, we will create a userform for adding a new account right in your web UI. - -To work with accounts, we will need the ``Accounts`` object and a provider, such as ``KeyringLocalProvider``, -which saves accounts in a secure store using the ``@polkadot/ui-keyring`` package. - -First of all, we need to initialize the provider: - -```typescript:no-line-numbers -import { Accounts } from '@unique-nft/accounts'; -import { KeyringLocalProvider } from '@unique-nft/accounts/keyring-local'; - -... - -const options: KeyringLocalOptions = { - type: 'sr25519', // - passwordCallback: async (keyring: KeyringPair) => { - ... // here you need to ask the user to enter a password to sign the transaction and return it from this callback - }, -}; - -const provider = new KeyringLocalProvider(options); -await provider.init(); -``` - -Next, we need to associate it with the ``Accounts`` instance: - -```typescript:no-line-numbers -const accounts = new Accounts(); -await accounts.addProvider(provider); -``` - -Finally, let’s create a web form which will use this code. Please find below a code sample on React. -The user interface contains two fields: a mnemonic phrase and a password that must be filled. -Optionally, you can offer to fill in the account name. - -```jsx:no-line-numbers -
-
- - setMnemonicPhrase(e.target.value)}/> -
-
- - setName(e.target.value)} /> -
-
- - setPassword(e.target.value)} /> -
- -
-``` - -Using the ``mnemonicGenerate`` method from the ``@polkadot/util-crypto`` library, you can generate a new mnemonic: - -```typescript:no-line-numbers -const newMnemonicPhrase = mnemonicGenerate(); -``` - -In the ``onSubmit`` function, we will add an account through a provider this way: - -```typescript:no-line-numbers -const onSubmit = () => { - provider.addUri(mnemonicPhrase, password, { name }); -} -``` - -After that, the account will be added to the local storage and it will be possible to get it through the ``getAccounts`` method: - -```typescript:no-line-numbers -const accountsList = await accounts.getAccounts(); -``` diff --git a/docs/tutorials/burnNFT.md b/docs/tutorials/burnNFT.md deleted file mode 100644 index 8727c50..0000000 --- a/docs/tutorials/burnNFT.md +++ /dev/null @@ -1,111 +0,0 @@ -# Burn an NFT - -In this guide, we will go through the entire process on how to burn an NFT using the Unique Network SDK. - -### Prerequisites - -To get started, you need to have a [Substrate address](../tutorials/accounts/create-account.md) and an existing collection (see [create a collection](../tutorials/create-collection-token.md). Also, you should have an NFT (see [create an NFT](../tutorials/create-collection-token.md)) that we are going to burn. - -### Burn NFT, RFT or bundle - -The method, that we are going to use, allows destroying an instance of NFT, amount of Fungible tokens, an RFT, or even a bundle. - -If the `from` parameter is not specified, then the token is destroyed on behalf of the owner of the item. Only the collection owner, collection admin, or current NFT owner has permission to call this method. - -Here are some specifics for different modes: - -- Non-Fungible Mode: the default mode, nothing to handle. -- Fungible Mode: you must specify the transferred amount. -- Re-Fungible Mode: you must specify transferred portion (between 0 and 1). -- Bundle: you can burn only those tokens in the bundle that do not contain other tokens. If you want to burn such the token, then you must pull out all the tokens from it first. - -### Sample code - -```typescript:no-line-numbers -import '@unique-nft/substrate-client/tokens'; -import { BurnTokenArguments } from '@unique-nft/substrate-client/tokens/types'; - -const burnItemArgs: BurnTokenArguments = { - tokenId: 1, - collectionId: 1, -}; - -const setResult = await sdk.token.burn.submitWaitResult(burnItemArgs); -const { collectionId, tokenId, address, value } = setResult.parsed; -``` - -### Examples - - - - - -```typescript:no-line-numbers -import Sdk from "@unique-nft/sdk"; - -const sdk = new Sdk({ baseUrl: 'https://rest.unique.network/opal' }); - -const result = await sdk.token.burn.submitWaitResult({ - "collectionId": 1, - "tokenId": 1, - "address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" -}); - -const { parsed: { collectionId, tokenId } } = result; - -console.log(`burned token ${tokenId} collection ${collectionId}`); -``` - - - - -```typescript:no-line-numbers -import '@unique-nft/substrate-client/tokens'; -import { BurnTokenArguments } from '@unique-nft/substrate-client/tokens/types'; - -const burnItemArgs: BurnTokenArguments = { - tokenId: 1, - collectionId: 1, -}; - -const setResult = await sdk.token.burn.submitWaitResult(burnItemArgs); -const { collectionId, tokenId, address, value } = setResult.parsed; -``` - - - - -```bash:no-line-numbers - curl -X 'DELETE' \ - 'http://rest.unique.network/opal/token?use=Build&withFee=false&verify=false' \ - -H 'accept: application/json' \ - -H 'Content-Type: application/json' \ - -d '{ - "collectionId": 183, - "tokenId": 5, - "address": "5FHneW46xGXgs5mUiveU4sbTyGBzmstUspZC92UhjJM694ty" - }' - - # then we sign, then we call - - curl -X 'POST' \ - 'https://rest.unique.network/opal/extrinsic/submit' \ - -H 'accept: application/json' \ - -H 'Content-Type: application/json' \ - -d '{ - "signerPayloadJSON": { *from previous response* }, - "signature": "0x_your_signature_in_hex" - }' -``` - - - - - - - - - - - - diff --git a/docs/tutorials/create-collection-token.md b/docs/tutorials/create-collection-token.md deleted file mode 100644 index 2c0b858..0000000 --- a/docs/tutorials/create-collection-token.md +++ /dev/null @@ -1,87 +0,0 @@ -# Create a collection and mint a token - -The following example demonstrates how to use the Unique SDK to create a collection and add a token to it. - -The extrinsic lifecycle consists of the following operations: - -1. Request the REST service to build the extrinsic. -2. Sign the extrinsic. -3. Submit the signed extrinsic. - - We'll need a Substrate address to use in this example. Since we need to provide the *mnemonic seed* and the *wallet address* at some point remember to have these two data items handy if you are using an already existing account or make a note of these two data items during the account creation process if you will be creating a new account for the sake of this exercise. The instructions for creating an account with the Polkadot.js wallet browser extension can be found [here](../tutorials/createAccount.md). And, since some Opal tokens are required to pay for the transaction fees as well (around 2 to 2.5 OPL) note that these can be obtained via the [Telegram faucet bot](https://t.me/unique2faucet_opal_bot). - -We will use the following packages this sample: - -- [@unique-nft/sdk](https://www.npmjs.com/package/@unique-nft/sdk) -- [@unique-nft/accounts](https://www.npmjs.com/package/@unique-nft/accounts) - -```ts -import Sdk from '@unique-nft/sdk'; -import { KeyringProvider } from '@unique-nft/accounts/keyring'; - -const baseUrl = 'https://rest.unique.network/opal/v1'; -const mnemonic = 'bus ahead nation nice damp recall place dance guide media clap language'; - -// Creating an SDK client -function createSdk(account) { - const options = { - baseUrl, - signer: account, - } - return new Sdk(options); -} - -// Creating a sample collection -// The signer specified in the SDK constructor is used to sign an extrinsic -export async function createCollection(sdk, address) { - const { parsed, error } = await sdk.collection.create.submitWaitResult({ - address, - name: 'Test collection', - description: 'My test collection', - tokenPrefix: 'TST', - }); - - if (error) { - console.log('create collection error', error); - process.exit(); - } - - const { collectionId } = parsed; - - return sdk.collection.get({ collectionId }); -} - -// Creating a sample token in the newly created collection -// The signer specified in the SDK constructor is used to sign an extrinsic -export async function createToken(sdk, address, collectionId) { - const { parsed, error } = await sdk.token.create.submitWaitResult({ - address, - collectionId, - }); - - if (error) { - console.log('create token error', error); - process.exit(); - } - - const { tokenId } = parsed; - - return sdk.token.get({ collectionId, tokenId }); -} - -// Entrypoint -async function main() { - const signer = await KeyringProvider.fromMnemonic(mnemonic); - const address = signer.instance.address; - - const sdk = createSdk(signer); - - const collection = await createCollection(sdk, address); - console.log('collection', collection); - - const token = await createToken(sdk, address, collection.id); - console.log('token', token); -} - -main(); -``` diff --git a/docs/tutorials/createAccount.md b/docs/tutorials/createAccount.md deleted file mode 100644 index 0c32388..0000000 --- a/docs/tutorials/createAccount.md +++ /dev/null @@ -1,21 +0,0 @@ -# Creating Substrate Address via browser extension - -Unique Network, like most blockchains, is based on [accounts or addresses](/concepts/addresses/index.md). An address can own some QTZ or UNQ tokens, NFTs or some ERC-20 tokens. It can sign transactions to transfer these valuable assets to other addresses or to make some actions in Decentralized Apps (dApps). For example, an address can buy and sell NFTs on the NFT Marketplace. - -A typical Quartz address looks like this: `yGHuU9CWnrHMUz8GJRmpA9MowmtMKZvnq2tLc5mk3zMFizW5X` - -So, to purchase and sell tokens on the Market, you need to have a Unique Network account. To create a new account, you should get an address that can own NFTs and allow you to manage KSM. The best way to get an address is to install the **Polkadot{.js} browser extension** [for Chrome](https://chrome.google.com/webstore/detail/polkadot%7Bjs%7D-extension/mopnmbcafieddcagagdcbnhejhlodfdd) or [for Firefox](https://addons.mozilla.org/en-US/firefox/addon/polkadot-js-extension/). This browser extension manages accounts and allows you to sign transactions with these accounts. - -To create a Unique Network account: -1. Download the [Polkadot{.js} extension](https://polkadot.js.org/extension/) and add it to your browser. -2. Click on the Polkadot{.js} extension in your browser. -3. In the **Authorize** window, read the disclaimer and click **Yes, allow this application access**. -4. Click the **Add Account** icon (**+**) and then click **Create new account**. This will generate your wallet address and the 12-word mnemonic seed – the series of words that can be used to restore your wallet. :warning: Copy this seed and keep it a safe place. :warning: - ![Seed](./images/seed.png) -5. Select "I have saved my mnemonic seed safely" and click **Next step**. -6. On the next screen, in the _NETWORK_ drop-down menu, select **Allow use on any chain**. Add a name for your account and create a new password by entering the password twice. - ![AccountName](./images/acc-name.png) -7. Click **Add the account with the generated seed**. Your newly created account is now displayed in the Accounts window. - ![AccountList](./images/acc-list.png) - -Each account has an icon displayed next to the account name. Clicking this icon, you copy the account address to the clipboard. diff --git a/docs/tutorials/createCollectionV2/index.md b/docs/tutorials/createCollectionV2/index.md deleted file mode 100644 index 76d597e..0000000 --- a/docs/tutorials/createCollectionV2/index.md +++ /dev/null @@ -1,188 +0,0 @@ -# Mint an NFT using Unique Schema v2 - -Below is an example of creating a collection and generating a token. Upon executing this script, you will obtain a collection and token similar to the following: - [Collection](https://unqnft.io/collection/654?filterState=) - [NFT1](https://unqnft.io/unique/token/654/1). - [NFT2](https://unqnft.io/unique/token/654/4). - - [Metadata description](/reference/schemas) - -```typescript:no-line-numbers -import { Sr25519Account } from '@unique-nft/utils/sr25519' -import Sdk from '@unique-nft/sdk' -import * as dotenv from 'dotenv' - -const SUBSTRATE_MNEMONIC = PUT_YOUR_MNEMONIC_HERE; - -const getLinkToCollection = (sdk: Sdk, collectionId: number) => { - return `${sdk.options.baseUrl}/collections?collectionId=${collectionId}`; -}; - -const getLinkToToken = (sdk: Sdk, collectionId: number, tokenId: number) => { - return `${sdk.options.baseUrl}/tokens?collectionId=${collectionId}&tokenId=${tokenId}`; -}; - -const createCollectionV2 = async (sdk: Sdk): Promise => { - const collectionCreationV2Result = await sdk.collection.createV2({ - address: PUT_YOUR_ADDRESS_HERE, - name: 'Winter Wonders: Husky Adventures', - description: 'NFT collection of huskies in winter sports and snowy landscapes, capturing their adventurous spirit and joyful energy.', - tokenPrefix: 'WWW', - symbol: "WWW", - cover_image: { - "url": "https://stage-ipfs.unique.network/ipfs/QmXQs4AEdREu4ecyuVPJaNRYZCavwj75CAkjszDn84cPqP" - }, - potential_attributes: [ - { - "trait_type": "Fur Color", - }, - { - "trait_type": "Eye Color", - }, - { - "trait_type": "Winter Sport", - "values": ["Skiing", "Snowboarding", "Sledding", "Ice Skating", "Snowshoeing"] - }, - { - "trait_type": "Accessory", - "values": ["Scarf", "Hat", "Goggles", "Boots", "Jacket"] - }, - { - "trait_type": "Background", - "values": ["Snowy Mountain", "Frozen Lake", "Snow-Covered Forest", "Cozy Cabin", "Snowfall"] - }, - { - "trait_type": "Rarity", - "values": ["Common", "Uncommon", "Rare", "Epic", "Legendary"] - } - ], - permissions: {nesting: {collectionAdmin: true}}, - limits: {ownerCanTransfer: true, ownerCanDestroy: true}, - }); - - if (!collectionCreationV2Result.parsed) { - throw collectionCreationV2Result.error; - } - const collectionId = collectionCreationV2Result.parsed.collectionId; - console.log(`Collection created, id ${collectionId}. ${getLinkToCollection(sdk, collectionId)}`); - return collectionId; -}; - -const mintTokensV2 = async (sdk: Sdk, collectionId: number, image: string) => { - const tokensMintingResult = await sdk.token.createMultipleV2({ - collectionId, - tokens: [ - { - owner: sdk.options.account?.address, - schemaName: "unique", - schemaVersion: "2.0.0", - image, - name: "Demo Hasky", - description: "Demo Hasky Description", - background_color: "#00BFFF", - image_details: { - name: "test image details", - type: "image", - }, - attributes: [ - { - "trait_type": "Fur Color", - "value": 'test string attribute 1' - }, - { - "trait_type": "Eye Color", - "value": 'test string attribute 2' - }, - { - "trait_type": "Winter Sport", - "value": "Snowboarding", - }, - { - "trait_type": "Accessory", - "value": "Boots", - }, - { - "trait_type": "Background", - "value": "Jacket", - }, - { - "trait_type": "Rarity", - "value": "Legendary", - } - ], - media: { - image_1: { - type: "image", - url: "https://stage-ipfs.unique.network/ipfs/QmPT3F16h5jPuELwBqHiApM7iWkQtJdSrok1Z4JDMhnze3", - }, - image_2: { - type: "image", - url: "https://stage-ipfs.unique.network/ipfs/QmScKMbkUBQWADSrZusc7SUPVTCtdadAaFQ1eb7nN5Ysff", - }, - video_1: { - type: "video", - url: "https://stage-ipfs.unique.network/ipfs/QmcDMcdhp23fRe6aAZKwkJVsHzvSh5i3EdK2LPFhJPZjgb", - }, - audio_1: { - type: "audio", - url: "https://stage-ipfs.unique.network/ipfs/QmVniYM43KfoNahTM8Mg9UStG7LmC5rVAHYBKj9WnDjuRX", - }, - }, - }, - ], - }); - - if (!tokensMintingResult.parsed) { - throw tokensMintingResult.error; - } - const tokenIds = tokensMintingResult.parsed.map(({ tokenId }) => tokenId); - - console.log( - `Tokens minted in collection ${collectionId}, ids ${tokenIds.join(", ")}` - ); - for (const tokenId of tokenIds) { - console.log(`${getLinkToToken(sdk, collectionId, tokenId)}`); - } - - return tokenIds; -}; - -const main = async () => { - // init substrate account and sdk - const mnemonic = SUBSTRATE_MNEMONIC - if (!mnemonic) throw new Error('SUBSTRATE_MNEMONIC env variable is not set') - const account = Sr25519Account.fromUri(mnemonic); - - const sdk = new Sdk({baseUrl: 'https://rest.unique.network/opal/v1', account}) - - try { - const collectionId = await createCollectionV2(sdk); - await mintTokensV2(sdk, collectionId, "https://stage-ipfs.unique.network/ipfs/QmPT3F16h5jPuELwBqHiApM7iWkQtJdSrok1Z4JDMhnze3"); - await mintTokensV2(sdk, collectionId, "https://stage-ipfs.unique.network/ipfs/QmbtRoNz7GMbSu4iBerp5zuW9W9pZZdAhXFPNxfmEWSUSy"); - } catch (e) { - console.log(e); - } -}; - -main() - .catch((error) => { - if (typeof error === 'object' && error !== null) { - if (error.isAxiosError === true) { - const url = error.response.request.res.responseUrl || error.config.url - console.log({...error.response.data, url}) - if (error.details) { - console.dir(error.details, {depth: 100}) - } - } else { - if (error.details) { - console.log(error.toString()) - console.dir(error.details, {depth: 100}) - } else { - console.error(error) - } - } - } else { - console.error(error) - } - }).finally(() => process.exit()); -``` diff --git a/docs/tutorials/destroyCollection.md b/docs/tutorials/destroyCollection.md deleted file mode 100644 index 3aad5e5..0000000 --- a/docs/tutorials/destroyCollection.md +++ /dev/null @@ -1,92 +0,0 @@ -# Destroy a collection - -In this guide, we will go through the entire process on how to destroy a collection using the Unique Network SDK. - -### Prerequisites - -To get started, you need to have a [Substrate address](../tutorials/accounts/create-account.md) and an existing collection (see [create a collection](../tutorials/create-collection-token.md). - -### Limitations - -There are some scenarios when it is impossible to destroy a collection: - -- a collection is not found. -- not enough balance to destroy a collection. -- a collection contains tokens. -- your address is not the collection owner. -- the corresponding permission is specified when the collection is created. - -### Sample code - -```typescript:no-line-numbers -import { DestroyCollectionArguments } from '@unique-nft/substrate-client/tokens/types'; - -const destroyArgs: DestroyCollectionArguments = { - address: '', - collectionId: '' -}; - -const result = await sdk.collection.destroy.submitWaitResult(destroyArgs); -const { success } = result.parsed; -``` - -### Examples - - - - -```typescript:no-line-numbers -import Sdk from "@unique-nft/sdk"; - -const sdk = new Sdk({ baseUrl: 'https://rest.unique.network/opal' }); - -sdk.collection.destroy.submitWaitResult({ - address: '', - collectionId: 1, -}); -``` - - - - -```typescript:no-line-numbers -import { DestroyCollectionArguments } from '@unique-nft/substrate-client/tokens/types'; - -const destroyArgs: DestroyCollectionArguments = { - address: '', - collectionId: '' -}; - -const result = await sdk.collection.destroy.submitWaitResult(destroyArgs); -const { success } = result.parsed; -``` - - - - -```bash:no-line-numbers -curl -X 'DELETE' \ - 'https://rest.unique.network/opal/collection' \ - -H 'accept: application/json' \ - -H 'Content-Type: application/json' \ - -d '{ - "address": "yGCyN3eydMkze4EPtz59Tn7obwbUbYNZCz48dp8FRdemTaLwm", - "collectionId": 1 -}' - -# then we sign, then we call - -curl -X 'POST' \ -'https://rest.unique.network/opal/extrinsic/submit' \ --H 'accept: application/json' \ --H 'Content-Type: application/json' \ --d '{ -"signerPayloadJSON": { *from previous response* }, -"signature": "0x_your_signature_in_hex" -}' -``` - - - - - diff --git a/docs/tutorials/easy-market.md b/docs/tutorials/easy-market.md deleted file mode 100644 index 3559f9e..0000000 --- a/docs/tutorials/easy-market.md +++ /dev/null @@ -1,382 +0,0 @@ -# How to create a marketplace from scratch - -## __What is this article about__ -Here we will show you all the most important *concepts* you need to know about when going to make your own marketplace. - -__```This is not a step by step guide on building a marketplace, but rather an explisit look at main specifics regarding BlockChain marketplaces.```__ - -Complete marketplace created by this guide can be found [here](https://github.com/UniqueNetwork/insert-your-marketplace-name) - -Below we will describe main operations that you will need in order to create a marketplace. Keep in mind that it's just a simplified concept to give you an idea of how to develop main marketplace operations using blockchain. - -- We will look into _account managment_ - simple look on how to interact with polkadotjs extension in order to receive payments and place NFTs to sale - -- Then we will look on _how to put NFT on sale_ - how to transfer token to marketplace wallet and validate that transfer - -- Finally we will learn _how to puchase_ previously placed NFT. How to provide bought NFT to buyer and give money to the seller. -### __Content__ -- [Managing accounts](#Accounts) -- [How to place NFT on sale](#Sale) -- [How to process NFT purchase](#Buy) - -__Different usecases and advanced implementations will be described in later chapters of this guide-series__ - -### __⚠️ Prerequisites:__ -
- Important parts of application that are neccessary for marketplace - -1. Main wallet that will accept money from buyers and hold selling tokens. Also known as `escrow` or `marketplaceWallet` - -2. DB of sorts to store data about tokens on sale and their prices. In our examples, we will use json file as our db for simplicity - -3. Frontend should know the address of `marketplace wallet` - the one that holds tokens. In our example we will receive it via back-end request on /settings, you can use environment variables - -4. We will use polkadot.js extension as the main source for users to subscribe to transactions and maintain accounts. - -5. `Marketplace wallet` should always have some funds on it to cover transaction fees at transfering tokens or nfts - -[Extension can be found here](https://polkadot.js.org/extension/) -
- - - -## __Manage accounts__ - -Before we begin we need a way to manage users accounts. User should be able to select an account that will be used for buying/selling. We also would like to present the balance of selected account. - -⚠️ `selectedAccount` in further parts of this guide will be the one received from extension - -
-Front-end: - -```ts -import { PolkadotAccount, PolkadotProvider } from '@unique-nft/accounts/polkadot'; - -// Get all accounts presented in polkadotjs extension -const getAccounts = async () => { - const provider = new PolkadotProvider({ accountType: ['sr25519']}); - await provider.init(); - // get accounts from polkadot's extension - const _accountsList = await provider.getAccounts() as PolkadotAccount[]; - if (_accountsList.length !== 0) { - const _accounts = await web3Accounts(); - return _accounts; - } - throw new Error('Polkadotjs extension is not installed'); -} - -const getBalance = async (account) => { - if (!account) return; - const address = account.getAddress(); - const { availableBalance } = await sdk.balance.get({ address }) - return availableBalance; -}; - -let accounts = await getAccounts(); -// ⚠️ User account used for interactions -let selectedAccount = accounts[0]; -``` -[More about accounts](../createAccount) -
- - - -## __Sell NFT__ - -Before some one can buy a token it should be placed on sale. Sale is represented as `offer` which is basically a token on `marketplace wallet` and a record in DB about the price of this token. - -### __1. Get user tokens.__ - -For that matter we will be using unique-scan API since there's no on-chain solution to get all user tokens. - -
-Example: - -```ts -// GQL request to get tokens -const GqlQuery = "query getTokens($limit: Int, $offset: Int, $where: TokenWhereParams = {}, $orderBy: TokenOrderByParams = {}) {\n" + - " tokens(where: $where, limit: $limit, offset: $offset, order_by: $orderBy) {\n" + - " data {\n" + - " collection_id\n" + - " collection_name\n" + - " owner\n" + - " owner_normalized\n" + - " image\n" + - " token_id\n" + - " token_prefix\n" + - " __typename\n" + - " }\n" + - " count\n" + - " timestamp\n" + - " __typename\n" + - " }\n" + - "}"; - -// address can be obtained as "selectedAccount.address" -const getTokens = async (address: string): Promise => { - // GQL params to specify which tokens we want to get from DB - const body = JSON.stringify({ - "operationName": "getTokens", - "variables": { - "where": { - "_and": [ - { - "_or": [ - { - "owner": { - "_eq": address - } - }, - { - "owner_normalized": { - "_eq": address - } - } - ] - }, - ] - } - }, - "query": GqlQuery - }); - - const response = await fetch(config.scanUrl || '', { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body, - }); - - const { data } = await response.json(); - return data.tokens.data; -}; - -``` -
- -### __2. Transfer token to marketplace wallet__ -Before we, as marketplace, can do anything with token - we need to obtain ownership over it. Which will be achieved by transfering token by user to our `marketplace wallet`. - -
-Example: - -```ts -const { hash } = await sdk.token.transfer.submitWatch({ - address: selectedAccount?.address, - collectionId, - tokenId, - from: selectedAccount?.address, - to: escrowAddress, - value: 1 -}, { - signer: selectedAccount -}); - -// Make sure to save "hash" for next step -``` -
- -### __3. Setup a price for selling NFT__ -Put NFT price to DB. It is unwise to keep pricing inside nft, so we will handle it offchain. - -⚠️ We will send `hash` obtained in previouse step to back-end, so we can validate transfer on server side before saving it to DB - -
-Front-end: - -```ts -// hash - from previouse step -// price - from UI -// selectedAccount - from extension -await fetch(`${config.marketApiUrl}/offers`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ hash, price, seller: selectedAccount.address }) -}); - -``` -
- -
-Back-end: - -```ts -import { hexToNumber } from "@polkadot/util"; -// validate transaction -const { parsed, blockHash } = await sdk.extrinsics.status({ hash: txHash }); - -if (!parsed) throw new Error('Extrinsic is not complited or doesn\'t exist'); - -const { args } = await sdk.extrinsics.get({ blockHashOrNumber: blockHash, extrinsicHash: txHash }); - -const [from, to, collectionIdEncoded, tokenIdEncoded, valueEncoded] = args || []; - -const collectionId = hexToNumber(collectionIdEncoded); -const tokenId = hexToNumber(tokenIdEncoded); -const value = hexToNumber(valueEncoded); - -if (from.substrate !== seller - || to.substrate !== getEscrowAddress() // marketplace wallet address - || value !== 1 - ) throw new Error('Extrinsic is not valid'); - -// save offer to DB -const collection = await sdk.collection.get({ collectionId }); -const token = await sdk.token.get({ collectionId, tokenId }); - -const { name, tokenPrefix } = collection; -const { image } = token; - -// in our example - this will save offer to file -await appendOffer({ collectionId, tokenId, seller, price, tokenDescription: { collectionName: name, prefix: tokenPrefix, imageUrl: image.fullUrl || '' } }); -``` -
- - - -## __Purchase NFT__ - -When purchasing NFT keep in mind that we need not only to send NFT to buyer, but also send money to seller. At this step you can also manage comissions if you want to. - -### __1. Send funds to marketplace wallet__ -As a purchase - we should give money to marketplace. It can be done simply by transfering funds to `marketplaceWallet` by buyer. - -
-Front-end: - -```ts -const payForToken = async (offerId, price) => { - if(!selectedAccount?.address) return; - - const { hash } = await sdk.balance.transfer.submitWatch({ - address: selectedAccount.address, - destination: marketplaceWallet, - amount: price - }, { - signer: selectedAccount - }); - return hash; // we will use it in next step to finilize purchase -} - -// Once again - we will use { hash } in next step -``` -
- -### __2. Call REST api to confirm purchase__ -Send transaction info to back-end for validation. Backend will verify that sufficient amount of funds was provided to confirm purchase. - -⚠️ We will send `hash` obtained in previouse step to back-end, so we can validate transfer on server side before saving it to DB - -
-Front-end: - -```ts -const offerId; // from token we are trying to buy -const selectedACcount; // from polkadojs widget -const hash; // from previouse step - -await fetch(`${config.marketApiUrl}/offers/buy`, { - method: 'POST', - headers: { - 'Content-Type': 'application/json' - }, - body: JSON.stringify({ - offerId, - buyer: selectedAccount.address, - txHash: hash - }) -}); -``` -
- -
-Back-end: - -Validate purchase request -```ts -import { hexToBn } from "@polkadot/util"; - -// get offer from DB -const offer = await getOffer(offerId); -// make sure offer does exist and not finished -if(!offer || offer.status !== 'active') throw new Error('Active offer not found'); - -const { collectionId, tokenId, price, seller } = offer; - -const { blockHash } = await sdk.extrinsics.status({ hash: txHash }); - -const { args } = await sdk.extrinsics.get({ blockHashOrNumber: blockHash, extrinsicHash: txHash }); - -const [destination, amountRaw] = args || []; - -const { decimals } = await sdk.chain.properties(); - -const amount = hexToBn(amountRaw); -// human price is "1 UNQ" but for chain it should be "1 000 000 000 000" (12 zeros - the amount of decimals) -const priceBN = (new BN(price)).mul((new BN(10)).pow(new BN(decimals))); - -// validate user transfer: it exist, it send money to crrect address and amount is higher than the price - -// Don't forget to account chain decimals when calculating transfered amounts -if (destination.id !== getEscrowAddress() - || !amount.eq(priceBN) -) throw new Error('Extrinsic is not valid'); -``` - -
- -### __3. Backend should send NFT to buyer__ -Now, when everything verified - we need to finish up the trade. First - give NFT to buyer. - -
-Back-end: - -```ts -await sdk.token.transfer.submit({ - address, // market wallet address - collectionId, - tokenId, - from: address, // market wallet address` - to: buyer, // buyer address` - value: 1 -}, { - signer: selectedAccount -}); -``` -
- -### __4. Backend should send funds to seller__ -Second - send money to buyer. - -💡At that step you can keep a portion of money as a market fee: - -Cut a part, for example 10%, of the summ that should be sent to seller - and keep it on marketplace wallet or send on your own wallet instead. - -
-Back-end: - -```ts -await sdk.balance.transfer.submit({ - address, - destination: seller, - amount: price -}, { - signer: selectedAccount -}); -``` -
- -### __5. Close/complete offer in DB__ -Don't forget to mark offer as completed in DB. -```ts -await setOfferStatus(offerId, 'closed'); -``` - -# Usefull links for further reading -1. [SDK](/sdk-docs) - see how to use all SDK methods and more -2. [EVM and smartcontracts](/evm-docs/Smart%20contracts.html) - checkout out how you can use ethereum smart contracts and an example of how we did it with our market -3. [Adresses](/about/addresses/) - general information about accounts -4. Whitelabel marketplace by UniqueNetwork [FE](https://github.com/UniqueNetwork/unique-marketplace-frontend)/[BE](https://github.com/UniqueNetwork/unique-marketplace-api) - our implementation for marketplace. Be warned - this one is very complex and utilizes custom smartcontract as well as built-in scan solution for TX validations. \ No newline at end of file diff --git a/docs/tutorials/examplesREST.md b/docs/tutorials/examplesREST.md deleted file mode 100644 index a9068cf..0000000 --- a/docs/tutorials/examplesREST.md +++ /dev/null @@ -1,207 +0,0 @@ -# REST API examples - -REST service calls are a language independent set of methods that facilitate communication between a client and a server. Unique Network provides such a REST service as a language-independent means to interact with the blockchain. - -To see this in action, let's create a Collection using pure REST service calls. The main tool that will be used to accomplish this will be *curl*. Out of convenience Node.js will be utilized to parse the JSON formatted server responses and extract necessary data from them. - -Important note: interacting with the blockchain requires user interaction in cases where signing of an extrinsic (a transaction) is necessary. Signing requires a client-side utility that can invoke SR25519 encryption methods needed to perform this in case of non-custodial solutions. For custodial solutions there are cases where the REST service can be invoked to perform this action. - -But before we dive into the code we'll need to create a Substrate account to use in this example. Since we will need to provide the *mnemonic seed* and the *wallet address* at some point remember to make a note of these two data items during the creation process. The instructions for creating an account with the Polkadot.js wallet browser extension can be found [here](/sdk-guides/createAccount). And, since some Opal tokens are required to pay for the transaction fees as well(around 2 to 2.5 OPL) these can be obtained via a [Telegram faucet bot](https://t.me/unique2faucet_opal_bot). - -## Pure REST (curl) example - -The lifecycle of an extrinsic is: - -1. request the REST service to build the extrinsic -2. sign the extrinsic -3. submit the signed extrinsic - -For this example a custodial solution will be considered as it provides the most straightforward approach. - -```bash -apiUrl="https://rest.unique.network/opal/v1" -mnemonic="bus ahead nation nice damp recall place dance guide media clap language" -address="5HNUuEAYMWEo4cuBW7tuL9mLHR9zSA8H7SdNKsNnYRB9M5TX" - -# Build extrinsic -buildResult=`curl -s -X 'POST' \ - "${apiUrl}/collections?use=Build" \ - -H "accept: application/json" \ - -H "Content-Type: application/json" \ - -d '{ - "name": "Test collection", - "description": "My test collection", - "tokenPrefix": "TST", - "address": "'${address}'" -}'` -signerPayloadJSON=`node -e "console.log(JSON.stringify(JSON.parse('$buildResult').signerPayloadJSON))"` -signerPayloadHex=`node -e "console.log(JSON.stringify(JSON.parse('$buildResult').signerPayloadHex))"` - - -# Sign extrinsic -signResult=`curl -s -X 'POST' \ - "${apiUrl}/collections?use=Sign" \ - -H "accept: application/json" \ - -H "Authorization: Seed ${mnemonic}" \ - -H "Content-Type: application/json" \ - -d '{ - "signerPayloadJSON": '${signerPayloadJSON}', - "signerPayloadHex": '${signerPayloadHex}' - }'` -signature=`node -e "console.log(JSON.parse('$signResult').signature)"` - - -# Submit extrinsic -submitResult=`curl -s -X 'POST' \ - "${apiUrl}/collections?use=SubmitWatch" \ - -H "accept: application/json" \ - -H "Content-Type: application/json" \ - -d '{ - "signerPayloadJSON": '${signerPayloadJSON}', - "signature": "'${signature}'" - }'` -hash=`node -e "console.log(JSON.parse('$submitResult').hash)"` - -# Check status extrinsic by hash -echo "status: $apiUrl/extrinsic/status?hash=$hash" -``` - -At this point the transaction is submitted for processing and if needed a periodic status request can be looped in a cycle. - -## Programming language example - -It would be tempting to show an example of how easy it is to apply an Unique JS/TS SDK to accomplish this (this also stands in the case of future SDKs for languages other than JS/TS), but a much more compelling case for a programming language example would be to illustrate how to use the REST service in a 'pure' language case with no SDK support. - -A fitting language for this is Javascript as it is a very widely adopted language and will provide a good basis for understanding the example code as it provides the structure for implementing this approach in any other language. - -As was mentioned earlier, a non-custodial approach will require an SR25519 signer procedure to sign an extrinsic. - -To illustrate this functionality at the core level we will avoid using the high-level `@polkadot/api` library and opt for the low-level `@polkadot/util-crypto` one instead to perform the signing. It is, in contrast to the api, rudimentary and contains just a set of atomic functions which will be invoked to sign a binary data chunk (treating it as a black box, ignoring its contents). - -Packages used in this example: -- [@polkadot/node-fetch](https://www.npmjs.com/package/@polkadot/node-fetch) -- [@polkadot/util](https://www.npmjs.com/package/@polkadot/util) -- [@polkadot/util-crypto](https://www.npmjs.com/package/@polkadot/util-crypto) - -```ts -import fetch from 'node-fetch'; -import { - u8aToHex -} from '@polkadot/util'; -import { - sr25519Sign, - mnemonicToMiniSecret, - sr25519PairFromSeed, -} from '@polkadot/util-crypto'; - -const mnemonic = 'bus ahead nation nice damp recall place dance guide media clap language'; -const address = '5HNUuEAYMWEo4cuBW7tuL9mLHR9zSA8H7SdNKsNnYRB9M5TX'; -const restUrl = 'https://rest.opal.uniquenetwork.dev/v1'; - - -//////////////////////////////////////////////////////////////////////////// -// Call a request in rest-api -//////////////////////////////////////////////////////////////////////////// - -function callApi(path, payload) { - const url = `${restUrl}/${path}`; - return fetch(url, { - method: 'POST', - headers: { - 'Content-type': 'application/json', - }, - body: JSON.stringify(payload) - }).then(res => res.json()); -} - -//////////////////////////////////////////////////////////////////////////// -// Build extrinsic -//////////////////////////////////////////////////////////////////////////// -async function buildExtrinsic() { - const payload = { - address, - mode: "Nft", - name: "Sample collection name", - description: "sample collection description", - tokenPrefix: "TEST" - }; - - const { - signerPayloadJSON, - signerPayloadHex, - error - } = await callApi(`collections?use=Build`, payload); - - if (error) { - console.error('build extrinsic error', error); - process.exit(-1); - } - - return { - signerPayloadJSON, - signerPayloadHex - } -} - - -//////////////////////////////////////////////////////////////////////////// -// Signing extrinsic -//////////////////////////////////////////////////////////////////////////// - -const TYPE_PREFIX = { - ecdsa: '02', - ed25519: '00', - ethereum: '02', - sr25519: '01' -}; -function signExtrinsic(signerPayloadHex) { - const seed = mnemonicToMiniSecret(mnemonic); - - const { - publicKey, - secretKey, - } = sr25519PairFromSeed(seed); - - const signatureBytes = sr25519Sign(signerPayloadHex, { - publicKey, - secretKey, - }); - const signature = u8aToHex(signatureBytes); - - return `0x${TYPE_PREFIX.sr25519}${signature.substring(2)}`; -} - - -//////////////////////////////////////////////////////////////////////////// -// Submit extrinsic -//////////////////////////////////////////////////////////////////////////// -async function submitExtrinsic(signerPayloadJSON, signature) { - const payload = { - signerPayloadJSON, - signature - }; - const { hash, error } = await callApi(`collections?use=SubmitWatch`, payload); - - if (error) { - console.log('submit extrinsic error', error); - process.exit(-1); - } - - return hash; -} - -//////////////////////////////////////////// -// entrypoint -//////////////////////////////////////////// -async function main() { - const { signerPayloadJSON, signerPayloadHex } = await buildExtrinsic(); - - const signature = signExtrinsic(signerPayloadHex); - - const hash = await submitExtrinsic(signerPayloadJSON, signature); - - console.log('get status url', `${restUrl}/extrinsic/status?hash=${hash}`); -} - -main(); -``` \ No newline at end of file diff --git a/docs/tutorials/getAccountAndBalance.md b/docs/tutorials/getAccountAndBalance.md deleted file mode 100644 index 82b7c61..0000000 --- a/docs/tutorials/getAccountAndBalance.md +++ /dev/null @@ -1,47 +0,0 @@ -# Get and display accounts and balances - -### Get accounts - -You can use the [Accounts](https://www.npmjs.com/package/@unique-nft/accounts) package to work with accounts easily. - -The package allows connecting with different accounts. To get an accounts list, you need to create an instance of the `Accounts` class and connect the necessary providers to it: - -```typescript:no-line-numbers -import { Accounts } from '@unique-nft/accounts'; -import { KeyringLocalProvider } from '@unique-nft/accounts/keyring-local'; -import { PolkadotProvider } from '@unique-nft/accounts/polkadot'; - -const accounts = new Accounts(); -await accounts.addProvider(KeyringLocalProvider); -await accounts.addProvider(PolkadotProvider); - -const accountsList = await accounts.getAccounts(); - -``` -This will give us a list of available accounts. You can read a little more about accounts in the Polkadot docs - [Keyring](https://polkadot.js.org/docs/ui-keyring) and [Extension](https://polkadot.js.org/docs/extension). - - -### Get balances - -To get the balance of available accounts, we need to use [SDK](https://www.npmjs.com/package/@unique-nft/sdk). All we need to do is to pass the account address, whose balance we want to know, as an argument. - -```typescript:no-line-numbers -import Sdk, { Options } from '@unique-nft/sdk'; - -const options: Options = { - baseUrl: '' -}; -const sdk = new Sdk(options); - -const { address, availableBalance, lockedBalance, freeBalance } = sdk.balance.get({ address }); -``` - -As a result, we get the following data: - -`address` - current address. - -`availableBalance` - transferable balance. - -`lockedBalance` - locked balance. - -`freeBalance` - full balance. \ No newline at end of file diff --git a/docs/tutorials/getNFTsAndTokens.md b/docs/tutorials/getNFTsAndTokens.md deleted file mode 100644 index ab59163..0000000 --- a/docs/tutorials/getNFTsAndTokens.md +++ /dev/null @@ -1,294 +0,0 @@ -# How to get and display NFTs and collections - -To getting collections or NFTs use Apollo client for react. The Apollo Client API reference can be found at:
-[https://www.apollographql.com/docs/react/api/apollo-client/](https://www.apollographql.com/docs/react/api/apollo-client/) - -First, create two files: - -
_types.ts_ - -```typescript -// QueryResponse interface -export interface QueryResponse { - [key: string]: { - data?: T[]; - }; -} - -// Collection's full interface -export interface Collection { - collection_id: number; // Id of collection - collection_name: string; // Collection name - collection_cover: string; // Cover image - date_of_creation: number; // Creation date - description: string; // Description text - owner: string; // Owner's address - owner_mormalized: string; // Same as owner's address - owner_can_destroy: boolean; // Shows if collection can be burned - token_prefix: string; // Token prefix (up to 4 characters) - tokens_count: number; // Number of tokens in the each collection - token_limit: number; // Number of tokens limit - name: string; // Collection name - sponsorship: string; // Sponsor's address -} - -export interface Token { - token_id: number; // Token id - token_name: string; // Token name - token_prefix: number; // Token prefix (up to 4 characters) - owner: string; // Owner's address - owner_mormalized: string; // Same as owner's address - attributes: Record; // Token attributes - date_of_creation: number; // Creation date - // Token image - image?: { - fullUrl: string | null; - ipfsCid: string | null; - }; - - collection_id: number; // Id of the collection containing the token - collection_name: string; // The name of the collection containing the token - collection_cover: string; // Cover image of the collection containing the token - collection_description: string; // Description of the collection containing the token -} -``` -
- -and - -
_utils.ts_ - -```typescript -// Utilite for getting image path - -/* Data image cover different formats: - * 1) 'QmbuyQebXVQcZbaGmP4maWUqRiKYeAAyYZEiqL3rnev8i4' - * 2) https://www.ipfs-server/QmbuyQebXVQcZbaGmP4maWUqRiKYeAAyYZEiqL3rnev8i4 - * 3) "{\"ipfs\":\"QmZCuWx72x1ukhehLsg1qNjKhVj3d1feJjadUPJbyYfmpY\",\"type\":\"image\"}" - * */ -export const formatCoverSrc = ( - imagePath: string | null | undefined, -): string | undefined => { - if (!imagePath) { - return undefined; - } - - const buildPath = (url: string) => { - if (isValidHttpUrl(url)) { - return url; - } - return `https://ipfs.uniquenetwork.dev/ipfs/${url}`; - }; - - try { - const deserializedImagePath: unknown = JSON.parse(imagePath); - - if (IPFSGateway && isImagePath(deserializedImagePath) && deserializedImagePath.ipfs) { - return buildPath(deserializedImagePath.ipfs); - } - } catch { - return buildPath(imagePath); - } - - return undefined; -}; -``` -
- -## Get NFTs - -Create _useGqlTokens.ts_: - -```typescript -import { gql, useQuery } from '@apollo/client'; -import { QueryResponse, Token } from 'types'; - -// gql request -const OWNER_TOKENS_QUERY = gql` - query owner_tokens_query( - $where: TokenWhereParams - ) { - tokens( - where: $where - order_by: { token_id: "asc" } - ) { - - # You can add keys from Token interface above - data { - token_id - token_name - collection_name - collection_id - image - } - } - } -`; - -// hook -export const useGqlTokens = ( - ownerAddress: string | undefined -) => { - const { - data: response, - error, - } = useQuery>(OWNER_TOKENS_QUERY, { - skip: !ownerAddress, - fetchPolicy: 'network-only', - nextFetchPolicy: 'cache-first', - notifyOnNetworkStatusChange: true, - variables: { - where: { - _or: [ - { owner: { _eq: ownerAddress } }, - { owner_normalized: { _eq: ownerAddress } }, - ] - }, - }, - }); - - return { - tokens: response?.tokens.data || [], - error, - }; -}; -``` - -## Display NFTs - -Create _tokens.ts_: - -```typescript jsx -import { formatCoverSrc } from 'utils'; -import { useGqlTokens } from './hooks/useGqlCollections'; -import { Token } from 'types'; - -// NFTs list component -export const TokensList = ({ ownerAddress } : { - ownerAddress: string | undefined -}) => { - const { tokens } = useGqlTokens('5HNUuEAYMWEo4cuBW7tuL9mLHR9zSA8H7SdNKsNnYRB9M5TX'); - - if (!tokens) { - return

No data to display

; - } - - return ( - <> -

List of NFTs

-
    - {tokens.map((item: Token) => ( -
  • -

    {item.token_name} [{item.token_id}]

    - {item.token_name} -

    - Collection: {item.collection_name} [{item.collection_id}] -

    -
  • - ))} -
- - ); -}; -``` - -## Get collections - -Create file _hooks/useGqlCollections.ts_: - -```typescript -import { gql, useQuery } from '@apollo/client'; -import { Collection, QueryResponse } from 'types'; - -// gql request -const COLLECTIONS_BY_ACCOUNT_QUERY = gql` - query collections_by_account_query( - $where: CollectionWhereParams - ) { - collections(order_by: { collection_id: "asc" }, where: $where) { - - # You can add keys from Collection interface above - data { - name - description - collection_cover - collection_id - owner_normalized - tokens_count - } - } - } -`; - -// hook -export const useGqlCollections = ( - accountAddress: string | undefined -) => { - const { - data: response, - error, - } = useQuery>(COLLECTIONS_BY_ACCOUNT_QUERY, { - skip: !accountAddress, - fetchPolicy: 'network-only', - nextFetchPolicy: 'network-only', - notifyOnNetworkStatusChange: true, - variables: { - where: { - _or: [ - { owner: { _eq: accountAddress } }, - { owner_normalized: { _eq: accountAddress } }, - ], - }, - }, - }); - - return { - collections: response?.collections.data ?? [], - error, - }; -}; -``` - -## Display collections - -Create _collections.tsx_: - -```typescript jsx -import { formatCoverSrc } from 'utils'; -import { useGqlCollections } from './hooks/useGqlCollections'; -import { Collection } from 'types'; - -// Create some functional component for displaying collections list: -export const CollectionsList = () => { - const { collections } = useGqlCollections('5HNUuEAYMWEo4cuBW7tuL9mLHR9zSA8H7SdNKsNnYRB9M5TX'); - - if (!collections) { - return

Nothing to show

; - } - - return ( - <> -

List of collections

-
    - {collections.map((item: Collection) => ( -
  • -

    {item.name} [{item.collection_id}]

    - {item.name} -

    {item.description}

    -

    - Owner: {item.owner_normalized}
    - Tokens count: {item.tokens_count} -

    -
  • - ))} -
- - ); -} -``` diff --git a/docs/tutorials/graph-node-docker.md b/docs/tutorials/graph-node-docker.md deleted file mode 100644 index 6e67e87..0000000 --- a/docs/tutorials/graph-node-docker.md +++ /dev/null @@ -1,101 +0,0 @@ -# The Graph - -## Intro - -[The Graph](https://thegraph.com/) is a protocol for building decentralized applications (dApps) quickly -on Ethereum and IPFS using GraphQL. - -Graph Node is an open source Rust implementation that event sources the Ethereum blockchain to deterministically -update a data store that can be queried via the GraphQL endpoint. - -For detailed instructions and more context, check out the [Getting Started Guide](docs/getting-started.md). - -Since Unique Network provides the Ethereum API to work with networks, this approach can also be used. - -## Launch a local graph-node - -To start, please clone the [https://github.com/graphprotocol/graph-node](https://github.com/graphprotocol/graph-node) -repository using Git. - -```bash:no-line-numbers -git clone https://github.com/graphprotocol/graph-node.git - -cd graph-node/docker -``` - -Then, open the Dockerfile and modify the following variable: - -```docker:no-line-numbers -environment.ethereum: 'mainnet:https://rpc-opal.unique.network' -``` - -When the new variable is saved, please start the container by this command: - -```bash:no-line-numbers -docker compose up -``` - -## Generate subgraph locally using the ABI file - -Let's install the Graph CLI. To do this, please run the command below. - - - - -```bash:no-line-numbers -npm install -g @graphprotocol/graph-cli -``` - - - - -```bash:no-line-numbers -yarn global add @graphprotocol/graph-cli -``` - - - - -After this, run this command to initialize the Graph, and also specify the smart contract address and the ABI file -(for more details, please check the [Graph docs](https://thegraph.com/docs/en/developing/creating-a-subgraph/#from-an-existing-contract)). - -```bash:no-line-numbers -graph init --from-contract contractAddress --abi ./abi.json -``` - -After executing this command, the subgraph is generated using the specified contract and the ABI file. -Here is [the example](https://github.com/AzgatG/test_opal). - -Now, we need to open the `subgraph.yaml` file and set the following: `dataSources.source.startBlock`. This is the start block for -smart contract scanning. - -When it is done, please execute these commands to deploy your subgraph to the local graph-node. - - - - -```bash:no-line-numbers -npm run create-local -npm run deploy-local -``` - - - - -```bash:no-line-numbers -yarn run create-local -yarn run deploy-local -``` - - - - -## Examples - -Here are the examples how the main items look: - -Smart contract address - `0xED83A4E878E496ec1C84BdBbbC50c6657181F906` - -The ABI file - `./abi.json` - -Subgraph for indexing all blocks data - [https://thegraph.com/hosted-service/subgraph/kybernetwork/ethereum-blocks](https://thegraph.com/hosted-service/subgraph/kybernetwork/ethereum-blocks) \ No newline at end of file diff --git a/docs/tutorials/how-to-get-collections-tokens.md b/docs/tutorials/how-to-get-collections-tokens.md deleted file mode 100644 index 178f658..0000000 --- a/docs/tutorials/how-to-get-collections-tokens.md +++ /dev/null @@ -1,108 +0,0 @@ -# ScanAPI. How to get collections and tokens. - -You can get some data about collections and tokens using our [ScanAPI](#todo). - -Let's say you want to get the list of collections of your own. You just need to write correct GraphQL query and connect to correct blockchain ScanAPI [endpoint](#todo). - -Or you can query data in your browser using [GraphQL console](https://scan-api.opal.uniquenetwork.dev/v1/graphql/). - -## Get collections that you own - -Let's say your address is `5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr`. - -To get your collections, you should do query like this: - -```graphql:no-line-numbers -query { - collections (where: { - { - owner: { - _eq: "5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr" - } - } - }) { - data { - collection_id - name - token_prefix - } - } -} -``` - -## Get all your collections and all collections with tokens that you own - -If you want to extend the list of your own collections by collections that contain tokens that you own, you should add corresponding condition into _where_ part of your GraphQL query: - -```graphql:no-line-numbers -query { - collections (where: { - _or: [ - # Collection owner filter - { - owner: { - _eq: "5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr" - } - }, - # Collection tokens by owner filter - { - tokens: { - owner: { - _eq: "5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr" - } - } - } - ] - }) { - data { - collection_id - name - token_prefix - } - } -} -``` - -## Get all my tokens within a specific collection - -If you want to check all (address is still `5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr`) tokens within a specific collection, you can specify a collection ID (**1336**) like this: - -```graphql:no-line-numbers -query { - tokens (where: { - collection_id: { _eq: 1336 }, - owner: { _eq: "5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr" } - }) { - count - data { - token_id - token_name - token_prefix - } - } -} -``` - -## Get all my tokens (with pagination) - -If you want to get the list of all NFT tokens of your own regardless of the collection they belong to, you should also consider using pagination. This is, because the list could be potentially huge. To use the pagination, just remove the _collection\_id_ condition from previous query, and add the _limit_ and _offset_ arguments. - -```graphql:no-line-numbers -query { - tokens ( - limit: 20 - offset: 0 - where: { - owner: { _eq: "5Gzff3r5hs59WPcCK1cvdDtobYb2bf5sw9pAmVbaRicfF5Qr" } - } - ) { - count - data { - token_id - token_name - token_prefix - } - } -} - -``` \ No newline at end of file diff --git a/docs/tutorials/how-to-use-nesting-and-bundling.md b/docs/tutorials/how-to-use-nesting-and-bundling.md deleted file mode 100644 index 22c227f..0000000 --- a/docs/tutorials/how-to-use-nesting-and-bundling.md +++ /dev/null @@ -1,255 +0,0 @@ -# How to use nesting and bundling - -The **nesting** feature allows you to create **nested tokens**. Thus, the topmost token will have a wallet address as its owner, while the owner of tokens nested in it will be the token above it. -The entire chain of nested tokens will be called a **bundle**. If you transfer a top-level token to another owner, all tokens in the bundle will go with it. - -In this guide, we will go through the entire process of creating nested token using the Unique Network SDK. - -To create nested tokens and collections, we need install the following packages: - -1) [@unique-nft/sdk](https://www.npmjs.com/package/@unique-nft/sdk) -2) [@unique-nft/accounts](https://www.npmjs.com/package/@unique-nft/accounts) - - -_**Steps**_ -- [Prepare](#prepare) -- [CreateSdk](#create-Sdk) -- [Create new collection](#creating-a-collection) -- [Create new tokens](#token-creation) -- [Nesting function](#creating-a-nested-token) -- [Example](#example-execute-function) -- [IsBundle, getBundle](#isBundle-and-getBundle) -- [Unnest and others](#unnest) - - -## Prepare - -Install the above dependencies in the project - -Create js or ts file and write the following inside: - -```typescript -// Required imports -import Sdk from '@unique-nft/sdk'; -import { KeyringProvider } from '@unique-nft/accounts/keyring'; - -// Test mnemonic -const mnemonic = - 'bus ahead nation nice damp recall place dance guide media clap language'; - -// BaseUrl for test -const baseUrl = 'https://rest.unique.network/opal/v1'; -``` - -## Create Sdk - -```typescript -function createSdk(account: any) { - const options = { - baseUrl, - signer: account, - }; - return new Sdk(options); -} -``` - -## Creating a collection - -:warning: You need to set the permission field correctly in the collection. Otherwise, if the collection is not allowed to be nested, you won't be able to create a bundle of nested tokens in it. - -```typescript -export async function createCollection(sdk, address) { - const { parsed, error } = await sdk.collection.create.submitWaitResult({ - address, - name: 'Test collection', - description: 'My test collection', - tokenPrefix: 'TST', - permissions: { - nesting: { - tokenOwner: true, - collectionAdmin: true, - }, - }, - }); - - if (error) { - console.log('create collection error', error); - process.exit(); - } - - const { collectionId } = parsed; - - return sdk.collection.get({ collectionId }); -} -``` - -## Token creation - -```typescript -export async function createToken(sdk, address, collectionId) { - const { parsed, error } = await sdk.token.create.submitWaitResult({ - address, - collectionId, - }); - - if (error) { - console.log('create token error', error); - process.exit(); - } - - const { tokenId } = parsed; - - return sdk.token.get({ collectionId, tokenId }); -} -``` - -## Creating a nested token - -```typescript -export async function createNestedToken(sdk, nestedArgs) { - const { address, parent, nested } = nestedArgs; - const { parsed, error } = await sdk.token.nest.submitWaitResult({ - address, - parent, - nested, - }); - - if (error) { - console.log('create token error', error); - process.exit(); - } - - const { collectionId, tokenId } = parsed; - - console.log(`Token ${tokenId} from collection ${collectionId} successfully nested`); - - return sdk.token.get({ collectionId, tokenId }); -} -``` - -## Example execute function - -Now let's wrap all our steps in an asynchronous function and run it, watching the result in the console - -```typescript -async function main() { - - // get signer - const signer = await KeyringProvider.fromMnemonic(mnemonic); - const address = signer.instance.address; - - // initialize sdk - const sdk = createSdk(signer); - - // create a collection - const collection = await createCollection(sdk, address); - console.log('collection', collection); - - // token creating - const token = await createToken(sdk, address, collection.id); - console.log('token', token); - const token2 = await createToken(sdk, address, collection.id); - console.log('token2', token2); - - // nesting (put tokenId 2 in tokenId 1) - const nestedToken = await createNestedToken(sdk, { - address, - parent: { - collectionId: collection.id, - tokenId: token.tokenId, - }, - nested: { - collectionId: collection.id, - tokenId: token2.tokenId, - }, - }); - - console.log('nestedToken', nestedToken); -} - -main(); -``` - -## IsBundle and getBundle - -Let's look at some more functions provided by our library: IsBundle and getBundle. Add the following code inside the `main` function - -```typescript - const isBundle = await sdk.token.isBundle({ - collectionId: collection.id, - tokenId: 1, - }); - - const isBundle2 = await sdk.token.isBundle({ - collectionId: collection.id, - tokenId: 2, - }); - - console.log('token 1 isBundle?', isBundle); - console.log('token 2 isBundle?', isBundle2); - - const result: any = await sdk.token.getBundle({ - collectionId: collection.id, - tokenId: 2, - }); - - console.log('getBundle', result); -``` -Running the whole process, you will see that both the top-level and the nested token are part of the bundle. - -## Unnest - -The reverse process is called `unnest`, when a token no longer belongs to any token and its owner becomes the wallet address. - -Add to your js file unnesting function - -```typescript -export async function createUnNestedToken(sdk: any, nestedArgs: any) { - const { address, parent, nested } = nestedArgs; - const { parsed, error } = await sdk.token.unnest.submitWaitResult({ - address, - parent, - nested, - }); - - if (error) { - console.log('create token error', error); - process.exit(); - } - - const { collectionId, tokenId } = parsed; - - console.log(`Token ${tokenId} from collection ${collectionId} successfully unnested`); - - return sdk.token.get({ collectionId, tokenId }); -} -``` - -and add its call to our function`main` - -```typescript -const unNestedToken = await createUnNestedToken(sdk, { - address, - parent: { - collectionId: collection.id, - tokenId: token.tokenId, - }, - nested: { - collectionId: collection.id, - tokenId: token2.tokenId, - }, - }); - - console.log('unNestedToken', unNestedToken); - - const isBundleAfterUnnest = await sdk.token.isBundle({ - collectionId: collection.id, - tokenId: 2, - }); - - console.log('token 2 isBundle?', isBundleAfterUnnest); - ``` - after unnesting you will see that the token with tokenId=2 is no longer part of the bundle. Neither is the token with tokenId=1 - because it no longer has any attached tokens. - - -Read more about this and other nesting functions [**here**](../sdk/methods.md) diff --git a/docs/tutorials/live-nft-example.md b/docs/tutorials/live-nft-example.md deleted file mode 100644 index 481bd2b..0000000 --- a/docs/tutorials/live-nft-example.md +++ /dev/null @@ -1,171 +0,0 @@ -# Live NFT example - -This examples shows how to create a collection where token properties can be changed, -mint a token and then change it. - -```ts -import Sdk from '@unique-nft/sdk' -import {KeyringProvider} from '@unique-nft/accounts/keyring' -import {AttributeType, COLLECTION_SCHEMA_NAME, SchemaTools, UniqueCollectionSchemaToCreate} from '@unique-nft/schemas' - -const SDK_BASE_URL = 'https://rest.unique.network/opal/v1' -const MNEMONIC = 'electic suit...' - -const sdk = new Sdk({baseUrl: SDK_BASE_URL}) - -const main = async () => { - const account = await KeyringProvider.fromMnemonic(MNEMONIC) - - //////////////////////////////////////// - // Creating collection in Unique schema - // Part 1: creating the schema - //////////////////////////////////////// - - const collectionSchema: UniqueCollectionSchemaToCreate = { - schemaName: COLLECTION_SCHEMA_NAME.unique, - schemaVersion: '1.0.0', - image: {urlTemplate: 'https://ipfs.unique.network/ipfs/QmcAcH4F9HYQtpqKHxBFwGvkfKb8qckXj2YWUrcc8yd24G/image{infix}.png'}, - coverPicture: {urlInfix: '1'}, - // important: - // attributes are virtual thing which exists only in the Unique schema - // it can be encoded and decoded and read only with collection schema encoded in Unique format - // it's like traits in cryptopunks - // actually every attribute is stored as token's property - // and token property is a real on-chain record with access controlled by blockchain - // so, the attribute - is just a value stored in property - attributesSchemaVersion: '1.0.0', - attributesSchema: { - 0: { - name: {_: "colour"}, - type: AttributeType.string, - optional: false, - isArray: false, - enumValues: { - 0: {_: 'red'}, - 1: {_: 'green'}, - 2: {_: 'blue'}, - } - }, - 1: { - name: {_: 'this attr is some free form string'}, - type: AttributeType.string, - optional: true, - isArray: false, - } - } - } - - //////////////////////////////////////// - // Creating collection in Unique schema - // Part 2: creating the collection - //////////////////////////////////////// - - const collectionResult = await sdk.collection.create.submitWaitResult({ - address: account.getAddress(), - name: 'Test collection', - description: 'Test collection description', - tokenPrefix: "TEST", - schema: collectionSchema as any, // temporary typing mismatch - tokenPropertyPermissions: [ - // we need manually set token property permissions - // because by default it's set with mutable: false flag - { - key: 'a.0', // 'a.0' corresponds to the attribute from 'schema.attributesSchema.0' - permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }, - }, - { - key: 'a.1', // 'a.0' corresponds to the attribute from 'schema.attributesSchema.1' - permission: { - tokenOwner: true, - collectionAdmin: true, - mutable: true, - }, - }, - ] - }, { - signer: account - }) - const {collectionId} = collectionResult.parsed! - console.log(collectionId) - - //////////////////////////////////////// - // Minting a token - //////////////////////////////////////// - - const tokenProperties = SchemaTools.encodeUnique.token({ - image: { - urlInfix: '1', - }, - encodedAttributes: { - 0: 0, // red by default - 1: {_: 'test value'}, - } - }, collectionSchema) - - const tokenMintResult = await sdk.token.create.submitWaitResult({ - address: account.getAddress(), - collectionId, - properties: tokenProperties, - }, { - signer: account - }) - - console.log(tokenMintResult) - const tokenId = tokenMintResult.parsed!.tokenId - - //////////////////////////////////////// - // Getting the token from the chain - // the colour attr value should be 'red' - //////////////////////////////////////// - - const token = await sdk.token.get({ - collectionId, - tokenId, - }) - console.dir(token, {depth: 100}) - - const color = token.attributes[0].value._ - if (color !== 'red') { - throw new Error(`token color should be red, got ${color}`) - } - - //////////////////////////////////////// - // Changing the token property - //////////////////////////////////////// - - const tokenChangeResult = await sdk.token.setProperties.submitWaitResult({ - address: account.getAddress(), - collectionId, - tokenId, - properties: [{ - key: 'a.0', - value: '2', - }] - }, { - signer: account - }) - - ///////////////////////////////////////// - // Getting the token from the chain - // the colour attr value should be 'blue' - ///////////////////////////////////////// - - const tokenAfterChange = await sdk.token.get({ - collectionId, - tokenId, - }) - console.dir(tokenAfterChange, {depth: 100}) - - const colorAfterChange = tokenAfterChange.attributes[0].value._ - if (colorAfterChange !== 'blue') { - throw new Error(`token color should be blue, got ${colorAfterChange}`) - } - -} - -main().catch(err => console.error(err)) -``` \ No newline at end of file diff --git a/docs/tutorials/nfts-fetching.md b/docs/tutorials/nfts-fetching.md deleted file mode 100644 index 75b29cf..0000000 --- a/docs/tutorials/nfts-fetching.md +++ /dev/null @@ -1,212 +0,0 @@ -# Fetching NFT - -This article demonstrates the basics of fetching NFT data using GraphQL. We are going to share some code examples. We hope this could become a starting point for creating your own queries with different filters and sortings. This might be useful in the following cases: - -1) Interacting with pagination and infinite scrolling; -2) Making different filters, including dynamic conditions; -3) Requesting limited and distinct data; -4) ...and in other things, for which necessary to obtain and handle arrays of data. - -### Basic example - -In this example, only main NFTs fields like id, prefix, and name are included in a request. - -```graphql:no-line-numbers -query tokens_query { - tokens { - data { - token_id - token_name - token_prefix - } - count - } -} -``` - -The request returns all existing tokens, but probably you'd like to make some filtering because handling big arrays of data might be expensive for both back-end and front-end parts of your application. -For this reason, we will define a limit value in our query: - -```graphql:no-line-numbers -query tokens_query { - tokens(limit: 10) { - data { - token_id - token_name - token_prefix - } - count - } -} -``` - -From a response, you can get NFT data and total number (count) of the tokens: - -```json:no-line-numbers -{ - "data": { - "tokens": { - "data": [ - { - "token_id": 1, - "token_name": "FPBase #1", - "token_prefix": "FPBase" - }, - ... - { - "token_id": 10, - "token_name": "FPBase #10", - "token_prefix": "FPBase" - } - ], - "count": 7286 - } - } -} -``` - -### Pagination - -Let's make our query a little bit more complex by adding offset and limit parameters. It might be your first step to implement pagination and requests that depend on external arguments. - -Arguments: - -```json:no-line-numbers -{ - "offset": 100, - "limit": 20 -} -``` - -Request: - -```graphql:no-line-numbers -query tokens_query($limit: Int, $offset: Int) { - tokens(limit: $limit, offset: $offset) { - data { - token_id - token_name - token_prefix - } - count - } -} -``` - -### DistinctOn and OderBy statements - -If you'd like to get a sorted list or different data (e.g. without duplicates), you can use the `distinct_on` or `order_by` statements. - -Arguments: - -```json:no-line-numbers -{ - "offset": 0, - "limit": 20, - "distinct_on": "collection_id", - "order_by_creation_data": {"date_of_creation": "desc"} -} -``` - -Request: - -```graphql:no-line-numbers -query tokens_query( - $limit: Int - $offset: Int - $distinct_on: TokenEnum - $order_by_creation_data: TokenOrderByParams -) { - tokens( - limit: $limit - offset: $offset - distinct_on: $distinct_on - order_by: $order_by_creation_data - ) { - data { - token_id - token_name - token_prefix - collection_id - date_of_creation - } - count - } -} -``` - -### Search and filter - -In this section, you will learn how to search and filter NFTs. Below request selects NFTs that are filtered by a collection name. - -Arguments: - -```json:no-line-numbers -{ - "offset": 0, - "limit": 20, - "distinct_on": "collection_id", - "order_by_creation_data": {"date_of_creation": "desc"}, - "where": {"collection_name": {"_eq": "chelobrick"}} -} -``` - -Request: - -```graphql:no-line-numbers -query tokens_query( - $limit: Int - $offset: Int - $distinct_on: TokenEnum - $order_by_creation_data: TokenOrderByParams - $where: TokenWhereParams -) { - tokens( - limit: $limit - offset: $offset - distinct_on: $distinct_on - order_by: $order_by_creation_data - where: $where - ) { - data { - token_id - token_name - token_prefix - collection_id - collection_name - date_of_creation - } - count - } -} -``` - -The following examples contains the "where" argument which adds a search by name: - -```json:no-line-numbers -{ - "offset": 0, - "limit": 20, - "distinct_on": "collection_id", - "order_by_creation_data": {"date_of_creation": "desc"}, - "where": { - "_and": { - "collection_name": {"_eq": "chelobrick"}, - "token_name": {"_ilike": "che%"} - } - } -} -``` - -You can define any conditions for your needs using available fields and operators: `_or`, `_and`, `_eq`, `_like`, `_ilike`, etc. - -In our wallet, we use React library called Apollo for convenient interaction with the Graphql queries. Its hooks help to make dynamic queries, watch a current query status, refetch queries, handle data and errors easier, and do a lot of other useful things. -If you'd like to know more about it, you can visit [Apollo](https://www.apollographql.com/docs/) - -### To learn next - -In this article, we got acquainted with the basics of fetching NFT data. -The same principles you can use to make requests into other tables. -If you are interested in more information, we suggest you to read about [Apollo](https://www.apollographql.com/docs/), [React Query](https://tanstack.com/query/v4/docs/examples/react/basic-graphql-request), or another state management library available for working with GraphQL. - -In addition, if you are interested about deeper fundamental knowledge, you can read [GraphQL](https://graphql.org/learn/) documentation. \ No newline at end of file diff --git a/docs/tutorials/nfts-how-to-create-and-tune-collection.md b/docs/tutorials/nfts-how-to-create-and-tune-collection.md deleted file mode 100644 index 0f97743..0000000 --- a/docs/tutorials/nfts-how-to-create-and-tune-collection.md +++ /dev/null @@ -1,139 +0,0 @@ -# How to create and setup a collection - -To create an NFT collection, you need to initialize the SDK client. You can learn how to to do this in this [article](../sdk/installation.md). Also, you can learn how to get the signer address there, that you'll need to sign a collection creation transaction. - -And, you'll need a _valid ipfs cid_ of your cover image. Read more about storing images, videos, etc. [here](./store-files.md). - -### Create a simple collection - -Please keep in mind that collections can be created using different schemas. At the moment, we support three schemas: - -```typescript:no-line-numbers -enum COLLECTION_SCHEMA_NAME { - unique = "unique", - old = "_old_", - ERC721Metadata = "ERC721Metadata", -} -``` - -In fact, you have only two available options, because we strongly do not recommend using the `_old_` schema, because it is already deprecated. - -So, let's create a simple collection using the `unique` schema: - -```typescript:no-line-numbers -import Sdk from "@unique-nft/substrate-client"; -import { - COLLECTION_SCHEMA_NAME, - CreateCollectionNewArguments, -} from "@unique-nft/substrate-client/tokens"; -import { UniqueCollectionSchemaToCreate } from "@unique-nft/api"; - -main() { - // Create SDK client, get address - ... - - // Get ipfs cid of collection cover - ... - - // Create basic collection schema - const collectionSchema: UniqueCollectionSchemaToCreate = { - schemaName: COLLECTION_SCHEMA_NAME.unique, - schemaVersion: "1.0.0", - image: { urlTemplate: "some_url/{infix}.extension" }, - coverPicture: { - ipfsCid: "", - }, - }; - - // Fill up required arguments - const args: CreateCollectionNewArguments = { - address: "", - name: "My simple collection", - description: "I've created my first NFT collection!", - tokenPrefix: "MSC", - schema: collectionSchema, - }; - - const result = await sdk.collection.create.submitWaitResult(args); - - const { isCompleted } = result; - - if (isCompleted) { - const { - parsed: { collectionId }, - } = result; - - console.log(`Created new collection with id ${collectionId}`); - } else { - const { - submittableResult: { dispatchError }, - } = result; - - console.warn("Something went wrong: ", dispatchError); - } -} - -main(); - -``` - -**Arguments for the `sdk.collection.create` method**: - -`address` - the address of the collection owner. - -`name` - the collection name (text value up to 64 characters). - -`description` - the collection description (text value up to 256 characters). - -`mode` - the collection type (NFT, Fungible, or ReFungible). - -`tokenPrefix` - the token prefix (text value up to 4 characters). - -`sponsorship` - this field defines whether a sponsorship is enabled and which address is the current collection sponsor. - -`limits` - the collection limits. - -`metaUpdatePermission` - the permission to update metadata (ItemOwner, Admin, None). - -`permissions` - the collection permissions. - -`schema` - the collection schema. - -## Setup a collection - -Your NFT collection have a bunch of various properties such as limits, permissions, token attributes and some others. Some of them you can set only while collection creation, but others you can set up later, when your collection is already created. - - - -You can find the list of SDK methods, that you can use to adjust your collection [here](../sdk/methods.md#collection). - -For example, let's update the collection limits using `sdk.collection.setLimits` method. The method sets some collection limits and starts enforcing them immediately. By the way, only the collection owner has the permission to call this method. - -```typescript:no-line-numbers -import "@unique-nft/substrate-client/tokens"; -import { SetCollectionLimitsArguments } from "@unique-nft/substrate-client/tokens/types"; - -const limitsArgs: SetCollectionLimitsArguments = { - address: "", - collectionId: "", - limits: { - accountTokenOwnershipLimit: 1000, - sponsoredDataSize: 1024, - sponsoredDataRateLimit: 30, - tokenLimit: 1000000, - sponsorTransferTimeout: 6, - sponsorApproveTimeout: 6, - ownerCanTransfer: false, - ownerCanDestroy: false, - transfersEnabled: false, - }, -}; - -const setResult = await sdk.collection.setLimits.submitWaitResult(limitsArgs); -const { - parsed: { collectionId, limits }, -} = result; -``` diff --git a/docs/tutorials/nfts-how-to-mint.md b/docs/tutorials/nfts-how-to-mint.md deleted file mode 100644 index e5a3dd9..0000000 --- a/docs/tutorials/nfts-how-to-mint.md +++ /dev/null @@ -1,191 +0,0 @@ -# Mint an NFT using schemas - -### Create an account - -Install the the needed packages. - - - - -```bash:no-line-numbers -npm i @unique-nft/substrate-client --save-dev -npm i @unique-nft/accounts --save-dev -``` - - - - -```bash:no-line-numbers -yarn add @unique-nft/substrate-client --dev -yarn add @unique-nft/accounts --dev -``` - - - - - -Import the required objects and initialize the account using the Substrate Client. - -```typescript:no-line-numbers -import Sdk from '@unique-nft/substrate-client'; -import { KeyringProvider } from '@unique-nft/accounts/keyring'; - -const mnemonic = 'bus ahead nation nice damp recall place dance guide media clap language'; -const chainWsUrl = 'wss://ws-opal.unique.network'; - -const signer = await KeyringProvider.fromMnemonic(mnemonic); - -const sdk = new Sdk({ - chainWsUrl, - signer, -}); -``` - -### Upload images to IPFS - -When we have a collection cover image or an NFT image, we can upload these files to IPFS by the following script: - -```typescript:no-line-numbers -import { createReadStream } from 'fs'; -import FormData from 'form-data'; -import fetch from 'node-fetch'; -import mime from 'mime-types'; - -const filename = process.argv[process.argv.length - 1]; - -if ( - !filename || - filename === __filename -) { - process.exit(); -} - -const URL = 'https://rest.unique.network/opal/v1/ipfs/upload-file'; - -const form = new FormData(); -form.append( - 'file', - createReadStream(filename), - { - contentType: mime.lookup(filename) as string, - filename, - }, -); - -const result = fetch(URL, { method: 'POST', body: form }); -result.then((res) => { - if (res.ok) { - res.json().then(console.log); - } -}); -``` - -After uploading a file, we get a response from a server with an IPFS Cid: - -```json:no-line-numbers -{ - cid: 'Qmc1Dj8m4z2vcojjJjp348FKffmSjopSFTgATpU8gUx5k1', - fileUrl: 'https://ipfs.unique.network/ipfs/Qmc1Dj8m4z2vcojjJjp348FKffmSjopSFTgATpU8gUx5k1' -} -``` - -### Create a collection - -For create a brand-new collection with cats, we should set a name, a description, a token prefix and add some information about future tokens. In other words, we should define a attributes scheme. - -We can take the cover picture and the URL template from previous step. - -```typescript:no-line-numbers -const { parsed: { collectionId } } = await sdk.collection.create.submitWaitResult({ - address, - name: 'Crazy cats', - description: 'Crazy cats from village', - tokenPrefix: 'CRC', - schema: { - schemaName: COLLECTION_SCHEMA_NAME.unique, - schemaVersion: '1.0.0', - coverPicture: { - ipfsCid: 'Qmc1Dj8m4z2vcojjJjp348FKffmSjopSFTgATpU8gUx5k1', - }, - image: { - urlTemplate: 'https://ipfs.unique.network/ipfs/{infix}', - }, - attributesSchemaVersion: '1.0.0', - attributesSchema: { - 0: { - name: { '_': 'sex' }, - type: AttributeType.string, - optional: true, - isArray: false, - enumValues: { - 0: { '_': 'male' }, - 1: { '_': 'female' } - } - }, - 1: { - name: { '_': 'name' }, - isArray: false, - optional: false, - type: AttributeType.string, - }, - 2: { - name: { '_': 'color' }, - isArray: true, - type: AttributeType.string, - optional: true, - enumValues: { - 0: { '_': 'black' }, - 1: { '_': 'white' }, - 2: { '_': 'gray' }, - 3: { '_': 'brown' }, - }, - } - } - } -}); - -console.log('Collection ID: ', collectionId); -``` - -The `attributesSchema` object should contain a token description fields like types, possible values, requirement flag and an array with a list of values. - -### Create an NFT token - -When you perform all previous steps, you have all what is needed to create a new NFT token. - -```typescript:no-line-numbers -const { parsed: { tokenId } } = await sdk.token.create.submitWaitResult({ - address, - collectionId, - data: { - image: { - ipfsCid: 'QmQuDVCPqkQhXw37Tbi35Ue4xUXYgBsyifoWR69aCngn82', - }, - encodedAttributes: { - 0: 0, - 1: { '_': 'Jack' }, - 2: [1, 2, 3], - }, - } -}); - -console.log('Token was minted. ID: ', tokenId) -``` - -As you can see, for creating a token we should have an account address. Also, we should pass `CollectionID` created on the previous step and the IPFS Cid. - -If we have collection with specified schema attributes (see the collection schema [above](#create-a-collection)), we can define the token attributes in accordance with collection attributes schema: - -```typescript:no-line-numbers -encodedAttributes: { - 0: 0, - 1: { '_': 'Jack' }, - 2: [1, 2, 3], -} -``` - -First attribute have 0 index and it means what cat sex is 'male' according the enum values. - -Second attribute means that cat name is 'Jack' and that this attribute can contain any text value. - -Third attribute is a cat color and it can contain the array of colors specified in the enum with colors. \ No newline at end of file diff --git a/docs/tutorials/nfts-ways-to-create.md b/docs/tutorials/nfts-ways-to-create.md deleted file mode 100644 index d79e0b1..0000000 --- a/docs/tutorials/nfts-ways-to-create.md +++ /dev/null @@ -1,166 +0,0 @@ -# (Duplicate) Substrate Client. Different approaches to create a collection - -In this guide, we will go through the entire process of creating token using the Unique Network SDK. - -To create tokens and collections, we need the following packages: - -[@unique-nft/substrate-client](https://www.npmjs.com/package/@unique-nft/substrate-client) - -[@unique-nft/accounts](https://www.npmjs.com/package/@unique-nft/accounts) - - -## Initialization - -To get signer we need to install the [@unique-nft/accounts](https://www.npmjs.com/package/@unique-nft/accounts) package. -An easy way to get signer is to use the `KeyringProvider` function from the `@unique-nft/accounts/keyring` package: - -#### Get a signer - -```typescript:no-line-numbers -// signer.ts -import {KeyringProvider} from '@unique-nft/accounts/keyring'; - -export const getSigner = async (seed = '//Alice') => { - const provider = new KeyringProvider({type: 'sr25519'}); - await provider.init(); - provider.addSeed(seed); - const account = await provider.first(); - const signer = account?.getSigner(); - - return signer; -} -``` - -#### Initialize SDK - -```typescript:no-line-numbers -// sdk.servicce.ts -import Sdk from '@unique-nft/substrate-client'; -import { getSigner } from './signer'; - -export const sdk = await Sdk.create({ - chainWsUrl: 'wss://ws-opal.unique.network', - signer: await getSigner('//Alice'), -}); -``` - -## Upload a file - -You can upload files in the following ways: upload to IPFS, or use our [Rest method](https://rest.opal.uniquenetwork.dev/swagger/#/ipfs/IpfsController_uploadFile). - -Response example: - -```json:no-line-numbers -{ - "cid": "QmUDXW8FbHQ1GGhJbj9kiPeCeTH5SX7QPHJFQGtEp9QeEm", - "fileUrl": "https://ipfs.uniquenetwork.dev/ipfs/QmUDXW8FbHQ1GGhJbj9kiPeCeTH5SX7QPHJFQGtEp9QeEm" -} -``` - -And then, you can use those response entities to create collections/tokens. - -## Create a collection - -```typescript:no-line-numbers -import '@unique-nft/substrate-client/tokens'; -import Sdk from './sdk.servicce.ts'; - -const myCollection = { - address: 'unjKJQJrRd238pkUZZvzDQrfKuM39zBSnQ5zjAGAGcdRhaJTx', - description: 'Just sample collection', - name: 'Sample', - tokenPrefix: 'SMPL', - schema: { - schemaName: 'unique', - schemaVersion: '1.0.0', - image: { urlTemplate: 'https://ipfs.uniquenetwork.dev/ipfs/{infix}.ext' }, - coverPicture: { - ipfsCid: 'QmZ6J9dVMa7B1Xd8PWXALyV8pQUSX5TNNSTpxxWuGH4ZRi', - }, - }, -}; - -const result = await sdk.collection.create.submitWaitResult(myCollection); -const collectionId = result.parsed.collectionId; -const collection = await sdk.collection.get({ collectionId }); -``` - -#### Other ways to create a collection - -```typescript:no-line-numbers -/** - * returns unsigned extrinsic - */ -const unsignedExtrinsic = await sdk.collection.create.build(myCollection); - -/** - * return signed extrinsic (unsigned extrinsic + signature + signature type) - */ -const signedExtrinsic = await sdk.collection.create.sign(myCollection); - -/** - * submitting extrinsic and returns extrinsic hash - */ -const { hash } = await sdk.collection.create.submit(myCollection); - -/** - * submitting extrinsic and returns Observable of extrinsic progress - */ -const newCollection$ = sdk.collection.create.submitWatch(myCollection); -``` - -#### Collection settings methods - -`setLimits` - sets collection limits. - -`setProperties` - sets collection properties. - -`deleteProperties` - deletes collection properties. - -`setPropertyPermissions` - sets permissions for collection properties. - -`setSponsorship` - set sponsor of collection. - -`confirmSponsorship` - confirms sponsorship of a collection. - -`removeSponsorship` - deletes the collection sponsor. - -`setPermissions`- sets onchain permissions for a collection. - -`destroy` - destroys collection if no tokens within this collection. - -`setTransfersEnabled` - enables or disables transfers in a collection. - -`transfer` - changes the owner of the collection. - -`addAdmin` - adds an administrator of a collection. - -`removeAdmin` - removes administrator of a collection. - -`addToAllowList` - adds an address to the allow list. - -`removeFromAllowList` - removes an address from the allow list. - -## Create a token - -```typescript:no-line-numbers -import '@unique-nft/substrate-client/tokens'; -import Sdk from './sdk.servicce.ts'; - -// example ot token arguments -const createTokensArgs = { - owner, - data: { - image: { - ipfsCid: '', - }, - }, - address, - collectionId, -}; - -const result = await sdk.token.create.submitWaitResult(createTokensArgs); -const tokenId = result.parsed; - -const token = await this.sdk.token.get({ collectionId, tokenId }) -``` diff --git a/docs/tutorials/store-files.md b/docs/tutorials/store-files.md deleted file mode 100644 index 90e1b9f..0000000 --- a/docs/tutorials/store-files.md +++ /dev/null @@ -1,93 +0,0 @@ -# (Duplicate) How to store files - -### SDK - -Install the [SDK](https://www.npmjs.com/package/@unique-nft/sdk) package. - - - - -```bash:no-line-numbers -npm install @unique-nft/sdk -``` - - - - -```bash:no-line-numbers -yarn add @unique-nft/sdk -``` - - - - -In the `baseUrl` parameter, you need to pass one of the following URLs depending on which parachain you want to work with. - -Opal - `https://rest.unique.network/opal` - -Quartz - `https://rest.unique.network/quartz` - -Unique - `https://rest.unique.network/unique` - -```typescript:no-line-numbers -import Sdk from '@unique-nft/sdk'; - -const baseUrl = 'https://rest.unique.network/opal'; - -const main = async () => { - const uploadFile = async (file) => { - const options = { baseUrl }; - const client = new Sdk(options); - - // the "uploadZip" method can be used to upload archives - return client.ipfs.uploadFile({ file }); - }; - - const form = document.querySelector('form'); - form.addEventListener('submit', async (event) => { - event.preventDefault(); - const file = document.querySelector('#nftFile').files[0]; - const response = await uploadFile(file); - document.querySelector('#response').innerText = JSON.stringify( - response, - null, - 4, - ); - }); -}; - -main(); -``` - -### REST libraries - -```typescript:no-line-numbers -import { createReadStream } from 'fs'; -import FormData from 'form-data'; -import fetch from 'node-fetch'; -import mime from 'mime-types'; - -const filename = ''; - -const URL = 'https://rest.unique.network/opal/v1/ipfs/upload-file'; -const form = new FormData(); -form.append( - 'file', - createReadStream(filename), - { - contentType: mime.lookup(filename) as string, - filename, - }, -); - -const result = fetch(URL, { method: 'POST', body: form }); -``` - -After uploading a file, we get a response from a server with an IPFS Cid: - -```json:no-line-numbers -{ - "cid": "Qmc1Dj8m4z2vcojjJjp348FKffmSjopSFTgATpU8gUx5k1", - "fileUrl": "https://ipfs.unique.network/ipfs/Qmc1Dj8m4z2vcojjJjp348FKffmSjopSFTgATpU8gUx5k1" -} -``` \ No newline at end of file diff --git a/docs/tutorials/subquery-indexer.md b/docs/tutorials/subquery-indexer.md deleted file mode 100644 index 336ced6..0000000 --- a/docs/tutorials/subquery-indexer.md +++ /dev/null @@ -1,21 +0,0 @@ -# SubQuery Indexer - -## Intro - -SubQuery is a leading blockchain data indexer that provides developers with fast, flexible, universal, open source and decentralised APIs for web3 projects. SubQuery SDK allows developers to get rich indexed data and build intuitive and immersive decentralised applications in a faster and more efficient way. SubQuery supports 100+ ecosystems including Unique, Polkadot, Cosmos, Ethereum, Polygon, Algorand, NEAR, and Avalanche. - -Another Subquery's competitive edge lies in the capacity to consolidate data, not just within a single blockchain but across multiple blockchains within a unified project. This facilitates the development of comprehensive dashboard analytics, cross-chain block scanners, or initiatives aimed at indexing XCM messages across parachains. - -Other advantages include superior performance with multiple RPC endpoint configurations, multi-worker capabilities and a configurable caching architecture. To find out more, visit our [documentation](https://academy.subquery.network/). - -**Useful resources**: -- SubQuery Docs: [SubQuery Academy (Documentation)](https://academy.subquery.network/) -- Intro Quick Start Guide: [1. Create a New Project](https://academy.subquery.network/quickstart/quickstart.html) -- [Unique Starter Project](https://github.com/subquery/subql-starter/tree/main/Unique/unique-starter) - -## Running and Hosting your Unique SubQuery APIs - -SubQuery is open-source, meaning you have the freedom to run it in the following three ways: -- Locally on your own computer (or a cloud provider of your choosing), [view the instructions on how to run SubQuery Locally](https://academy.subquery.network/run_publish/run.html). -- You can publish it to SubQuery's enterprise-level [Managed Service](https://managedservice.subquery.network/), where we'll host your SubQuery project in production ready services for mission critical data with zero-downtime blue/green deployments. There even is a generous free tier. [Find out how](https://academy.subquery.network/run_publish/publish.html). -- You can publish it to the decentralised [SubQuery Network](https://subquery.network/network), the most open, performant, reliable, and scalable data service for dApp developers. The SubQuery Network indexes and services data to the global community in an incentivised and verifiable way and supports Unique from launch. \ No newline at end of file diff --git a/docs/tutorials/wallet-integration.md b/docs/tutorials/wallet-integration.md deleted file mode 100644 index f46f69f..0000000 --- a/docs/tutorials/wallet-integration.md +++ /dev/null @@ -1,79 +0,0 @@ -# Wallet integration - -### Scan API - -[Scan API for Unique parachain](https://api-unique.uniquescan.io/v1/graphql) - -[Scan API for Quartz parachain](https://api-quartz.uniquescan.io/v1/graphql) - -[Scan API for Opal testnet](https://api-opal.uniquescan.io/v1/graphql) - -Every endpoint provides a GraphQL playground. - -## Examples - -In all code examples at this page we assume that the `$ownerNormalized` variable should be one of: -- a Substrate address in default (42) format, like `"5HNUuEAYMWEo4cuBW7tuL9mLHR9zSA8H7SdNKsNnYRB9M5TX"` -- an Ethereum address in __lowercase__, like `"0xeabbf89e7a3866183c49366dc30c10837c073a6f"` - -### Collections - -How to obtain a list of collections where the address is owner or owns at least one NFT: - -```graphql -query MyCollections($ownerNormalized: String) { - collections( - where: { - _or: [ - {owner_normalized: {_eq: $ownerNormalized}}, - {tokens: {owner_normalized: {_eq: $ownerNormalized}}}, - ] - }, - order_by: {collection_id: asc} - offset: 0 - limit: 10 - ) { - count - timestamp - data { - collection_id - type - token_prefix - name - collection_cover - description - } - } -} -``` - -### Tokens - -How to obtain a list of tokens where the address is owner. - -`collection_id` param is optional, it's here just for example how to obtain NFTs from specific collections. - -```graphql -query MyTokens($ownerNormalized: String) { - tokens( - where: { - owner_normalized: {_eq: $ownerNormalized}, - collection_id: {_in: [123]} - }, - order_by: {collection_id: desc, token_id: asc} - offset: 0 - limit: 10 - ) { - count - timestamp - data { - collection_id - token_id - token_name - image - owner_normalized - date_of_creation - } - } -} -``` diff --git a/docs/tutorials/websocket-subscriptions.md b/docs/tutorials/websocket-subscriptions.md deleted file mode 100644 index 98b5fd3..0000000 --- a/docs/tutorials/websocket-subscriptions.md +++ /dev/null @@ -1,227 +0,0 @@ -# Websocket subscriptions - -### Connect to the client - -In this tutorial, we will using the [Socket.IO client](https://socket.io/docs/v4/client-initialization/) library. So first of all, you need to install it: - - - - -```bash:no-line-numbers -npm install socket.io-client -``` - - - - -```bash:no-line-numbers -yarn add socket.io-client -``` - - - - -Then, you can import it to your project and initialize the client by the following code: - -```typescript:no-line-numbers -import { io } from 'socket.io-client'; - -const socket = io('https://rest.unique.network', { - path: '/opal/socket.io', - transports: ['websocket'], -}); - -socket.on('system', (room, data) => { - console.log(room); - console.log(data); -}); -``` - -When the connection is established, you will receive a `system` event with the blockchain network information to which you connected. - -```typescript:no-line-numbers -{ - name: 'system' -} -{ - SS58Prefix: 42, - token: 'OPL', - decimals: 18, - wsUrl: 'wss://ws-opal.unique.network', - genesisHash: '0xc87870ef90a438d574b8e320f17db372c50f62beb52e479c8ff6ee5b460670b9' -} -``` - -### Headers - -To receive events about new `headers`, you can subscribe the following way: - -```typescript:no-line-numbers -socket.on('headers', (room, header) => { - console.log(header); -}); - -socket.on('connect', () => { - socket.emit('subscribe:headers'); -}); -``` - -You will receive the `header` object that contains these fields: - -```typescript:no-line-numbers -interface HeaderResult { - hash: string; - parentHash: string; - number: string; - stateRoot: string; - extrinsicsRoot: string; - digest: object; -} -``` - -### Blocks - -To receive events about new `blocks`, you can subscribe the following way: - -```typescript:no-line-numbers -socket.on('blocks', (room, block) => { - console.log(block); -}); - -socket.on('connect', () => { - socket.emit('subscribe:blocks'); -}); -``` - -You will receive `block` object that contains these fields: - -```typescript:no-line-numbers -interface BlockResult { - hash: string; - header: HeaderResult; -} -``` - -### Extrinsics - -To receive events about new `extrinsics`, you can subscribe the following way: - -```typescript:no-line-numbers -socket.on('extrinsics', (room, extrinsic) => { - console.log(extrinsic); -}); - -socket.on('connect', () => { - socket.emit('subscribe:extrinsics'); -}); -``` - -You will receive a `extrinsic` object that contains these fields: - -```typescript:no-line-numbers -interface ExtrinsicResultResponse { - isCompleted: boolean; - hash: string; - blockIndex: number; - error: object; - events: ExtrinsicResultEvent[]; - parsed?: object; - fee?: FeeResponse; - callbackUrl: string; - createdAt: number; - block: BlockResult; - callMethod: MethodName; -} -``` - -##### Extrinsic filtering - -You can filter extrinsics by specifying the collection id. - -In the example below, you will only get extrinsics from collection #123: - -```typescript:no-line-numbers -socket.on('extrinsics', (room, extrinsic) => { - console.log(extrinsic); -}); - -socket.on('connect', () => { - socket.emit('subscribe:extrinsics', { - collectionId: 123, - }); -}); -``` - -To get extrinsics from any collection, you can use this filter: - -```typescript:no-line-numbers -socket.on('extrinsics', (room, extrinsic, block) => { - console.log(extrinsic); -}); - -socket.on('connect', () => { - socket.emit('subscribe:extrinsics', { - collectionId: '*', - }); -}); -``` - -### Events - -To receive events about new `events`, you can subscribe the following way: - -```typescript:no-line-numbers -socket.on('events', (room, event, extrinsic, block) => { - console.log(event); -}); - -socket.on('connect', () => { - socket.emit('subscribe:events'); -}); -``` - -You will receive a `event` object that contains these fields: - -```typescript:no-line-numbers -interface ExtrinsicResultEvent { - section: string; - method: string; - data: object; - meta: object; - index: string; -} -``` - -##### Events filtering - -You can filter the `events` stream to receive events from a specific section and method. - -The following example will display only the token creation events: - -```typescript:no-line-numbers -socket.on('events', (room, event, extrinsic, block) => { - console.log(event); -}); - -socket.on('connect', () => { - socket.emit('subscribe:events', { - method: 'ItemCreated', - section: 'common', - }); -}); -``` - -As values for the `method` and `section` fields, you can specify the "*" character which will mean any value. -For example, the following filter will return all events from the `common` section. - -```typescript:no-line-numbers -socket.on('events', (room, event, extrinsic, block) => { - console.log(event); -}); -socket.on('connect', () => { - socket.emit('subscribe:events', { - method: '*', - section: 'common', - }); -}); -``` \ No newline at end of file diff --git a/docs/tutorials/work-with-accounts.md b/docs/tutorials/work-with-accounts.md deleted file mode 100644 index c3e0eb7..0000000 --- a/docs/tutorials/work-with-accounts.md +++ /dev/null @@ -1,520 +0,0 @@ -# Cookbook for working with accounts and signers - -## Account as an entity - -A blockchain account is an entity associated with a public blockchain address to identify a network participant and is used to sign transactions. In Web3, you digitally sign any transaction, or more generally, any message, using your private key. - -To work with accounts in the front-end application, you will need to install the following libraries: - -```sh:no-line-numbers -npm install @unique-nft/sdk @unique-nft/utils -``` - -In this guide, we will implement the connection of accounts stored in the local browser storage, as well as those supplied by browser extensions, such as Polkadot and Metamask. - -But let's start with creating an account interface in accordance with its abstraction: - -```typescript -import { Signer } from "@unique-nft/sdk"; - -//define enum of signer types -enum SignerTypeEnum { - Local = 'Local', - Polkadot = 'Polkadot', - Metamask = 'Metamask' -} - -interface Account { - name: string; // human-readable name of account - address: string; // address - signerType: SignerTypeEnum; // type of account - signer: Signer; // interface for sign transaction via SDK -} -``` - -The transaction signing mechanism in the SDK requires that the Signer interface contains the sign method: - -```typescript -// @unique-nft/sdk -interface Signer { - address?: string; - sign(unsignedTxPayload: UnsignedTxPayloadBody): Promise; -} -``` - -## Local Account - -### Create local account - -Creating an account begins with generating a random mnemonic. To generate an account in the UI, the user can be asked to enter a mnemonic in the field or generate a new one. To generate, we will use the `@unique-nft/sr25519` library: - -```sh:no-line-numbers -npm install @unique-nft/sr25519 -``` - -The `@unique-nft/sr25519` library is a specific implementation of the `SR25519` cryptographic algorithm. It provides a set of functions and utilities for working with the SR25519 keypair generation, signing, verification, and other related operations. `SR25519` is a cryptographic algorithm used in blockchain networks, particularly in Substrate-based systems like `Polkadot` and Kusama. This library enables developers to integrate and interact with the SR25519 cryptography in their applications or projects within those blockchain ecosystems. - -To encrypt mnemonic pharase and save it in localStorage we use cryptographic library `tweetnacl-ts`: - -```sh:no-line-numbers -npm install tweetnacl-ts -``` - -We can get the mnemonic phrase: - -```typescript -import { Sr25519Account } from '@unique-nft/sr25519'; - -const mnemonicPhrase = Sr25519Account.generateMnemonic() -``` - -It is also necessary to request the user’s password and, if desired, an account name (a human-readable name for better UX), after which we will encrypt mnemonic phrase by the password and save it in localStorage. - -```typescript -import { StringUtils } from '@unique-nft/utils'; -import { secretbox } from 'tweetnacl-ts'; -import { algorithms } from '@unique-nft/utils/address'; - -// get passphrase hash -const passwordHash = algorithms.keccak_256(passphrase) - -// encrypt mnemonic phrase -const boxed = secretbox( - StringUtils.Utf8.stringToU8a(mnemonicPhrase), - NONCE, - passwordHash -); - -// save -localStorage.setItem(`account:${address}`, JSON.stringify({ - name, - secret: StringUtils.HexString.fromU8a(boxed) -})); -``` - -The `secretbox` function in the `tweetnacl-ts` library is used for encrypting messages using symmetric-key encryption. It takes a message and a secret key as input and generates an encrypted message that can be transmitted over insecure communication channels. The `secretbox` function provides data confidentiality, allowing the sender and recipient to exchange information without the risk of it being read by a third party. - -The `NONCE` argument in the secretbox function of the tweetnacl-ts library is a unique value used in symmetric-key encryption to ensure the uniqueness of each encrypted message. Nonce stands for "number used once." - -Finally, let's define the complete function that creates the account: - -```typescript -function addLocalAccount(name: string, mnemonicPhrase: string, passphrase: string) { - const passwordHash = algorithms.keccak_256(passphrase) - - const { address } = Sr25519Account.fromUri(mnemonicPhrase); - - const secret = secretbox( - StringUtils.Utf8.stringToU8a(mnemonicPhrase), - NONCE, - passwordHash - ); - - localStorage.setItem(`account:${address}`, JSON.stringify({ - name, - secret: StringUtils.HexString.fromU8a(secret) - })); -} -``` - -### Getting a list of accounts -To fetch all accounts from localStorage, we will iterate all the keys, select accounts and add this account into a `Map`: - -```typescript - -function getLocalAccounts(askPassphraseCallback: AskPassphraseCallback) { - const accounts = new Map(); - for (const key of Object.keys(localStorage)) { - if(key && /^account:/.test(key)){ - const value = localStorage.getItem(key); - if(!value) break; - const address = key.split(':')[1]; - const { name, secret } = JSON.parse(value); - - accounts.set(address, { - name, - address, - signerType: SignerTypeEnum.Local, - signer: new LocalAccountSigner(secret, askPassphraseCallback) - }); - } - } - - return accounts; -}; -``` - -In the code above, the `askPassphraseCallback` argument passes the callback that will be needed to call the mechanism in the UI for obtaining a password from the user. This callback will be called in the sign method, display a password entry form in the UI and wait for the user’s response, in which we can also unlock the keyring of the account. To do this, we will pass it to the `LocalAccountSigner` constructor. In the next chapter we will describe the KeyringSigner class. In the meantime, let’s take a closer look at signing and calling the password entry form. - -To understand the principle of calling a password entry form from a callback to the UI, here is a small example. Let's say we have this html: - -```html - - - -
- - ... -
- -
- - - -
- - -``` - -Let's write a callback code that, when called, will show a modal window with a password input field and return a Promise awaiting the user's response: - -```typescript -function showAskPasswordModal(decrypt: (password: string) => boolean) { - return new Promise((resolve) => { - modal.classList.add('visible'); // show modal - - submitButton.onclick = () => { - modal.classList.remove('visible'); // hide modal - const password = passworInput.value; // get password - if (decrypt(password)) { // decrypt - resolve(); - } - } - }); -} -``` - -### Sign a transaction via SDK - -The SDK expects a signer object containing a method: - -```typescript:no-line-numbers -// @unique-nft/sdk -async sign(unsignedTxPayload: UnsignedTxPayloadBody): Promise -``` -to sign a transaction. Let's describe a class for creating a signer object for local accounts: - -```typescript -export class LocalAccountSigner implements Signer { - secret: string; - askPassphraseCallback: AskPassphraseCallback; - - constructor(secret: string, askPassphraseCallback: AskPassphraseCallback) { - this.secret = secret; - this.askPassphraseCallback = askPassphraseCallback; - } - - private async getAccount() { - let mnemonicPhrase: string | undefined; - - await this.askPassphraseCallback?.((passphrase: string) => { - // get password hash - const passwordHash = Address.algorithms.keccak_256(passphrase) - - // decrypt - const mnemonicPhraseU8a = secretbox_open( - StringUtils.HexString.toU8a(this.secret), - NONCE, - passwordHash - ); - - if (mnemonicPhraseU8a) { - mnemonicPhrase = StringUtils.Utf8.u8aToString(mnemonicPhraseU8a); - } - - return !!mnemonicPhrase; - }); - if(!mnemonicPhrase) return; - return Sr25519Account.fromUri(mnemonicPhrase); - } - - public async sign(unsignedTxPayload: UnsignedTxPayloadBody): Promise { - const account = await this.getAccount(); - if(!account) throw new Error('No account'); - - return await account.signer.sign(unsignedTxPayload); - } -} -``` - -We now have two ways to pass signer to the SDK: - -a. when initializing the SDK client (if there is only one account in the application or there is a default account): - -```typescript -const sdk = new Sdk({ - baseUrl, - signer: account.signer -}) -``` - -b. when creating a transaction: -```typescript -sdk?.balance.transfer.submitWaitResult({ - address: sender.address, - destination: receiver.address, - amount: 100, -}, { - signer: sender.signer, // signer here -}) -``` - -### Sign a message - -Sometimes it becomes necessary to sign not only transactions but also some text messages. To do this, we’ll add the `signMessage` method to our class: - -```typescript -export class LocalAccountSigner implements Signer { - ... - - public async signMessage(message: string): Promise { - const account = await this.getAccount(); - if(!account) throw new Error('No account'); - - const signatureU8a = account.sign(message); - return StringUtils.HexString.fromU8a(signatureU8a); - } -} -``` - -### Remove account - -To delete a local account in the local storage just remove key: - -```typescript -function deleteLocalAccount(address: string) { - localStorage.removeItem(`account:${address}`); -} -``` - -## Polkadot-extension Account -### Getting a list of accounts - -To work with accounts in browser extensions, for example Polkadot extension, the `@unique-nft/utils` library provides a module: - -```typescript -import { Polkadot } from '@unique-nft/utils/extension'; - -const { accounts } = await Polkadot.enableAndLoadAllWallets(); -``` - -The `enableAndLoadAllWallets` method allows you to get an array of accounts, or triggers one of the errors: - -```typescript -export const getPolkadotAccounts = async () => { - try { - const { accounts } = await Polkadot.enableAndLoadAllWallets() - - return new Map( - accounts.map(({ name, address, signer }) => { - return [ - address, // address as map key - { - name, - address, - signerType: SignerTypeEnum.Polkadot, - signer, - } - ] - }) - ); - } catch(e: any) { - if (e.extensionNotFound) { - alert(`Please install some polkadot.js compatible extension`) - } else if (e.accountsNotFound) { - if (e.userHasWalletsButHasNoAccounts) { - alert(`Please, create an account in your wallet`) - } else if (e.userHasBlockedAllWallets) { - alert(`Please, grant access to at least one of your accounts`) - } - } else { - alert(`Connection to polkadot extension failed: ${e.message}`) - } - } - return new Map(); -}; -``` - -### Sign a transaction via SDK - -The accounts array element with type `IPolkadotExtensionAccount` already contains a signer object, it's ready for use with the SDK: - -```typescript -const sdk = new Sdk({ - baseUrl, - signer: account.signer - }) -``` - -Or when creating a transaction: - -```typescript -sdk?.balance.transfer.submitWaitResult({ - address: sender.address, - destination: receiver.address, - amount: 100, -}, { - signer: sender.signer, // signer here -}) -``` - -### Sign a message -Also in the `IPolkadotExtensionAccount` interface there is a `signRaw` method: - -```typescript -signRaw: (raw: SignerPayloadRawWithAddressAndTypeOptional | string) => Promise -``` - -It will easily allow you to sign a string value. - -## Metamask-extension Account -### Getting a list of accounts - -For Ethereum browser extensions, like Metamask the `@unique-nft/utils` library provides a module Ethereum: - -```typescript -import { Ethereum } from '@unique-nft/utils/extension' - -try { - const {address, chainId} = await Ethereum.requestAccounts() -} catch (e: IEthereumExtensionError) { - if (e.extensionNotFound) { - alert(`Please install some ethereum browser extension`) - } else if (e.userRejected) { - alert(`User rejected access`) - } else { - alert(`Connection to ethereum extension failed: ${e.message}`) - } -} -``` - -### Sign and send a transaction -To create and sign transactions via the Ethereum-like extension, install the following libraries: - -```sh:no-line-numbers -npm install ethers @unique-nft/solidity-interfaces -``` - -The `ethers` module is needed to create a Web3 provider, which is used to connect and interact with Ethereum nodes. -The `@unique-nft/solidity-interfaces` library is a Solidity-specific library designed for creating and working with non-fungible tokens (NFTs) on the Ethereum blockchain. This library offers functionalities such as defining NFT contract interfaces, implementing NFT metadata and URI standards, handling token ownership and transfers, and managing token metadata storage. By using `@unique-nft/solidity-interfaces`, developers can efficiently build, deploy, and interact with NFT contracts in their Solidity projects, ensuring compatibility with other NFT-related applications and protocols. - -Finally, we can transfer some tokens: - -```typescript -import { ethers } from "ethers"; -import { UniqueFungibleFactory } from "@unique-nft/solidity-interfaces" -import { Address } from "@unique-nft/utils" - -//get ethers provider -const provider = new ethers.providers.Web3Provider(window.ethereum); -const signer = await provider.getSigner(sender.address) - -// convert addresses to CrossAccountId -const from = Address.extract.ethCrossAccountId(sender.address); -const to = Address.extract.ethCrossAccountId(receiver.address); - -// get factory -const uniqueFungible = await UniqueFungibleFactory(0, signer); - -// make transsaction -const contractTransaction = await uniqueFungible.transferFromCross(from, to, amountRaw, { from: sender.address })); - -// wait -await contractTransaction.wait() -``` - -### Sign a message - -Also, to sign a message, we can use `provider`: - -```typescript -const provider = new ethers.providers.Web3Provider(window.ethereum); -const signer = await provider.getSigner(address) -const result = await signer.signMessage(message); -``` - -## Subscription to balance changes via SDK - -The SDK-client allows to subscribe on changing account balance: - -```typescript -import { SocketClient, SubscriptionEvents } from '@unique-nft/sdk/full'; -// connect client -const client: SocketClient = sdk.subscription.connect({ - transports: ['websocket'] -}); - -// subscribe -client.subscribeAccountCurrentBalance({ address: accountAddress }); - -client.on(SubscriptionEvents.ACCOUNT_CURRENT_BALANCE, (_, data) => { - - // update balance here - -}) -``` - -Argument `data` will be an object with the parameter `balance`: - -```typescript -interface AccountCurrentBalanceData { - extrinsic: Extrinsic; - balance: AllBalancesResponse; -} -``` - -And AllBalancesResponse is: - -```typescript -interface AllBalancesResponse { - availableBalance: BalanceResponse; // transferable balance - lockedBalance: BalanceResponse; // any frozen balance: staking, vesting etc - freeBalance: BalanceResponse; // transferable + locked balance - - address: string; -} -``` - -Every balance contains this: - -```typescript -interface BalanceResponse { - /** @example 92485000000000000 */ - raw: string; - /** @example 0.092485000000000000 */ - amount: string; - /** @example 92.4850 m */ - formatted: string; - /** @example UNQ */ - unit: string; - /** @example 18 */ - decimals: number; -} -``` - -For example, we can extend the `Account` for saving balances: - -```typescript -interface Account { - // ... - balances: AllBalancesResponse -} -``` - -Finally, we can update balances: - -```typescript -client.on(SubscriptionEvents.ACCOUNT_CURRENT_BALANCE, (_, data) => { - const { balance } = data; - - accounts.get(balance.address).balances = balance; -}) -``` - -## Examples of working with accounts - -There are couple examples of use with popular front-end frameworks: - -[React Example](https://github.com/UniqueNetwork/accounts-react-example) - -[Vue Example](https://github.com/UniqueNetwork/accounts-vue-example) \ No newline at end of file diff --git a/docs/tutorials/work-with-evm-via-sdk.md b/docs/tutorials/work-with-evm-via-sdk.md deleted file mode 100644 index d6bf016..0000000 --- a/docs/tutorials/work-with-evm-via-sdk.md +++ /dev/null @@ -1,352 +0,0 @@ -# EVM in SDK - -In this tutorial, we will execute the smart contract methods and read their properties using EVM features from our SDK. - -### Sample smart contract - -We will use the following smart contract written in the Solidity language: - -```solidity:no-line-numbers -// SPDX-License-Identifier: UNLICENSED -pragma solidity ^0.8.16; - -contract MyContract { - string public myStrValue = "my string value"; - uint private myUintValue = 123; - - event ChangeValue(uint delta, uint value); - - function getMyUint() external view returns (uint) { - return myUintValue; - } - - function updateMyUint(uint delta) external { - myUintValue += delta; - - emit ChangeValue(delta, myUintValue); - } - - function dropError(uint delta) external { - myUintValue = myUintValue / delta; - } -} -``` - -This smart contract is deployed on the Opal network at this address: ``0xf1917b3D87E0D355a29435A79a63670790E73Aa1``. - -### Smart contract ABI - -To make any request to a smart contract, you will need an ABI JSON file. This file describes all methods and properties -that your smart contract contains. - -For example, the following ABI describes the ``myStrValue`` string. - -```json:no-line-numbers -{ - "inputs": [ ], - "name": "myStrValue", - "outputs": [ - { - "internalType": "string", - "name": "", - "type": "string" - } - ], - "stateMutability": "view", - "type": "function" -} -``` - -The [smart contract above](#sample-smart-contract) is described by this ABI file, which we will use later. -To generate the contract ABI, you need to compile your the smart contract (see -[Compilation artifacts](https://hardhat.org/hardhat-runner/docs/advanced/artifacts)). - -
-
- -### Read a value - - -You can read the `myStrValue` string property using the code below. We will need to -[initialize our SDK](./../sdk/installation.md#initialization) and use its `evm` object. -To connect to the deployed smart contract, we need to call the -`contractConnect` method and pass to it the contract address and its abi that we can store in a file. - -When the contract is found, we just specify our address that performs a transaction and specify which entity -(the `myStrValue` variable) we need to access. - -```typescript:no-line-numbers -import Sdk from '@unique-nft/sdk' -import {KeyringProvider} from '@unique-nft/accounts/keyring' -import abiJSON from './abi.json' - -async function main() { - const account = await KeyringProvider.fromMnemonic( - 'bonus rubber price price initial finger finger finger scorpion pioneer pioneer pioneer' - ) - const address = account.getAddress() - - const sdk = new Sdk({ - baseUrl: 'https://rest.unique.network/opal/v1', - signer: account, - }) - - const contractAddress = '0xf1917b3D87E0D355a29435A79a63670790E73Aa1' - - const contract = await sdk.evm.contractConnect(contractAddress, abiJSON.abi) - - const value = await contract.call({ - address, - funcName: 'myStrValue', - }) - - console.log('The myStrValue value:', value) -} - -main().catch((error) => { - console.error(error) -}) -``` - -### Call a function - -Working with functions is quite similar as we described above. -You can execute a function that does not require a transaction (marked `view`) using the following code. - -:exclamation: Numbers in EVM are represented in the `BigNumber` format. - -```typescript:no-line-numbers -import {KeyringProvider} from '@unique-nft/accounts/keyring' -import abiJSON from './abi.json' -import Sdk from '@unique-nft/sdk' - -async function main() { - const account = await KeyringProvider.fromMnemonic( - 'bonus rubber price price initial finger finger finger scorpion pioneer pioneer pioneer' - ) - const address = account.getAddress() - - const sdk = new Sdk({ - baseUrl: 'https://rest.unique.network/opal/v1', // https://rest.unique.network/opal/v1 https://rest.unq.uniq.su/v1 - signer: account, - }) - - const contractAddress = '0xf1917b3D87E0D355a29435A79a63670790E73Aa1' - - const contract = await sdk.evm.contractConnect(contractAddress, abiJSON.abi) - - const value = await contract.call({ - address, - funcName: 'getMyUint', - }) - - console.log('The getMyUint returns value:', value) -} - -main().catch((error) => { - console.error(error) -}) -``` - -### Send a transaction - -If you want to make a request that makes changes in the chain state, you need to execute the transaction. -But, to make it execute, you must sign it. - -```typescript:no-line-numbers -import Sdk, { Options } from '@unique-nft/sdk'; -import {KeyringProvider} from '@unique-nft/accounts/keyring'; - -For example, to execute the `updateMyUint` method, you can use the following code: - -```typescript:no-line-numbers -import Sdk from '@unique-nft/sdk' -import {KeyringProvider} from '@unique-nft/accounts/keyring' -import abiJSON from './abi.json' - -async function main() { - const account = await KeyringProvider.fromMnemonic( - 'bonus rubber price price initial finger finger finger scorpion pioneer pioneer pioneer' - ) - const address = account.getAddress() - - const sdk = new Sdk({ - baseUrl: 'https://rest.unique.network/opal/v1', - signer: account, - }) - - - const contractAddress = '0xf1917b3D87E0D355a29435A79a63670790E73Aa1' - const contract = await sdk.evm.contractConnect(contractAddress,abi,); - - - const contract = await sdk.evm.contractConnect(contractAddress, abiJSON.abi) - - const result = await contract.send.submitWaitResult( - { - address, - funcName: 'updateMyUint', - args: [1], - }, - { - signer: account, - } - ) - - console.log(result) -} - -main().catch((error) => { - console.error(error) -}) -``` - -#### Arguments interface - -The arguments for the `send` method are the following: - -```typescript:no-line-numbers -interface EvmSendArguments { - address: string; // a Substrate address to sign a transaction - funcName: string; // a function name in smart contract - args?: any[]; // an array of arguments that are passed to the function - - value?: number | string; // the money amount is required to be transferred to the smart contract - gasLimit?: number | string; // the gas limit you want to spend on a function - maxFeePerGas?: number | string; // EIP-1559 Max base fee the caller want to pay - maxPriorityFeePerGas?: number | string; // EIP-1559 Priority fee the caller pays to the block author -} -``` - -#### Parse events - -If your smart contract emits events, you will be able to receive them when the transaction is completed using -these properties: - -`result.parsed.parsedEvents` - events that were successfully read and translated into a readable form. - -`result.parsed.unknownEvents` - events that could not be read. Events cannot be read, for example, -if there is no description of this event in the ABI file, or the description is incorrect. - -```typescript:no-line-numbers -const result = await contract.send.submitWaitResult({ - address: account.getAddress(), - funcName: 'updateMyUint', - args: [1], -}); - -console.log('Parsed Events: ', result.parsed.parsedEvents); -console.log('Unknown Events: ', result.parsed.unknownEvents); -``` - -### Possible errors - -###### EvmArgumentsError - -The error may occur due to the fact that the arguments passed to the function do not match the description in the ABI, -or the specified function name is missed in the ABI file. - -###### EvmCallError - -The error usually occurs in a smart contract using the `revert()` method with no arguments. - -###### EvmCustomError - -This is a custom error from the smart contract that can be thrown in Solidity in the following way: - -```solidity:no-line-numbers -revert MyCustomError({ - errorMessage: "my custom error message", - myData: { - ... - } -}); -``` - -###### EvmPanicError - -An unexpected error in a smart contract. The example of such an error could be, for example, division by 0 -or accessing an array with an index greater than the size of the array. -A complete list of such errors and their codes can be found in the [Solidity documentation](https://docs.soliditylang.org/en/v0.8.18/control-structures.html#panic-via-assert-and-error-via-require).