Craft, burn and transfer assets
Crafting, burning and transferring assets are important gameplay mechanisms.
Not only do they enhance player engagement and satisfaction, they allow you to manage and regulate your game economy.


- If you'd like to test the transfer and burn functionality below with your own wallet, it will need to contain NFTs. Learn how to create and deploy assets to your wallet here.
- Understand the terms "crafting", "burning" and "transferring" of assets in our concepts page.
Setup
Prerequisites
The Immutable SDK requires Node v18 (Active LTS version) or higher. Node v18 can be installed via nvm
which allows to quickly install and use different versions of node via the command line.
The installation steps for nvm
for Linux, Mac, and Windows Install & Update Script. You are now ready to install the Node LTS version:
nvm install --lts
Install the Immutable SDK
Run the following command in your project root directory.
- npm
- yarn
npm install -D @imtbl/sdk
yarn add --dev @imtbl/sdk
The Immutable SDK is still in early development. Should complications arise during the installation, please use the following commands to ensure the most recent release of the SDK is correctly installed:
- npm
- yarn
rm -Rf node_modules
npm cache clean --force
npm i
rm -Rf node_modules
yarn cache clean
yarn
Dependencies
- npm
- yarn
npm install -D typescript ts-node
yarn add --dev typescript ts-node
Transfer or burn assets
You might want to enable the transfer of assets in your application. This can be between users or between the application wallet and users (i.e. distributing newly minted assets to users or receiving assets from users).
An asset transfer simply involves transferring the ownership of an asset from one wallet to another, and must be authorised by the sending wallet. This transaction requires a small amount of gas, which is paid by the transferring wallet.
Why "burn" assets?
Games and applications may want to take tokens out of circulation to balance the economy or to decrease the supply of a particular asset for some reason.
The mechanism for doing this is called "burning" and it simply means transferring a token to a null wallet address (0x0000000000000000000000000000000000000000
). (This null address is also the address from which a new token is minted - sender
or from
variable).
1. Create signer from user wallet
A signer is a representation of a user's wallet (account) that is used to authorise transactions. These transactions typically involve transferring the ownership of assets (currencies or NFTs) from one account to another, hence why the user is required to "sign" these transactions. Applications use signers to obtain user signatures to execute payments, asset transfers, and more. For more information, see the Signers definition from the ethers
library.
- Passport
- Browser
- Private key
To initialise the passport
module, please see the Passport Setup guide.
const passportWallet = passport.connectEvm();
import {ethers} from "ethers"; // ethers v5
import CodeBlock from '@theme/CodeBlock';
const browserWallet = new ethers.providers.Web3Provider(window.ethereum);
Then, create a signer from the wallet connection:
const signer = wallet.getSigner();
import { getDefaultProvider, Wallet } from "ethers"; // ethers v5
const rpcProvider = getDefaultProvider("https://rpc.testnet.immutable.com/")
const walletFromPrivateKey = new Wallet(PRIVATE_KEY, rpcProvider);
Then, create a signer from the wallet connection:
const signer = wallet.getSigner();
2. Transfer NFT
To transfer an asset, you first create a contract instance then call a transfer method on it.
Creating a contract instance is the initial step to interacting with smart contracts. This establishes a connection between your application and the contract's address on the network which allows you execute contract functions.
The following are two methods of facilitating asset transfers:
- Immutable SDK - Use the
ERC721
class, which simplifies methods like token transfers and minting operations with predefined functions. - Ethers.js - Directly interface with the contract's ABI, which provides flexibility and control over method calls.
The following parameters will be required:
Parameter | Description |
---|---|
signer | From the step above |
CONTRACT_ADDRESS | The address of the contract |
RECIPIENT | Address of the wallet that will be the new owner of the NFT when the transfer is completed |
TOKEN_ID | Unique ID of the NFT |
- Immutable SDK
- Ethers.js
import { Signer } from 'ethers'; // ethers v5
import { ERC721Client } from '@imtbl/zkevm-contracts';
const contractInstance = () => {
const CONTRACT_ADDRESS = '';
return new ERC721Client(CONTRACT_ADDRESS);
};
const transfer = async (signer: Signer) => {
const RECIPIENT = '';
const TOKEN_ID = '';
const sender = await signer.getAddress();
const contract = contractInstance();
const populatedTransaction = await contract[
'populateSafeTransferFrom(address,address,uint256)'
](sender, RECIPIENT, TOKEN_ID);
// If burning asset:
// const populatedTransaction = await contract.populateBurn(TOKEN_ID);
const transaction = await signer.sendTransaction(populatedTransaction);
return transaction;
};
import { ethers, Signer } from 'ethers'; // ethers v5
const contractInstance = async (signer: Signer) => {
const CONTRACT_ADDRESS = '';
return new ethers.Contract(
CONTRACT_ADDRESS,
[
'function safeTransferFrom(address from, address to, uint256 tokenId)',
],
signer,
);
};
const transfer = async (signer: Signer) => {
const RECIPIENT = '';
const TOKEN_ID = '';
const sender = await signer.getAddress()
const contract = await contractInstance(signer);
const transaction = contract.safeTransferFrom(sender, RECIPIENT, TOKEN_ID);
return transaction.wait();
}
Under the hood, ethers
will build a eth_sendTransaction
RPC call to the Passport provider:
{
"data": "0x42842e0e...the rest of encoded the data",
"to": "<address of the ERC-721 contract>",
"from": "<your wallet address>"
}
3. Transfer Multiple NFTs (Batch Transfers)
The preset ERC721 contract includes functionality for transferring multiple NFTs in one transaction. Batch transfers, like batch minting, are more gas efficient than sending multiple single transfer requests.
The following are two methods of facilitating asset transfers:
- Immutable SDK - Use the
ERC721
class, which simplifies methods like token transfers and minting operations with predefined functions. - Ethers.js - Directly interface with the contract's ABI, which provides flexibility and control over method calls.
The following parameters will be required:
Parameter | Description |
---|---|
signer | From the step above |
CONTRACT_ADDRESS | The address of the contract |
RECIPIENT | Address of the wallet that will be the new owner of the NFT when the transfer is completed |
TOKEN_IDS | Unique IDs of the NFTs to transfer |
- Immutable SDK
import { Signer } from 'ethers'; // ethers v5
import { ERC721Client } from '@imtbl/zkevm-contracts';
const contractInstance = () => {
const CONTRACT_ADDRESS = '';
return new ERC721Client(CONTRACT_ADDRESS);
};
const batchTransfer = async (signer: Signer) => {
const RECIPIENTS = ['', ''];
const TOKEN_IDS = ['', ''];
const sender = await signer.getAddress();
const contract = contractInstance();
const transfers = {
from: sender,
tos: RECIPIENTS,
tokenIds: TOKEN_IDS,
};
const populatedTransaction = await contract.populateSafeTransferFromBatch(
transfers
);
const transaction = await signer.sendTransaction(populatedTransaction);
return transaction;
};
4. Burn NFTs
The following parameters will be required to burn an asset:
Burning multiple assets in a single transaction, like batch minting, is more gas efficient than sending multiple single burn requests. This powerful feature enables game studios to reduce their operating costs when performing actions like crafting that burn multiple assets at the same time.
Parameter | Description |
---|---|
signer | From the step above |
CONTRACT_ADDRESS | The address of the contract |
TOKEN_IDS | Unique IDs of the NFTs to burn |
- Immutable SDK
import { Signer } from 'ethers'; // ethers v5
import { ERC721Client } from '@imtbl/zkevm-contracts';
const contractInstance = () => {
const CONTRACT_ADDRESS = '';
return new ERC721Client(CONTRACT_ADDRESS);
};
const transfer = async (signer: Signer) => {
const TOKEN_IDS = ['', ''];
const sender = await signer.getAddress();
const contract = contractInstance();
const populatedTransaction = await contract.populateBurnBatch(TOKEN_IDS);
const transaction = await signer.sendTransaction(populatedTransaction);
return transaction;
};
Obtain type safety using the TypeChain library
Constructing the contract interface using the ABI is not type-safe. To make it type-safe, we can use Typechain to generate typescript interfaces from the contract ABI.
The contract ABI could be stored or exported to a file and then used to generate the typescript interfaces.
typechain --target=ethers-v5 -out-dir=app/contracts abis/ERC721.json
Here's how you create a contract instance to use type-safe methods. This function returns a contract factory on which you can call the safeTransferFrom
method:
import { Signer } from 'ethers'; // ethers v5
import { ERC721_factory, ERC721 } from './contracts';
const contractInstance = async (signer: Signer) => {
const CONTRACT_ADDRESS = '';
const contract: ERC721 = ERC721_factory.connect(
CONTRACT_ADDRESS,
signer,
);
return contract;
// You can call the `safeTransferFrom` method on this contract:
// contract.safeTransferFrom(sender, RECIPIENT, TOKEN_ID);
};
Craft assets
Crafting assets is a broad term for the process of combining various in-game resources or items to create new and unique items or equipment. The benefits of enabling this in your game includes enriching players' gaming experience, providing additional gameplay mechanisms, and fostering a dynamic and interconnected gaming ecosystem.
At its core, crafting consists of:
- Receiving existing assets from users
- Destroying, or locking the use, of the assets
- Creating new assets with different characteristics (i.e. metadata) to replace the old assets
- Distributing the new assets to users
An example of how a crafting mechanism could be implemented is through the use of a specialised NFT contract that contains a crafting function. Please note, however, that this is just an example and it is up to you how you would like to implement crafting in your game on the basis of understanding the transfer and burn mechanisms above.