Nesting

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.

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