How to build and use XRC-1155 tokens
An intro to the new standard for having many tokens in one
XRC-1155 allows you to send multiple different token classes in one transaction. You can imagine it as transferring Chinese Yuan and US Dollars in a single transfer. XRC-1155 can be used in games, but there are also other many more use cases for it.
What is XRC-1155?
The key feature introduced by XRC-1155 is multi-token support. Let’s say we are in a world where each airline has its own token. In a way that’s not too far from reality where each airline sits in a different country with its own national currency. Now let’s say I wanted to book a flight across the world with three connecting flights from three different airlines. To pay for my flight ticket, I would need to make three separate transactions and pay for each individually.
Many things could go wrong this way. What if I pay for flight 1, but then flight 2 is already booked out? Now I have to find new connections. In the real world with centralized currencies, this job is done by the online travel agency (OTA) or a more old-school traditional agency. In our example though, one would have to create a new smart contract that does all three trades in a row, deploy it and then call the trade function. A bit complicated to only pay for one flight trip, isn’t it?
What if instead, we created a single multi-token contract that holds the individual tokens for every airline? That’s exactly what XRC-1155 allows you to do. Instead of just one token class, we can add as many as we want. Let’s look at the functionality in more depth.
XRC-1155 Functions and Features:
- Batch Transfer: Transfer multiple assets in a single call.
- Batch Balance: Get the balances of multiple assets in a single call.
- Batch Approval: Approve all tokens to an address.
- XIP-165 Support: Declare supported interfaces.
- Hooks: Receive tokens hook.
- NFT support: If supply is only 1, treat it as NFT.
- Safe Transfer Rules: Set of rules for secure transfer.
1. Batch Transfer
The batch transfer works very similarly to regular ERC-20 transfers. Let’s look at the regular XRC-20 transferFrom function:
// XRC-20function transferFrom(address from, address to, uint256 value) external returns (bool);
Now let’s have a look at XRC-1155 transferFrom function:
// XRC-1155ifunction safeBatchTransferFrom(address _from,address _to,uint256 calldata _ids,uint256 calldata _values,bytes calldata _data) external;
The only difference in XRC-1155 is that we pass the values as an array and we also pass an array of id’s. For example given _ids=[3, 6, 13] and _values=[100, 200, 5], the resulting transfers will be
- Transfer 100 tokens with id 3 from _from to _to.
- Transfer 200 tokens with id 6 from _from to _to.
- Transfer 5 tokens with id 13 from _from to _to.
In XRC-1155 we only have transferFrom, no transfer. To use it as a regular transfer, just set the from address to the address that’s calling the function.
- Why is it called ‘safe’-BatchTransferFrom? — See section 7 below about safe transfer rules.
- Why is it not returning boolean? — The transaction reverts on failure
- What is the bytes data field? — Just as in XRC-777, you can pass arbitrary data along with the call which will also be passed to the receive hook.
2. Batch Balance
The respective XRC-20 balanceOf call likewise has its partner function with batch support. As a reminder, this is the XRC-20 version: Let’s look at the regular XRC-20 balanceOf call function:
// XRC-20function balanceOf(address owner) external view returns (uint256);
Now let’s have a look at the XRC-1155 balanceOf call function:
// XRC-1155function balanceOfBatch(address calldata _owners,uint256 calldata _ids) external view returns (uint256 memory);
Even simpler for the balance call, we can retrieve multiple balances in a single call. We pass the array of owners, followed by the array of token ids.
For example given _ids=[3, 6, 13] and _owners=[xdcbeef…, xdc1337…, xdc1111…], the return value will be
3. Batch Approval
The approvals are slightly different from XRC-20. Instead of approving specific amounts, you simply set an operator to approved or not approved via setApprovalForAll.
// XRC-1155function setApprovalForAll(address _operator,bool _approved) external;
// XRC-1155function isApprovedForAll(address _owner,address _operator) external view returns (bool);
Reading the current status can be done via isApprovedForAll. As you can see, it’s an all or nothing. You cannot define how many tokens to approve or even which token class.
This is intentionally designed with simplicity in mind. You can only approve everything for one address.
4. XIP-165 Support
If you don’t know what XIP-165 is In short, it’s a way for smart contracts to define what interfaces they support. So for example, does the smart contract support receiving XRC-1155 tokens? If it does, the respective supports interface function must exist and return true for that contract.
This brings us directly to the next point: hooks.
5. Receive Hook
Given the XIP-165 support, XRC-1155 supports receive hooks for smart contracts only. The hook function must return a magic predefined bytes4 value which is given as:
address _operator,address _from,uint256 calldata _ids,uint256 calldata _values,bytes calldata _data) external returns(bytes4);
When the receiving contract returns this value, it is assumed the contract accepts the transfer and knows how to handle the XRC-1155 tokens. Great, no more stuck tokens in a contract!
6. NFT Support
When a supply is given as just one, the token is essentially a non-fungible token (NFT). And as is standard for ERC-721, you can define a metadata URL. The URL can be read and modified by clients.
7. Safe Transfer Rules
Now let’s have a look at the most important function of the rules:
- The caller must be approved to spend the tokens for the _from address or the caller must equal _from.
- The transfer call must revert if
- _to address is 0.
- length of _ids is not the same as the length of _values.
- any of the balance(s) of the holder(s) for token(s) in _ids is lower than the respective amount(s) in _values sent to the recipient.
- any other error occurs.
Note: All batch functions including the hook also exist as versions without batch. This is done for gas efficiency, considering transferring just one asset will likely still be the most commonly used way. We’ve left them out for simplicity in the explanations, incl. safe transfer rules. The names are identical, just remove the ‘Batch’.
In the meantime, if you have any questions about XRC tokens types or you are a DeFi project and want to know more about how to migrate your project to XinFin Network you can always contact me via Telegram @Ruslan_wing