Since ROOT token is a security token, it's managed by the balances pallet. To transfer ROOT tokens, use the following snippet:
// transfer 1 ROOT to Aliceawaitapi.balances.transfer(1_000_000,"0xE04CC55ebEE1cBCE552f250e85c57B70B2E2625b").signAndSend(...);
Note: ROOT token has 6 decimals.
How do I transfer XRP or any other fungible tokens?
XRP and any other non-default fungible tokens are managed by the assets pallet, separate from the balances pallet. That also means you need to provide the asset ID when you want to transfer any of these tokens.
// get the `metadata` to determine the `decimals`constXRP_ID=2;constmetadata= (awaitapi.query.assets.metadata(XRP_ID)).toJson();constoneXRP=1*Math.pow(10,metadata.decimals);// transfer 1 XRP to Aliceawaitapi.assets.transfer(XRP_ID,"0xE04CC55ebEE1cBCE552f250e85c57B70B2E2625b", oneXRP).signAndSend(...);
Tip: To retrieve a list of available tokens and their asset IDs, checkout Tokens section in our Block Explorer.
NFT (Non-Fungible Token)
SFT (Semi-Fungible Token)
Primitives
Block
How do I listen to new blocks?
The example below shows how to subscribe to new blocks. It displays the block number every time a new block is seen by the node you are connected to.
// Import the APIasyncfunctionmain() {// Instantiate the API...// We only display a couple, then unsubscribelet count =0;// Subscribe to the new headers on-chain. The callback is fired when new headers// are found, the call itself returns a promise with a subscription that can be// used to unsubscribe from the newHead subscriptionconstunsubscribe=awaitapi.rpc.chain.subscribeNewHeads((header) => {console.log(`Chain is at block: #${header.number}`);if (++count ===256) {unsubscribe();process.exit(0); } });}main().catch(console.error);
How do I retrieve the header/extrinsic hash from blocks?
A block hash refers to the hash over the header, the extrinsic hash refers to the hash of the encoded extrinsic. Since all objects returned by the API implement the .hash => Hash getter, we can simply use this to view the actual hash.
// returns HashconstblockHash=awaitapi.rpc.chain.getBlockHash(blockNumber);// returns SignedBlockconstsignedBlock=awaitapi.rpc.chain.getBlock(blockHash);// the hash for the block, always via header (Hash -> toHex()) - will be// the same as blockHash above (also available on any header retrieved,// subscription or once-off)console.log(signedBlock.block.header.hash.toHex());// the hash for each extrinsic in the blocksignedBlock.block.extrinsics.forEach((ex, index) => {console.log(index,ex.hash.toHex());});
How do I extract the block author?
The block author is encoded inside the consensus logs for the block. To extract it, you need to decode the log (which the API does do) and then map the index of the validator to the list of session validators. This extraction is however available on the API derive for new head subscriptions, which returns an extended header with the author populated (assuming that the digest logs are known).
// subscribe to all new headers (with extended info)api.derive.chain.subscribeNewHeads((header) => {console.log(`#${header.number}: ${header.author}`);});
For a single header only, the derives also contain a getHeader, which once again returns a header extended with the author:
// retrieve the last header (hash optional)constheader=awaitapi.derive.chain.getHeader();console.log(`#${header.number}: ${header.author}`);
How do I view extrinsic information?
The transactions are included in a signed block as part of the extrinsics - some of these will be unsigned and generated by the block author and some of these may be submitted from external sources and be signed (some palettes use unsigned transactions, so signed/unsigned is not an indication of origin). To retrieve the block and display the transaction information, we can do the following:
// no blockHash is specified, so we retrieve the latestconstsignedBlock=awaitapi.rpc.chain.getBlock();// the information for each of the contained extrinsicssignedBlock.block.extrinsics.forEach((ex, index) => {// the extrinsics are decoded by the API, human-like viewconsole.log(index,ex.toHuman());const {isSigned,meta, method: { args,method,section }, } = ex;// explicit display of name, args & documentationconsole.log(`${section}.${method}(${args.map((a) =>a.toString()).join(", ")})`);console.log(meta.documentation.map((d) =>d.toString()).join("\n"));// signer/nonce infoif (isSigned) {console.log(`signer=${ex.signer.toString()}, nonce=${ex.nonce.toString()}`); }});
In the above example, .toHuman() is used to format the extrinsic into a human-readable representation. You can inspect/extract specific fields from the decoded extrinsic as required. For instance ex.method.section would return the pallet that executed this transaction.
How do I map extrinsics to their events?
While the blocks contain the extrinsics, the system event storage will include the events and the details needed to allow for a mapping between them. For events, the phase is an enum that would be isApplyExtrinsic with the index in the cases where it refers to an extrinsic in a block. This index maps through the order of the extrinsics as found.
To perform a mapping between the two, we need information from from both sources, as per the example below.
// no blockHash is specified, so we retrieve the latestconstsignedBlock=awaitapi.rpc.chain.getBlock();constapiAt=awaitapi.at(signedBlock.block.header.hash);constallRecords=awaitapiAt.query.system.events();// map between the extrinsics and eventssignedBlock.block.extrinsics.forEach(({ method: { method, section } }, index) => {// filter the specific events based on the phase and then the// index of our extrinsic in the blockconstevents= allRecords.filter(({ phase }) =>phase.isApplyExtrinsic &&phase.asApplyExtrinsic.eq(index)).map(({ event }) =>`${event.section}.${event.method}`);console.log(`${section}.${method}:: ${events.join(", ") ||"no events"}`);});
How do I determine if an extrinsic succeeded/failed?
The code below extends the above example, where extrinsics are mapped to their blocks. In this example, we will specifically look for specific extrinsic events—namely, the system.ExtrinsicSuccess and system.ExtrinsicFailed events. The same logic can be applied to inspect any other type of expected event.
// no blockHash is specified, so we retrieve the latestconstsignedBlock=awaitapi.rpc.chain.getBlock();// get the api and events at a specific blockconstapiAt=awaitapi.at(signedBlock.block.header.hash);constallRecords=awaitapiAt.query.system.events();// map between the extrinsics and eventssignedBlock.block.extrinsics.forEach(({ method: { method, section } }, index) => { allRecords// filter the specific events based on the phase and then the// index of our extrinsic in the block.filter(({ phase }) =>phase.isApplyExtrinsic &&phase.asApplyExtrinsic.eq(index))// test the events against the specific types we are looking for.forEach(({ event }) => {if (api.events.system.ExtrinsicSuccess.is(event)) {// extract the data for this event// (In TS, because of the guard above, these will be typed)const [dispatchInfo] =event.data;console.log(`${section}.${method}:: ExtrinsicSuccess:: ${JSON.stringify(dispatchInfo.toHuman())}` ); } elseif (api.events.system.ExtrinsicFailed.is(event)) {// extract the data for this eventconst [dispatchError,dispatchInfo] =event.data;let errorInfo;// decode the errorif (dispatchError.isModule) {// for module errors, we have the section indexed, lookup// (For specific known errors, we can also do a check against the// api.errors.<module>.<ErrorName>.is(dispatchError.asModule) guard)constdecoded=api.registry.findMetaError(dispatchError.asModule); errorInfo =`${decoded.section}.${decoded.name}`; } else {// Other, CannotLookup, BadOrigin, no extra info errorInfo =dispatchError.toString(); }console.log(`${section}.${method}:: ExtrinsicFailed:: ${errorInfo}`); } });});
Event
How do I subscribe to node events?
Query the system events and extract information from them.
asyncfunctionmain() {// Instantiate the API...// Subscribe to system events via storageapi.query.system.events((events) => {console.log(`\nReceived ${events.length} events:`);// Loop through the Vec<EventRecord>events.forEach((record) => {// Extract the phase, event and the event typesconst { event,phase } = record;consttypes=event.typeDef;// Show what we are busy withconsole.log(`\t${event.section}:${event.method}:: (phase=${phase.toString()})`);console.log(`\t\t${