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 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 IxAssetCreate
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.
- Code
- Output
const createAsset = async() => {
// Submit the IxAssetCreate Interaction and await the response
const response = await wallet.sendInteraction({
type: IxType.ASSET_CREATE,
fuel_price: 1,
fuel_limit: 200,
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)
}
// Console Output
Interaction Hash: 0x06a9aa4aa11045868364957107a53e27964025914de5120c613f4ab30342c437
Interaction Receipt: {
ix_type: '0x3',
ix_hash: '0x06a9aa4aa11045868364957107a53e27964025914de5120c613f4ab30342c437',
status: 0,
fuel_used: '0x64',
hashes: [
{
address: '0x778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9',
state_hash: '0x0641394e80961eb13c836ad246a2e50978ecdda18b82d1b97afe116a37b02cbc',
context_hash: '0x5533c24db2639275d6e3170397887bce0c7378deb969d94a7f3286f7a5aeb2c8'
},
{
address: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
state_hash: '0xf0231db034f5183b521f0f71285e2f9379d6114bededa9040641af8f84f3840d',
context_hash: '0xa488de70c44e53a1a5b18407bfdac418813b410626aea1252bdf1fb2ba2ecacd'
},
{
address: '0xa6ba9853f131679d00da0f033516a2efe9cd53c3d54e1f9a6e60e9077e9f9384',
state_hash: '0x29330e2610f25185bf480686ef48d961f787ea8c5edde257723e6c279975bad5',
context_hash: '0xe678f9762ee2ebedf28bc75341c7af9e94415659c66f07f95c4f3262bcd8cb4e'
}
],
extra_data: {
asset_id: '0x00000001778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9',
address: '0x778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9'
},
from: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
to: '0x778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9',
ix_index: '0x0',
parts: [
{
address: '0x778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9',
hash: '0xca44ee30038ee65a3501a15e7c8e1d78f220e8075cfccadf8257bafd7d22a851',
height: '0x0'
},
{
address: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
hash: '0x097feffec3a32eea8633b45af7e04d5d9975ea19da365d486b724e0141f2871b',
height: '0x2'
},
{
address: '0xa6ba9853f131679d00da0f033516a2efe9cd53c3d54e1f9a6e60e9077e9f9384',
hash: '0xb81058531f3f8138e1e684a5a3ed6e05875764f4d4285ea9f4ed3d08126841d3',
height: '0x5'
}
]
}
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": "0x00000001778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9",
"address": "0x778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9"
}
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
- Code
- Output
const getAssetInfo = async() => {
// We get the Asset ID of CANDYLAND001 from the receipt of the Asset Create Interaction
const asset_id = "0x00000001778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9"
// Use the moi.AssetInfoByAssetID RPC to fetch Asset Metadata
const asset_info = await provider.getAssetInfoByAssetID(asset_id)
console.log("Asset Metadata: ", asset_info)
}
// Console Output
Asset Metadata: {
dimension: '0x0',
is_logical: false,
is_stateful: false,
operator: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
standard: '0x1',
supply: '0x1',
symbol: 'CANDYLAND001'
}
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 = "0x00000001778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9"
// 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 IxAssetTransfer
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:
- Code
- Output
const transferAssets = async() => {
// Create a Map of Asset ID to the transfer amount
const transferValues = new Map()
transferValues.set("0x00000001778549058becea5d34d6554ace488a9f63707c38f39e3d24b42f43453f3d83b9", 1)
// Submit the IxAssetTransfer Interaction and await the response
const response = await wallet.sendInteraction({
type: IxType.VALUE_TRANSFER,
fuel_price: 1,
fuel_limit: 200,
receiver: "0x6ef7715969a7a99edf06957e94494e40203b4be2b0ffb325196677a82de6fcb6",
transfer_values: transferValues,
})
// 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)
}
// Console Output
Interaction Hash: 0x1d30bc389fb3daf6ce2a27eb429d592a3d68af2424d72a1e1f71c46bafa9ca2f
Interaction Receipt: {
ix_type: '0x1',
ix_hash: '0x1d30bc389fb3daf6ce2a27eb429d592a3d68af2424d72a1e1f71c46bafa9ca2f',
status: 0,
fuel_used: '0x64',
hashes: [
{
address: '0x6ef7715969a7a99edf06957e94494e40203b4be2b0ffb325196677a82de6fcb6',
state_hash: '0xc5c6eccdcd265b27f211c2e126c5f535f7b29e461ee05aaa1d401cb1e9c49ead',
context_hash: '0x70a5b5d6f3229f62f38e424bf8196b2fd36905d851b318960d336f1fbf1a9850'
},
{
address: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
state_hash: '0x3cde3c79256e0f1330459da1e5649491bf06ab8abfb3b572e306c664ad0717b8',
context_hash: '0x9bf4b1b100903946c3f8ff5df795cb502bc3e47933b8efb636f4f5130205ef5c'
}
],
extra_data: null,
from: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
to: '0x6ef7715969a7a99edf06957e94494e40203b4be2b0ffb325196677a82de6fcb6',
ix_index: '0x0',
parts: [
{
address: '0x6ef7715969a7a99edf06957e94494e40203b4be2b0ffb325196677a82de6fcb6',
hash: '0xef37ffa1060f819f145d3ca675d37e7e8c5c95764fea38c6798329eb836b734d',
height: '0x1'
},
{
address: '0x9c6cc5cccfe2a3dc447bce25f88cb28ce142459057e6c04b59911b6d3f10930a',
hash: '0x9781f58b0c3c59714089c7b70d0a4494a27e35ddc3b8f11f6212de3eda79935d',
height: '0x3'
}
]
}
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
IxAssetCreate
Interaction.
- Code
- Output
const createAsset = async() => {
// Submit the IxAssetCreate Interaction and wait for the response
const response = await wallet.sendInteraction({
type: IxType.ASSET_CREATE,
fuel_price: 1,
fuel_limit: 200,
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.extra_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)
}
// Console Output
Asset Created: 0x000000007271777f1d93fbfadf53de3549899f0beb8ba089ebee4efc46d8fcd8a05d6dc9
Asset Supply: 0x5f5e100 // 100,000,000
Minting New Tokens (Increasing the Supply)
We will now increase the supply of MYTOKEN by 10,000 tokens using the IxAssetMint
Interaction and
verify that the supply has adjusted accordingly.
- Code
- Output
const mintAsset = async() => {
const response = await wallet.sendInteraction({
type: IxType.ASSET_MINT,
fuel_price: 1,
fuel_limit: 200,
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.extra_data.total_supply
console.log("New Supply: ", new_supply)
}
// Console Output
New Supply: 0x5f60810 // 100,010,000
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 IxAssetBurn
Interaction and verify that the supply adjusted accordingly.
- Code
- Output
const burnAsset = async() => {
const response = await wallet.sendInteraction({
type: IxType.ASSET_BURN,
fuel_price: 1,
fuel_limit: 200,
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.extra_data.total_supply
console.log("New Supply: ", new_supply)
}
// Console Output
New Supply: 0x5f48170 // 99,910,000