ERC20和ERC721都是单代币标准,一个合约中只能管理一种代币。而ERC1155的设计目标是统一管理同质化代币(FungibleToken)和非同质化代币(Non-FungibleToken),提高批量操作的效率,减少Gas成本:https://learnblockchain.cn/shawn_
ERC20
和 ERC721
都是单代币标准,一个合约中只能管理一种代币。而 ERC1155
的设计目标是统一管理 同质化代币(Fungible Token
) 和 非同质化代币(Non-Fungible Token
),提高批量操作的效率,减少 Gas
成本。
在 ERC721
中,每个代币都有唯一的 tokenId
,但一个合约中只能支持这一种代币。而在 ERC1155
中,一个合约可以有多种代币,甚至可以同质化代币和非同质化代币共同存在。在诸如区块链游戏的场景中,管理多种物品(装备、金币)等变得更为高效和节省 gas
。并且,ERC1155
支持批量转账的功能。
要知道 ERC1155
是怎么实现管理多种代币,通过对比 ERC721
和 ERC1155
的核心存储结构就可以很容易得知。
ERC721
中,核心存储 NFT
的结构为这一个 mapping
,通过记录某个 tokenId
被哪个地址所拥有,便可标识出这个 NFT
是归属谁的。
mapping(uint256 tokenId => address) private _owners;
ERC1155
中,核心数据结构也是一个 mapping
,但这是一个嵌套的 mapping
。其结构为 tokenId --> 地址 --> 数量
。
通过对某个 tokenId
归属进行标记,如果是同质化代币,数量可以大于 1
,如果是非同质化代币(NFT
),则数量固定为 1
。这样就起到了同时管理同质化代币和非同质化代币的作用。id
为 0
。被地址 0xbb
和 0xcc
持有。数量分别为 2
和 3
。则这个 mapping
中,一个 tokenId
则对应了两个子 mapping
,存储了两个地址的持仓数量。非同质化代币:
例如一个非同质化代币,其 id
为 1
。被 0xdd
持有。因为其数量固定为 1
,所以在这个 mapping
中,只能同时存在一个子 mapping
,记录了 0xdd
持有一个的记录。
mapping(uint256 id => mapping(address account => uint256)) private _balances;
ERC1155
的核心状态变量非常简单,就是一个上面提到的核心存储结构加上一个额外的 mapping
作为授权机制,来保存代币授权给谁的信息。其中 _uri
保存了所有代币的元数据,可以通过 {id}
插值来进行区分。
mapping(uint256 id => mapping(address account => uint256)) private _balances;
mapping(address account => mapping(address operator => bool)) private _operatorApprovals;
// Used as the URI for all token types by relying on ID substitution, e.g. https://token-cdn-domain/{id}.json
string private _uri;
ERC1155
的接口及解释如下。
ERC1155
继承了 ERC165
,提供接口检测的能力。ERC721
类似,safeTransferFrom
的时候也要求对方实现 ERC1155Receiver
接口来避免代币锁定的情况出现。(ERC1155Receiver
可以看做 ERC165
的抽离处理,单独拎出来让别的合约调用时判定,并同时完成发送代币的过程) /**
* @dev 返回某个 tokenId 被某个地址拥有的数量
*/
function balanceOf(address account, uint256 id) external view returns (uint256);
/**
批量返回 ids 和 accounts 对应的余额
*/
function balanceOfBatch(
address[] calldata accounts,
uint256[] calldata ids
) external view returns (uint256[] memory);
/**
给 operator 授权调用者地址的所有代币操作权限
本质上是操作 mapping(address account => mapping(address operator => bool)) 来记录
*/
function setApprovalForAll(address operator, bool approved) external;
/**
查询 account 的所有代币授权情况
*/
function isApprovedForAll(address account, address operator) external view returns (bool);
/**
安全转账,代币 id,从 from 地址转账到 to 地址 value 的数量
要求对方实现了 IERC1155Receiver 的接口,否则会失败回滚(防止发生代币锁定的危险)
*/
function safeTransferFrom(address from, address to, uint256 id, uint256 value, bytes calldata data) external;
/**
安全批量转账,代币 ids[],从 from 地址转账到 to 地址 values[] 的数量
要求对方实现了 IERC1155Receiver 的接口,否则会失败回滚(防止发生代币锁定的危险)
*/
function safeBatchTransferFrom(
address from,
address to,
uint256[] calldata ids,
uint256[] calldata values,
bytes calldata data
) external;
ERC1155
定义的事件非常简单,只有三个。分别对应转账、批量转账、授权全部代币的场景。
/**
单个转账事件
*/
event TransferSingle(address indexed operator, address indexed from, address indexed to, uint256 id, uint256 value);
/**
转账事件
*/
event TransferBatch(
address indexed operator,
address indexed from,
address indexed to,
uint256[] ids,
uint256[] values
);
/**
批量授权事件
*/
event ApprovalForAll(address indexed account, address indexed operator, bool approved);
下面,我们就来进行手搓一个 ERC1155
合约并上线到 holesky
测试网。
Shawn1155.sol
代码编写contract Shawn1155 is ERC1155 {
uint256 constant MAX_ID = 10;
constructor() ERC1155("ipfs://QmeSjSinHpPnmXmspMjwiXyN6zS4E9zccariGR3jxcaWtq/ "){
// mint 出来给自己
mintBatch();
}
// 批量 mint 出来
function mintBatch() internal {
uint256[] memory ids = new uint256[](MAX_ID);
uint256[] memory amounts = new uint256[](MAX_ID);
for(uint256 i=0;i<MAX_ID;i++){
ids[i] = i;
amounts[i] = 1000;
}
_mintBatch(msg.sender,ids,amounts,"");
}
}
Shawn1155.s.sol
部署脚本contract DeployAndTransfer is Script {
function run() external {
vm.startBroadcast();
// 1. 部署合约
Shawn1155 token = new Shawn1155();
console2.log("Deployed Shawn1155 at:", address(token));
// 2. 生成一个随机地址(模拟 EOA 接收者)
address randomEOA = address(uint160(uint256(keccak256(abi.encodePacked(block.timestamp, blockhash(block.number - 1))))));
console2.log("Random receiver:", randomEOA);
// 3. 发送 tokenId 1,数量 100 给 randomEOA
token.safeTransferFrom(msg.sender, randomEOA, 1, 100, "");
vm.stopBroadcast();
}
}
source .env
forge script script/ERC1155/Shawn1155.s.sol \
--rpc-url $HOLESKY_RPC_URL \
--private-key $PRIVATE_KEY \
--broadcast \
-vvvv
到holesky上查看 https://holesky.etherscan.io/address/0x6E6bC0328814Ed87396ACea53EB4363A5eA236DC#events
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!