Skip to main content

Create & Manage Assets in MOI

Prerequisites

To effectively utilise this guide, we recommend reading our guide on Submitting an Interaction and the documentation regarding, Interactions, Participants and Assets

Getting Started

This is a walkthrough of the different capabilities of Assets in MOI. We use the JS-MOI-SDK to submit Interactions and make RPC calls to the network. This can also be done directly with the JSON-RPC API or with MOI Voyage.

This walkthrough assumes that your code is already set up with VoyageProvider and that the wallet signer for sending Interactions is initialized. Refer to the section on Setting up JS-MOI-SDK.

Creating an Asset

We must first create an Asset in order to transfer and manage it. We can do this using the AssetCreate Interaction and specify the appropriate parameters and metadata. For the sake of this guide, we will first create an MAS1 Asset (Non-Fungible Token) with the symbol CANDYLAND001.

const createAsset = async() => {
// Submit the AssetCreate Interaction and await the response
const response = await wallet.sendInteraction({
fuel_price: 1,
fuel_limit: 200,
ix_operations: [
{
type: OpType.ASSET_CREATE,
payload: {
standard: AssetStandard.MAS1,
symbol: "CANDYLAND001",
supply: 1 // The supply for MAS1 Assets must always be 1
}
}
]
})

// Obtain the Interaction Hash and print it
const ixhash = response.hash;
console.log("Interaction Hash: ", ixhash)

// Poll for the Interaction Receipt and print it
const receipt = await response.wait()
console.log("Interaction Receipt: ", receipt)
}

Bingo! We have now successfully created an NFT called CANDYLAND001. We can see from the receipt that it has been created with the following address and Asset ID:

[
{
"asset_id": "0x0000000108ffe861df53c4c0c7d3cddeffd65070b90706d535a651bd64a6842bbded00b9",
"address": "0x08ffe861df53c4c0c7d3cddeffd65070b90706d535a651bd64a6842bbded00b9"
}
]

Retrieving Asset Information

Now that we have successfully created our Asset, let us retrieve some information about it with an RPC Call. We can also check that CANDYLAND001 is available in the spendable balances of the sender’s account.

Retrieving the Metadata of an Asset

const getAssetInfo = async() => {
// We get the Asset ID of CANDYLAND001 from the receipt of the Asset Create Interaction
const asset_id = "0x0000000108ffe861df53c4c0c7d3cddeffd65070b90706d535a651bd64a6842bbded00b9"

// Use the moi.AssetInfoByAssetID RPC to fetch Asset Metadata
const asset_info = await provider.getAssetInfoByAssetID(asset_id)
console.log("Asset Metadata: ", asset_info)
}

Retrieving the Balance of an Asset for a Participant

const getAssetBalance = async() => {
// We get the address of the participant who created the Asset,
// the initial supply of the Asset would have been credited to them
const address = await wallet.getAddress();
// We get the Asset ID of CANDYLAND001 from the receipt of the Asset Create Interaction
const asset_id = "0x0000000108ffe861df53c4c0c7d3cddeffd65070b90706d535a651bd64a6842bbded00b9"

// Use the moi.Balance RPC to fetch the balance of an Asset for a
// specific Asset ID. The moi.TDU RPC can be used to fetch all balances
const balance = await provider.getBalance(address, asset_id)
console.log("Asset Balance: ", balance)
}
// Console Output

Asset Balance: 1

Transferring an Asset between Participants

Now that we have a functional Asset, let's transfer this NFT to another Participant. MOI allows Participants to transfer multiple assets at once because all Assets are recognized natively in the account’s balances. While transferring assets with the AssetTransfer Interaction, only the token ownership moves between Participants, the operatorship of the Asset is not modified. The code for transferring the CANDYLAND001 Asset is as follows:

const transferAssets = async() => {
// Submit the AssetTransfer Interaction and await the response
const response = await wallet.sendInteraction({
fuel_price: 1,
fuel_limit: 200,
ix_operations: [
{
type: OpType.ASSET_TRANSFER,
payload: {
beneficiary: "0x6ef7715969a7a99edf06957e94494e40203b4be2b0ffb325196677a82de6fcb6",
asset_id: "0x0000000108ffe861df53c4c0c7d3cddeffd65070b90706d535a651bd64a6842bbded00b9",
amount: 1
}
}
]
})

// Obtain the Interaction Hash and print it
const ixhash = response.hash;
console.log("Interaction Hash: ", ixhash)

// Poll for the Interaction Receipt and print it
const receipt = await response.wait()
console.log("Interaction Receipt: ", receipt)
}

SUCCESS! We have successfully transferred the CANDYLAND001 Asset from the sender 0x9c6..30a to the receiver 0x6ef..cb6.

Transferring Operatorship of an Asset

When an Asset is created, the creator is marked as its operator. Only the operator can perform privileged actions such as modulating its supply. Currently, the operatorship of Assets is static and cannot be transferred, but this will change in the coming months.

Adjusting the Supply of an Asset

Supply Modulation of Assets involves the creation (minting) or destruction (burning) of tokens to manipulate its total supply. This action can only be performed on Assets of the MAS0 standard are also only capable of being performed by the operator of the Asset. Any minted or burned tokens are credited to or debited from the spendable balances of the operator.

The CANDYLAND001 Asset we have used until now does not fit support supply modulation. So we will now create another Asset of the MAS0 standard called MYTOKEN with a 100 million tokens as the initial supply with AssetCreate Interaction.

const createAsset = async() => {
// Submit the AssetCreate Interaction and wait for the response
const response = await wallet.sendInteraction({
fuel_price: 1,
fuel_limit: 200,
ix_operations: [
{
type: OpType.ASSET_CREATE,
payload: {
standard: AssetStandard.MAS0,
symbol: "MYTOKEN",
supply: 100000000
}
}
]
})

// Poll for the Interaction Receipt
const receipt = await response.wait()

// We get the Asset ID of MYTOKEN from the receipt and print it
const asset_id = receipt.ix_operations[0].data.asset_id
console.log("Asset Created: ", asset_id)

// Use the moi.AssetInfoByAssetID RPC to fetch Asset Metadata
const asset_info = await provider.getAssetInfoByAssetID(asset_id)
console.log("Asset Supply: ", asset_info.supply)
}

Minting New Tokens (Increasing the Supply)

We will now increase the supply of MYTOKEN by 10,000 tokens using the AssetMint Interaction and verify that the supply has adjusted accordingly.

const mintAsset = async() => {
const response = await wallet.sendInteraction({
fuel_price: 1,
fuel_limit: 200,
ix_operations: [
{
type: OpType.ASSET_MINT,
payload: {
asset_id: asset_id,
amount: 10000
}
}
]
})

// Poll for the Interaction Receipt
const receipt = await response.wait()


// We get the updated supply of MYTOKEN from the receipt and print it
const new_supply = receipt.ix_operations[0].data.total_supply
console.log("New Supply: ", new_supply)
}

Burning Existing Tokens (Decreasing the Supply)

The current supply of MYTOKEN is 100,010,00 tokens. We will now decrease the supply by 100,000 using the AssetBurn Interaction and verify that the supply adjusted accordingly.

const burnAsset = async() => {
const response = await wallet.sendInteraction({
fuel_price: 1,
fuel_limit: 200,
ix_operations: [
{
type: OpType.ASSET_BURN,
payload: {
asset_id: asset_id,
amount: 100000
}
}
]
})

// Poll for the Interaction Receipt
const receipt = await response.wait()

// We get the updated supply of MYTOKEN from the receipt and print it
const new_supply = receipt.ix_operations[0].data.total_supply
console.log("New Supply: ", new_supply)
}