EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
Smart Contract Developer

Subscribe to xyyme.eth
EIP-712 使用详解
之前的文章我们介绍过如何对数据进行签名,利用签名技术我们可以实现一些功能例如白名单校验等。但是这种签名技术的应用场景比较简单,一般就是给一串字符串,或者一串哈希签名,如果我们想为更复杂的数据签名就无法实现了。 EIP-712 的出现就是为了解决这个问题,利用 EIP-712,我们可以对更大的数据集,例如对结构体进行签名。那么这种签名格式有什么实际的应用场景呢。使用过 Uniswap,PancakeSwap 等 DEX 的朋友应该有印象,在移除 LP 流动性的时候,我们需要先签名,然后再发送一笔交易移除流动性。正常情况下,其实应该我们先调用 LP 代币的授权方法,授权 DEX 合约可以转移我们的 LP,然后再去移除流动性。而这种二合一的实现正是应用了 EIP-712。它帮助我们仅仅签名一次,就可以将两步交易合并为一步交易,从而节省 Gas 费用。这篇文章我们就来看看 EIP-712 到底是怎么使用的。基本结构EIP712Domain顾名思义,是一个与域相关的结构体,总共包含五个字段:name,合约或者协议的名称version,合约的版本chainId,合约部署的链 Id,一般使用 ...
流动性挖矿-合约原理详解
流动性挖矿应该是上个牛市最火热的内容,基本上整个 DeFi 都是在围绕着流动性挖矿展开的,今天我们就来看看它到底是什么以及合约代码层面是怎么实现的。流动性挖矿简介首先我们先从用户的角度来理解一下流动性挖矿是什么,实际上就是用户通过在合约中质押一个 token 从而赚取另一个 token 的过程。例如,SushiSwap 最初推出的 DEX 流动性挖矿,用户可以通过将 SushiSwap 的 LP token 质押到合约中赚取 Sushi token。那么这个奖励具体是怎么发放以及如何实现的呢?我们今天就来研究一下这部分内容。 先来看几个例子: 一:假设有一个流动性挖矿的合约,可以质押 A token 赚取 B token。它在 0 秒时开始活动,每秒奖励 R 个 B token。此时有用户 Alice 在第 3 秒时质押了 2 个 A token,并且之后没有其他人参与,在第 8 秒时取出 token,图示:那么他在此时获得的收益就是:5R = (2 / 2) * (8 - 3) * R 其中,第一个 2 是用户 A 质押的数量,第二个 2 是合约中质押的总量,(8-3)是用户 ...
CREATE2 操作码使用方法详解
CREATE2 是一个可以在合约中创建合约的操作码。我们先来举个例子看看它能干什么:这段代码是 Uniswap v2-core 里面的工厂合约代码,使用 create2 操作码创建了 pair 合约,返回值是 pair 的地址,这样就可以逻辑中直接使用其地址进行接下来的操作。 那么 create2 到底是怎么使用呢,根据官方 EIP 文档,create2 一共接收四个参数,分别是:endowment(创建合约时往合约中打的 ETH 数量)memory_start(代码在内存中的起始位置,一般固定为 add(bytecode, 0x20) )memory_length(代码长度,一般固定为 mload(bytecode) )salt(随机数盐)这里要注意的是第一个参数如果大于 0 的话,需要待部署合约的构造方法带有 payable。随机数盐是由用户自定,须为 bytes32 格式,例如在上面 Uniswap 的例子中,salt 为:bytes32 salt = keccak256(abi.encodePacked(token0, token1)); create2 还有一个优点,相...
Share Dialog
Share Dialog
<100 subscribers
<100 subscribers
最近 ERC-6551 的热度很高,我们就来聊聊 ERC-6551 到底是什么。这篇文章我想从它的原理,代码实现,以及应用案例来讲解 6551。相信看完这篇文章后大家应该会对 6551 有一个比较全面的认识。
简单来说,6551 是一个为 NFT 创建钱包的标准。这是什么意思呢?
我们考虑一个场景,假设在游戏中,我的地址 A 拥有一个角色 Bob,这个角色它本身是一个 ERC721 的 NFT,同时它的身上也有许多道具,例如帽子,鞋子,武器等,也可能拥有一些资产例如金元宝等。这些资产本身也是 ERC20,ERC721 等类型的 token。这些道具和资产在游戏逻辑中是属于我的角色 Bob 的,但是在实际的底层合约实现中,其实它们都是属于我的地址 A 的。如果我想将我的角色 Bob 出售给别人,我就需要分别将 Bob 还有它所拥有的所有资产都一一转账给买家。这在逻辑和操作上都不是很合理。
6551 标准的目的就是为角色 Bob 创建一个钱包,使得它所拥有的道具都是属于角色 Bob 的,这样看起来就合理多了。我们可以看看官方 EIP 里给到的示例图:

用户账户中拥有两个 NFT,分别是 A#123 和 B#456,其中 A#123 拥有两个账户( A 和 B),B#456 拥有一个账户(C)。
我们再来看看图中右边这一块是什么意思。6551 协议中提供了一个 Registry 合约,它是用来创建 NFT 钱包的合约,通过调用它的 createAccount 函数就可以对该 NFT 创建一个合约钱包。由于合约钱包的写法可以各式各样,因此该函数需要我们提供一个合约钱包的 Implementation。也就是说我们可以自定义钱包的合约,例如可以是 AA 钱包,可以是 Safe 钱包。图中还有 proxies 关键字,这表示 Registry 在创建钱包的时候是创建了 Implementation 的代理,而不是原模原样将其复制了一份,这样做的目的是为了节省 Gas。这块应用到了 EIP-1167 的知识,对其不熟悉的朋友可以看看我之前的文章。
6551 EIP 中把为 NFT 创建的钱包称为 token bound accounts。它有一个很重要的特性是可以向前兼容 NFT,对于之前链上已经部署的标准 NFT 合约,都可以兼容 6551。
我们平时使用的钱包,不论是 EOA 还是合约钱包,都是钱包本身拥有资产。但在 NFT 账户中,NFT 本身是不拥有资产的,而是它拥有一个合约钱包,该钱包是拥有资产的主体。也就是说 NFT 本身更类似于人的角色。用图说明应该更清晰一点:

6551 标准建议 Registry 实现 IERC6551Registry 接口:
interface IERC6551Registry {
/// @dev The registry SHALL emit the AccountCreated event upon successful account creation
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);
/// @dev Creates a token bound account for an ERC-721 token.
///
/// If account has already been created, returns the account address without calling create2.
///
/// If initData is not empty and account has not yet been created, calls account with
/// provided initData after creation.
///
/// Emits AccountCreated event.
///
/// @return the address of the account
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt,
bytes calldata initData
) external returns (address);
/// @dev Returns the computed address of a token bound account
///
/// @return The computed address of the account
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
createAccount 函数用来为 NFT 创建合约钱包,account 函数用来计算合约钱包地址。它们都利用了 create2 机制,因此可以返回一个确定的地址。对 create2 不了解的朋友可以看我之前的文章。
6551 官方给出了一个已经部署的 Registry 实现可供参考。
其中的 getCreationCode 函数值得留意一下:
function getCreationCode(
address implementation_,
uint256 chainId_,
address tokenContract_,
uint256 tokenId_,
uint256 salt_
) internal pure returns (bytes memory) {
// 将 salt, chainId, tokenContract, tokenId 拼接在 bytecode 后面
return
abi.encodePacked(
hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",
implementation_,
hex"5af43d82803e903d91602b57fd5bf3",
abi.encode(salt_, chainId_, tokenContract_, tokenId_)
);
}
它是用来组装 proxy 字节码的函数,可以看到在最后将 salt_、chainId_、tokenContract_、tokenId_ 等数据拼接在了 EIP-1167 的代理字节码后面。这里的目的是为了在创建的合约钱包中可以直接通过字节码读取到这些数据,我们之前学习过的 SudoSwap 代码中也使用了类似的写法,参考这里。
对于创建的 NFT 钱包合约(即 Implementation 合约),6551 也作出了一些要求:
应该通过 Registry 创建
必须实现 ERC-165 接口
必须实现 ERC-1271 接口
必须实现下面的 IERC6551Account 接口:
/// @dev the ERC-165 identifier for this interface is `0x400a0398`
interface IERC6551Account {
/// @dev Token bound accounts MUST implement a `receive` function.
///
/// Token bound accounts MAY perform arbitrary logic to restrict conditions
/// under which Ether can be received.
receive() external payable;
/// @dev Executes `call` on address `to`, with value `value` and calldata
/// `data`.
///
/// MUST revert and bubble up errors if call fails.
///
/// By default, token bound accounts MUST allow the owner of the ERC-721 token
/// which owns the account to execute arbitrary calls using `executeCall`.
///
/// Token bound accounts MAY implement additional authorization mechanisms
/// which limit the ability of the ERC-721 token holder to execute calls.
///
/// Token bound accounts MAY implement additional execution functions which
/// grant execution permissions to other non-owner accounts.
///
/// @return The result of the call
function executeCall(
address to,
uint256 value,
bytes calldata data
) external payable returns (bytes memory);
/// @dev Returns identifier of the ERC-721 token which owns the
/// account
///
/// The return value of this function MUST be constant - it MUST NOT change
/// over time.
///
/// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on
/// @return tokenContract The contract address of the ERC-721 token
/// @return tokenId The ID of the ERC-721 token
function token()
external
view
returns (
uint256 chainId,
address tokenContract,
uint256 tokenId
);
/// @dev Returns the owner of the ERC-721 token which controls the account
/// if the token exists.
///
/// This is value is obtained by calling `ownerOf` on the ERC-721 contract.
///
/// @return Address of the owner of the ERC-721 token which owns the account
function owner() external view returns (address);
/// @dev Returns a nonce value that is updated on every successful transaction
///
/// @return The current account nonce
function nonce() external view returns (uint256);
}
这里是一个已经实现的合约钱包示例。
Sapienz NFT 系列使用了 ERC 6551 标准,我们来看看它是怎样应用 6551 的,代码在此。
我们主要来看其中的 _mintWithTokens 函数,它的作用是,用户需要使用一些白名单的 NFT(例如 PIGEON)来 mint Sapienz NFT。具体逻辑是,函数将用户拥有的这些白名单的 NFT 转入到即将生成的 Sapienz NFT 对应的合约钱包中(即 token bound accounts),然后再为用户 mint Sapienz NFT。也就是说这些转入的白名单 NFT 是归属于生成的 Sapienz NFT 的。
我们来看看主要代码:
function _mintWithTokens(
address tokenAddress,
uint256[] memory tokenIds,
bytes32[][] memory proof,
address recipient
) internal {
// ...
uint256 quantity = tokenIds.length;
for (uint256 i = 0; i < quantity; i++) {
uint256 tokenId = tokenIds[i];
// 计算出 当前 tokenId(是当前 Sapienz 合约的 tokenId,不是传入的 tokenId)所有对应 tba 的地址
address tba = erc6551Registry.account(
erc6551AccountImplementation,
block.chainid,
address(this),
startTokenId + i,
0
);
// ....
// 将白名单 NFT#tokenId 从 msg.sender 转移到 tba 中
IERC721Upgradeable(tokenAddress).safeTransferFrom(
msg.sender,
tba,
tokenId
);
}
_safeMint(recipient, quantity);
}
我们在前面的 IERC6551Registry 接口中看到,account 函数是用来计算生成的合约钱包地址的,即代码中的 tba 就是合约钱包的地址,然后在后面会通过 tokenAddress 的 safeTransferFrom 函数将白名单 NFT 转入 tba 中。
我们注意到,代码中仅仅是计算出了 tba 的合约地址,但是并没有部署合约这一步操作,也就是说,这里的转入操作的目标地址可能是一个没有实际部署代码的地址,仅仅是一个预留地址而已。例如这个 tba,它是拥有 NFT 的,但是却并没有合约代码(不过在你看到的时候可能已经发生了状态变化)。如果我们希望该地址是有合约代码的话,需要调用 Registry 的 createAccount 函数来创建部署合约。例如这个 tba 就是已经部署的合约地址。

其中字节码的前半部分是 EIP-1167 的拼接字节码,后面的阴影部分是我们前面提到的 getCreationCode 函数中拼接的salt_、chainId_、tokenContract_、tokenId_ 数据。
我们通过原理,代码实现和实际案例学习了 ERC-6551,它本身内容并不复杂,提供了一个创建 NFT 钱包的新思路。我觉得它最友好的一点就是可以向前兼容 NFT,没有侵入性,这样使得 NFT 本身也不用去关心 6551 的逻辑,只要按照自己的业务开发就行。
欢迎和我交流
最近 ERC-6551 的热度很高,我们就来聊聊 ERC-6551 到底是什么。这篇文章我想从它的原理,代码实现,以及应用案例来讲解 6551。相信看完这篇文章后大家应该会对 6551 有一个比较全面的认识。
简单来说,6551 是一个为 NFT 创建钱包的标准。这是什么意思呢?
我们考虑一个场景,假设在游戏中,我的地址 A 拥有一个角色 Bob,这个角色它本身是一个 ERC721 的 NFT,同时它的身上也有许多道具,例如帽子,鞋子,武器等,也可能拥有一些资产例如金元宝等。这些资产本身也是 ERC20,ERC721 等类型的 token。这些道具和资产在游戏逻辑中是属于我的角色 Bob 的,但是在实际的底层合约实现中,其实它们都是属于我的地址 A 的。如果我想将我的角色 Bob 出售给别人,我就需要分别将 Bob 还有它所拥有的所有资产都一一转账给买家。这在逻辑和操作上都不是很合理。
6551 标准的目的就是为角色 Bob 创建一个钱包,使得它所拥有的道具都是属于角色 Bob 的,这样看起来就合理多了。我们可以看看官方 EIP 里给到的示例图:

用户账户中拥有两个 NFT,分别是 A#123 和 B#456,其中 A#123 拥有两个账户( A 和 B),B#456 拥有一个账户(C)。
我们再来看看图中右边这一块是什么意思。6551 协议中提供了一个 Registry 合约,它是用来创建 NFT 钱包的合约,通过调用它的 createAccount 函数就可以对该 NFT 创建一个合约钱包。由于合约钱包的写法可以各式各样,因此该函数需要我们提供一个合约钱包的 Implementation。也就是说我们可以自定义钱包的合约,例如可以是 AA 钱包,可以是 Safe 钱包。图中还有 proxies 关键字,这表示 Registry 在创建钱包的时候是创建了 Implementation 的代理,而不是原模原样将其复制了一份,这样做的目的是为了节省 Gas。这块应用到了 EIP-1167 的知识,对其不熟悉的朋友可以看看我之前的文章。
6551 EIP 中把为 NFT 创建的钱包称为 token bound accounts。它有一个很重要的特性是可以向前兼容 NFT,对于之前链上已经部署的标准 NFT 合约,都可以兼容 6551。
我们平时使用的钱包,不论是 EOA 还是合约钱包,都是钱包本身拥有资产。但在 NFT 账户中,NFT 本身是不拥有资产的,而是它拥有一个合约钱包,该钱包是拥有资产的主体。也就是说 NFT 本身更类似于人的角色。用图说明应该更清晰一点:

6551 标准建议 Registry 实现 IERC6551Registry 接口:
interface IERC6551Registry {
/// @dev The registry SHALL emit the AccountCreated event upon successful account creation
event AccountCreated(
address account,
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
);
/// @dev Creates a token bound account for an ERC-721 token.
///
/// If account has already been created, returns the account address without calling create2.
///
/// If initData is not empty and account has not yet been created, calls account with
/// provided initData after creation.
///
/// Emits AccountCreated event.
///
/// @return the address of the account
function createAccount(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt,
bytes calldata initData
) external returns (address);
/// @dev Returns the computed address of a token bound account
///
/// @return The computed address of the account
function account(
address implementation,
uint256 chainId,
address tokenContract,
uint256 tokenId,
uint256 salt
) external view returns (address);
}
createAccount 函数用来为 NFT 创建合约钱包,account 函数用来计算合约钱包地址。它们都利用了 create2 机制,因此可以返回一个确定的地址。对 create2 不了解的朋友可以看我之前的文章。
6551 官方给出了一个已经部署的 Registry 实现可供参考。
其中的 getCreationCode 函数值得留意一下:
function getCreationCode(
address implementation_,
uint256 chainId_,
address tokenContract_,
uint256 tokenId_,
uint256 salt_
) internal pure returns (bytes memory) {
// 将 salt, chainId, tokenContract, tokenId 拼接在 bytecode 后面
return
abi.encodePacked(
hex"3d60ad80600a3d3981f3363d3d373d3d3d363d73",
implementation_,
hex"5af43d82803e903d91602b57fd5bf3",
abi.encode(salt_, chainId_, tokenContract_, tokenId_)
);
}
它是用来组装 proxy 字节码的函数,可以看到在最后将 salt_、chainId_、tokenContract_、tokenId_ 等数据拼接在了 EIP-1167 的代理字节码后面。这里的目的是为了在创建的合约钱包中可以直接通过字节码读取到这些数据,我们之前学习过的 SudoSwap 代码中也使用了类似的写法,参考这里。
对于创建的 NFT 钱包合约(即 Implementation 合约),6551 也作出了一些要求:
应该通过 Registry 创建
必须实现 ERC-165 接口
必须实现 ERC-1271 接口
必须实现下面的 IERC6551Account 接口:
/// @dev the ERC-165 identifier for this interface is `0x400a0398`
interface IERC6551Account {
/// @dev Token bound accounts MUST implement a `receive` function.
///
/// Token bound accounts MAY perform arbitrary logic to restrict conditions
/// under which Ether can be received.
receive() external payable;
/// @dev Executes `call` on address `to`, with value `value` and calldata
/// `data`.
///
/// MUST revert and bubble up errors if call fails.
///
/// By default, token bound accounts MUST allow the owner of the ERC-721 token
/// which owns the account to execute arbitrary calls using `executeCall`.
///
/// Token bound accounts MAY implement additional authorization mechanisms
/// which limit the ability of the ERC-721 token holder to execute calls.
///
/// Token bound accounts MAY implement additional execution functions which
/// grant execution permissions to other non-owner accounts.
///
/// @return The result of the call
function executeCall(
address to,
uint256 value,
bytes calldata data
) external payable returns (bytes memory);
/// @dev Returns identifier of the ERC-721 token which owns the
/// account
///
/// The return value of this function MUST be constant - it MUST NOT change
/// over time.
///
/// @return chainId The EIP-155 ID of the chain the ERC-721 token exists on
/// @return tokenContract The contract address of the ERC-721 token
/// @return tokenId The ID of the ERC-721 token
function token()
external
view
returns (
uint256 chainId,
address tokenContract,
uint256 tokenId
);
/// @dev Returns the owner of the ERC-721 token which controls the account
/// if the token exists.
///
/// This is value is obtained by calling `ownerOf` on the ERC-721 contract.
///
/// @return Address of the owner of the ERC-721 token which owns the account
function owner() external view returns (address);
/// @dev Returns a nonce value that is updated on every successful transaction
///
/// @return The current account nonce
function nonce() external view returns (uint256);
}
这里是一个已经实现的合约钱包示例。
Sapienz NFT 系列使用了 ERC 6551 标准,我们来看看它是怎样应用 6551 的,代码在此。
我们主要来看其中的 _mintWithTokens 函数,它的作用是,用户需要使用一些白名单的 NFT(例如 PIGEON)来 mint Sapienz NFT。具体逻辑是,函数将用户拥有的这些白名单的 NFT 转入到即将生成的 Sapienz NFT 对应的合约钱包中(即 token bound accounts),然后再为用户 mint Sapienz NFT。也就是说这些转入的白名单 NFT 是归属于生成的 Sapienz NFT 的。
我们来看看主要代码:
function _mintWithTokens(
address tokenAddress,
uint256[] memory tokenIds,
bytes32[][] memory proof,
address recipient
) internal {
// ...
uint256 quantity = tokenIds.length;
for (uint256 i = 0; i < quantity; i++) {
uint256 tokenId = tokenIds[i];
// 计算出 当前 tokenId(是当前 Sapienz 合约的 tokenId,不是传入的 tokenId)所有对应 tba 的地址
address tba = erc6551Registry.account(
erc6551AccountImplementation,
block.chainid,
address(this),
startTokenId + i,
0
);
// ....
// 将白名单 NFT#tokenId 从 msg.sender 转移到 tba 中
IERC721Upgradeable(tokenAddress).safeTransferFrom(
msg.sender,
tba,
tokenId
);
}
_safeMint(recipient, quantity);
}
我们在前面的 IERC6551Registry 接口中看到,account 函数是用来计算生成的合约钱包地址的,即代码中的 tba 就是合约钱包的地址,然后在后面会通过 tokenAddress 的 safeTransferFrom 函数将白名单 NFT 转入 tba 中。
我们注意到,代码中仅仅是计算出了 tba 的合约地址,但是并没有部署合约这一步操作,也就是说,这里的转入操作的目标地址可能是一个没有实际部署代码的地址,仅仅是一个预留地址而已。例如这个 tba,它是拥有 NFT 的,但是却并没有合约代码(不过在你看到的时候可能已经发生了状态变化)。如果我们希望该地址是有合约代码的话,需要调用 Registry 的 createAccount 函数来创建部署合约。例如这个 tba 就是已经部署的合约地址。

其中字节码的前半部分是 EIP-1167 的拼接字节码,后面的阴影部分是我们前面提到的 getCreationCode 函数中拼接的salt_、chainId_、tokenContract_、tokenId_ 数据。
我们通过原理,代码实现和实际案例学习了 ERC-6551,它本身内容并不复杂,提供了一个创建 NFT 钱包的新思路。我觉得它最友好的一点就是可以向前兼容 NFT,没有侵入性,这样使得 NFT 本身也不用去关心 6551 的逻辑,只要按照自己的业务开发就行。
欢迎和我交流
No activity yet