data-book="docs" SFT (Native API example) | The Root Network
Skip to content

2.46 SFT (Native API example) ​
At a glance
What it isCreate and update an SFT collection and its tokens, mint and transfer. Futureverse’s Native API example for The Root Network, with its README and source.
Where it sitsPart II — Building · Chapter 2.46
CoversExamples · Source files
Sourcegithub.com/futureversecom/trn-examples/tree/main/examples/substrate/use-sft

Create and update an SFT collection and its tokens, mint and transfer. Reference: sft pallet.

Copied from Futureverse’s futureversecom/trn-examples, deleted with its GitHub organisation, at commit 44c9e16 of 2024-10-31. Apache-2.0: license. Before you run it, read Running the examples.

Offline destinations

Some places this page points to did not answer when last checked:

  • docs-beta.therootnetwork.com — does not resolve (checked 2026-10-18)
  • stackblitz.com/github/futureversecom — StackBlitz loads these projects from the deleted futureversecom organisation on GitHub (checked 2026-10-18)

Run in StackBlitz (repository deleted, checked 2026-10-18) · Pallet Documentation (offline, checked 2026-10-18)

IMPORTANT

Ensure the following ENV vars are available before running the examples

2.46.1 Examples ​

bash
# change your working directory to this example first
cd examples/substrate/use-sft

# export all required environments
export CALLER_PRIVATE_KEY=

# creates new collection
pnpm call:createCollection

# update collection info
pnpm call:updateCollection

# create new token for a collection
pnpm call:createToken

# update token info
pnpm call:updateToken

# mint token(s)
pnpm call:mint

# transfer tokens(s)
pnpm call:transfer

2.46.2 Source files ​

src/createCollection.ts2.46.2.1 ​

ts
import { stringToHex } from "@polkadot/util";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

/**
 * Use `sft.createCollection` extrinsic to create a new SFT collection.
 *
 * Assumes the caller has XRP to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	const collectionName = "MyCollection";
	const collectionOwner = caller.address;
	const metadataScheme = stringToHex("https://example.com/token/");
	const royaltiesSchedule = {
		entitlements: [[collectionOwner, 10_000 /* one percent */]],
	};

	logger.info(
		{
			parameters: {
				collectionName,
				collectionOwner,
				metadataScheme,
				royaltiesSchedule,
			},
		},
		`create a "sft.createCollection" extrinsic`
	);
	const extrinsic = api.tx.sft.createCollection(
		collectionName,
		collectionOwner,
		metadataScheme,
		royaltiesSchedule
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [createEvent] = filterExtrinsicEvents(result.events, ["Sft.CollectionCreate"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				createEvent: formatEventData(createEvent.event),
			},
		},
		"receive result"
	);
});

src/createToken.ts2.46.2.2 ​

ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

const COLLECTION_ID = 269412;

/**
 * Use `sft.createToken` extrinsic to create a new token for a collection.
 *
 * Assumes the caller has XRP to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	const tokenName = "MyToken";
	const initialIssuance = 0;
	const maxIssuance = null;
	const tokenOwner = caller.address;
	const collectionId = COLLECTION_ID;

	logger.info(
		{
			parameters: {
				collectionId,
				tokenName,
				initialIssuance,
				maxIssuance,
				tokenOwner,
			},
		},
		`create a "sft.createToken" extrinsic`
	);

	const extrinsic = api.tx.sft.createToken(
		collectionId,
		tokenName,
		initialIssuance,
		maxIssuance,
		tokenOwner
	);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [createEvent] = filterExtrinsicEvents(result.events, ["Sft.TokenCreate"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				createEvent: formatEventData(createEvent.event),
			},
		},
		"receive result"
	);
});

src/mint.ts2.46.2.3 ​

ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

const COLLECTION_ID = 269412;
const TOKEN_ID = 0;

/**
 * Use `nft.mint` extrinsic to mint new tokens.
 *
 * Assumes the caller is the owner of the collection, and has XRP to pay for gas.
 */

withChainApi("porcini", async (api, caller, logger) => {
	const collectionId = COLLECTION_ID;
	const tokenId = TOKEN_ID;
	const tokenOwner = caller.address;
	const quantity = 10;
	const serialNumbers = [[tokenId, quantity]] as unknown as [[number, number]];

	logger.info(
		{
			parameters: {
				collectionId,
				serialNumbers,
				tokenOwner,
			},
		},
		`create a "sft.mint" extrinsic`
	);
	const extrinsic = api.tx.sft.mint(collectionId, serialNumbers, tokenOwner);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [mintEvent] = filterExtrinsicEvents(result.events, ["Sft.Mint"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				mintEvent: formatEventData(mintEvent.event),
			},
		},
		"receive result"
	);
});

src/transfer.ts2.46.2.4 ​

ts
import { BOB } from "@trne/utils/accounts";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

const COLLECTION_ID = 269412;
const TOKEN_ID = 0;

/**
 * Use `sft.transfer` extrinsic to transfer tokens to someone else.
 *
 * Assumes the caller is the owner of the transferring tokens, and has XRP to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	const collectionId = COLLECTION_ID;
	const tokenId = TOKEN_ID;
	const quantity = 1;
	const newOwner = BOB;
	const serialNumbers = [[tokenId, quantity]] as unknown as [[number, number]];

	logger.info(
		{
			parameters: {
				collectionId,
				serialNumbers,
				newOwner,
			},
		},
		`create a "sft.transfer" extrinsic`
	);
	const extrinsic = api.tx.sft.transfer(collectionId, serialNumbers, newOwner);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [transferEvent] = filterExtrinsicEvents(result.events, ["Sft.Transfer"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				transferEvent: formatEventData(transferEvent.event),
			},
		},
		"receive result"
	);
});

src/updateCollection.ts2.46.2.5 ​

ts
import { stringToHex } from "@polkadot/util";
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

const COLLECTION_ID = 269412;

/**
 * Use `sft.setBaseUri` extrinsic to update the collection `metadataSchheme`.
 *
 * Similar extrinsics are available to update different properties of a collection
 *  - setName
 *  - setOwner
 *  - setRoyaltiesSchedule
 *  Check out the pallet documentation (linked in README) for more details
 *
 * Assumes the caller is the owner of the collection, and has XRP to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	const baseUri = stringToHex("https://example.com/token/");
	const collectionId = COLLECTION_ID;

	logger.info(
		{
			parameters: {
				collectionId,
				baseUri,
			},
		},
		`create a "sft.setBaseUri" extrinsic`
	);
	const extrinsic = api.tx.sft.setBaseUri(collectionId, baseUri);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [setEvent] = filterExtrinsicEvents(result.events, ["Sft.BaseUriSet"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				setEvent: formatEventData(setEvent.event),
			},
		},
		"receive result"
	);
});

src/updateToken.ts2.46.2.6 ​

ts
import { filterExtrinsicEvents } from "@trne/utils/filterExtrinsicEvents";
import { formatEventData } from "@trne/utils/formatEventData";
import { sendExtrinsic } from "@trne/utils/sendExtrinsic";
import { withChainApi } from "@trne/utils/withChainApi";

const COLLECTION_ID = 269412;
const TOKEN_ID = 0;

/**
 * Use `sft.setMaxIssuance` extrinsic to update the token `maxIssuance`.
 *
 * Assumes the caller is the owner of the token, and has XRP to pay for gas.
 */
withChainApi("porcini", async (api, caller, logger) => {
	const maxIssuance = 1000;
	const tokenId = [COLLECTION_ID, TOKEN_ID] as [number, number];

	logger.info(
		{
			parameters: {
				tokenId,
				maxIssuance,
			},
		},
		`create a "sft.setMaxIssuance" extrinsic`
	);
	const extrinsic = api.tx.sft.setMaxIssuance(tokenId, maxIssuance);

	logger.info(`dispatch extrinsic from caller="${caller.address}"`);
	const { result, extrinsicId } = await sendExtrinsic(extrinsic, caller, { log: logger });
	const [setEvent] = filterExtrinsicEvents(result.events, ["Sft.MaxIssuanceSet"]);

	logger.info(
		{
			result: {
				extrinsicId,
				blockNumber: result.blockNumber,
				setEvent: formatEventData(setEvent.event),
			},
		},
		"receive result"
	);
});

package.json2.46.2.7 ​

json
{
  "scripts": {
    "call": "tsx --tsconfig ../../../tsconfig.json",
    "call:createCollection": "pnpm call src/createCollection.ts",
    "call:updateCollection": "pnpm call src/updateCollection.ts",
    "call:createToken": "pnpm call src/createToken.ts",
    "call:updateToken": "pnpm call src/updateToken.ts",
    "call:mint": "pnpm call src/mint.ts",
    "call:transfer": "pnpm call src/transfer.ts"
  }
}
Source: futureversecom/trn-examples, examples/substrate/use-sft (deleted) · commit 44c9e16, 2024-10-31 · Apache-2.0, license · part of Code examples

Associated docs

Where else you might go from here. Following one is optional — this chapter stands without them.

Back matter — read in order
See also — The whole of Back matter as one continuous page, reprinted from its chapters without change.
Code examples
See also — Futureverse’s code examples for The Root Network: scripts for the native API and the EVM, and two contract projects, each with its README and source.
Contents
See also — Every chapter in this book, each with the one line that tells you what you get by reading it.
Front matter — read in order
See also — The whole of Front matter as one continuous page, reprinted from its chapters without change.
Guides for the code examples
See also — How Futureverse’s TRN code examples get a test account: create one in MetaMask and fund it from the Porcini faucet.
Index · B
See also — 302 index terms that start with B, each linked to the pages that define or use it.
Index · C
See also — 550 index terms that start with C, each linked to the pages that define or use it.
Index · M
See also — 492 index terms that start with M, each linked to the pages that define or use it.

Curated independently and source-attributed. Not affiliated with Futureverse or TRN Labs. Sources

Curated independently and source-attributed. Not affiliated with Futureverse or TRN Labs.