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

如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!