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:
Steps
- Prepare
- CreateSdk
- Create new collection
- Create new tokens
- Nesting function
- Example
- IsBundle, getBundle
- Unnest and others
Prepare
Install the above dependencies in the project
Create js or ts file and write the following inside:
// 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
function createSdk(account: any) {
const options = {
baseUrl,
signer: account,
};
return new Sdk(options);
}
Creating a collection
⚠️ 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.
export async function createCollection(sdk, address) {
const { parsed, error } = await sdk.collections.creation.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.collections.get({ collectionId });
}
Token creation
export async function createToken(sdk, address, collectionId) {
const { parsed, error } = await sdk.tokens.create.submitWaitResult({
address,
collectionId,
});
if (error) {
console.log('create token error', error);
process.exit();
}
const { tokenId } = parsed;
return sdk.tokens.get({ collectionId, tokenId });
}
Creating a nested token
export async function createNestedToken(sdk, nestedArgs) {
const { address, parent, nested } = nestedArgs;
const { parsed, error } = await sdk.tokens.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.tokens.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
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
const isBundle = await sdk.tokens.isBundle({
collectionId: collection.id,
tokenId: 1,
});
const isBundle2 = await sdk.tokens.isBundle({
collectionId: collection.id,
tokenId: 2,
});
console.log('token 1 isBundle?', isBundle);
console.log('token 2 isBundle?', isBundle2);
const result: any = await sdk.tokens.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
export async function createUnNestedToken(sdk: any, nestedArgs: any) {
const { address, parent, nested } = nestedArgs;
const { parsed, error } = await sdk.tokens.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.tokens.get({ collectionId, tokenId });
}
and add its call to our functionmain
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.tokens.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