本文介绍了 ERC-6551 提案,该提案使得非同质化代币 (NFT) 可以作为“钱包”进行资产管理,支持与其他智能合约的交互。通过使用 Hardhat 和 OpenZeppelin,读者可以学习如何创建和部署符合 ERC-6551 标准的代币,并了解 Token Bound Account 的构建和操作流程。
想象一下,如果你持有的 NFT 不仅可以代表资产本身,还可以充当“钱包”,允许你的资产与其他智能合约交互,并在其内部存储其他数字资产。一个新的 以太坊改进提案,ERC-6551:非同质化代币绑定账户 有望不久在以太坊上实现这一功能。本文将介绍 ERC-6551 提案的具体细节,并帮助你了解如何使用 Hardhat 和 OpenZeppelin 创建和部署符合 ERC-6551 规范的代币。
今天的 NFT 是存在于用户钱包(如 MetaMask、Coinbase Wallet 或 Ledger)中的资产。这些代币代表特定的资产和/或内容。通常,它们遵循一种代币标准 ERC-721 或 ERC-1155 标准,这些标准规定了它们如何被铸造、转移和交互。然而,它们的功能历来只限于简单的转移和元数据更新。
ERC-6551 使 Token Bound Accounts (TBAs) 成为可能。这些是具有自己地址并与特定 NFT 相关联/管理的智能合约。可以把它视为一个与 NFT 直接关联的小型钱包,开启了以下可能性:
请注意,随着 NFT 所有权的变化,其中的资产也会随之变化。
一些实现 ERC-6551 的想法包括:
现在,让我们更深入地技术性探讨构成 ERC-6551 的核心组件。
下图展示了注册表、NFT、NFT 持有者和 Token Bound Accounts 之间的流向:
在上面的图中,用户账户 拥有 ERC-721 代币。这些 ERC-721 拥有自己的 账户(例如,Token Bound Accounts),这些账户充当可以存储其他资产的“钱包”。为了使账户进行活动,用户账户必须发起交易。账户能够执行的活动受到其实现逻辑的限制,这些逻辑存储在账户会代理的另一个智能合约中。这些账户由注册表合约创建。
现在,让我们介绍 Token Bound Accounts 的注册表和接口的具体细节。
技术上称为单例注册表的注册表,可以被视为 NFTs 及其相关联的 Token Bound Accounts 的数据库。注册表是可以部署到任何 EVM 兼容区块链的智能合约。它是无权限的、不可变的,并且没有所有者。此注册表确保所有 Token Bound Account 地址使用标准方案。
请注意,注册表已在所有主链上部署(可以在 这里 找到列表)。因此,开发者无需重新部署注册表,除非他们想要定制实现。希望在同一 NFT 之下部署多个 Token Bound Account 的开发者可以部署自己的自定义注册表合约。
单例注册表合约包含两个主要功能:
createAccount
- 此功能为给定实现地址的 NFT 创建 Token Bound Account。此功能接受 5 个参数,分别是 implementation
地址、chainId
、NFT
地址、token ID
、salt
和 init
数据。chainId
参数允许用户在多个链上拥有持久的 Token Bound Accounts。通过这种逻辑,Token Bound Accounts 能够在需要时进行跨链通信。account
- 计算给定实现地址的 NFT 的 Token Bound Account 地址,给定的参数包括 implementation
地址、chainId
、NFT
地址、token ID
和 salt
。请注意,注册表要求所有 Token Bound Accounts 继承 ERC-1167 实现。
为确保所有 Token Bound Accounts 的一致性,标准规定了所有 TBAs 必须遵循的具体接口。这些接口使 TBAs 能够可靠地运作,同时确保互操作性和可预测性。
ERC-165 接口
这是以太坊智能合约中的标准接口检测机制。它允许智能合约发布它们实现的接口,并允许其他合约查询该接口。通过确保 TBAs 实现这个接口,我们可以在运行时可靠地确认一个合约是否支持所需的接口。
interface IERC165 {
function supportsInterface(bytes4 interfaceId) external view returns (bool);
}
ERC-1271 签名验证接口
此标准允许智能合约像常规的外部拥有账户一样验证签名。通过集成此接口,TBAs 可以有效地验证签名,并根据它们的有效性采取行动,从而实现交易或与其他合同的交互。
interface IERC1271 {
function isValidSignature(bytes32 hash, bytes memory signature) external view returns (bytes4 magicValue);
}
执行接口
此关键接口允许 TBAs 在提供了一份来自被认可的签署者的有效签名时执行任意操作。此接口的本质是允许 TBAs 代表账户执行操作。
interface IExecutionInterface {
function execute(address to, uint256 value, bytes memory data, bytes memory signature) external returns (bool success);
}
Token Bound Account 实现已在所有主要链上部署。如果你不需要为你的 Token Bound Account 自定义逻辑,可以使用 这里 引用的账户实现地址之一。
账户实现逻辑的核心功能是 execute
和 owner
。
execute
:该函数检查进行调用的签署者是否被授权。这是通过 _isValidSigner
函数完成的。它还检查传递的操作参数是否为 0
,并发送指定数量的以太币以及函数调用。如果发生错误调用,整个交易将被撤回。owner
:此函数返回特定 ERC721 代币的当前所有者。isValidSigner
:该函数检查签署人是否为 owner
地址。isValidSignature
:该函数检查签名是否与 owner
的签名匹配。token
:此函数返回与 token bound account 关联的 ERC721 代币的详细信息。需要注意的是:NFT 的所有者控制其相关的 Token Bound Account (TBA)。如果 NFT 被转移到其他所有者,TBA 的控制权也会随之转移,因为 TBA 永久关联于 NFT。因此,在任何时候持有或拥有 NFT 的人将控制其相关的 TBA。
ERC-6551 标准还实现了可升级性(通过代理)。在智能合约和以太坊的上下文中,代理指的是一种设计模式,其中一个智能合约(代理)将所有调用推迟到逻辑合约。代理保持状态,但业务逻辑则位于逻辑合约中。使用代理模式的主要目的是为了升级。当业务逻辑需要更改或升级时,将部署一个新的逻辑合约,并将代理重定向以委托调用到这个新的逻辑合约,同时保持原有状态。
在 ERC-6551 中,这种可升级性/代理机制用于部署每个 Token Bound Account。具体来说,使用了 ERC-1167 代理的自定义版本。这允许每个 Token Bound Account (TBA) 共享相同的逻辑代码,从而节省Gas费用并简化升级。每个代理通过将其存储为 ABI 编码的常量数据附加到合约字节码中,保留唯一的状态数据(如链 id、代币合约地址和代币 ID)。
接下来,在本指南的剩余部分,我们将介绍希望创建和部署自己符合 ERC-6551 智能合约的开发者的技术步骤。
为了在以太坊 Sepolia 区块链上部署我们的智能合约,我们需要访问以太坊 Sepolia 节点。为此,我们可以使用 MetaMask 中的默认 RPC 网络。然而,为了实现高达 8 倍的响应速度,我们将使用 QuickNode。创建一个免费账户,然后单击“创建一个端点”。选择 Ethereum Sepolia 链并创建你的端点。
创建你的以太坊 Sepolia 端点后,复制你的 HTTP Provider 端点,并导航到你的 MetaMask 钱包。在你的钱包主页上,单击左上角的网络选项卡,然后单击“添加网络”。MetaMask 将在新选项卡中以全屏模式打开。
在网络列表的底部找到“手动添加网络”按钮。然后,按以下方式完成各项字段:
然后单击“保存”以添加自定义网络。
现在我们已经在 MetaMask 中配置了 QuickNode 端点,下一步是检索一些测网 ETH 以支付交易费用。
导航至 QuickNode 多链水龙头,连接你的钱包,并请求 Sepolia 测试网上的 ETH。
注意:你至少需要在主网上有 0.001 ETH 的余额,才能有资格使用水龙头。
在我们的钱包获得资金并设置自定义 RPC 后,让我们进入代码部分。
我们将使用 Hardhat,一个智能合约开发工具包,以及 OpenZeppelin,一个经过审计的智能合约库,来创建智能合约。
打开终端窗口并克隆以下 GitHub 仓库:
https://github.com/quiknode-labs/qn-guide-examples
导航到 ethereum
下的 erc-6551
目录:
cd ethereum/erc-6551
然后运行以下命令导航到你的代码库并安装依赖项:
npm install
我们还创建一个 .env 文件,用于保存我们的私钥以进行交易。
echo > .env
在 .env 文件中,复制以下变量并输入你的 RPC URL 和私钥凭据。要从你的 MetaMask 钱包提取私钥,请按照本 指南 操作。
RPC_URL=
PRIVATE_KEY=
记得保存文件!
现在,随着我们的开发环境设置完成,我们可以进入通过注册表合约创建 Token Bound Account 的步骤。
由于 ERC-6551 要求与其关联一个 ERC-721 代币,因此我们还需要部署一个。请不用担心。我们在你克隆的 GitHub 仓库中已包含一个合约;然而,为了将来参考,你也可以查看此 QuickNode 指南 - 如何创建和部署一个 ERC-721 (NFT)。
现在,在项目的主目录中,在终端运行以下命令:
npx hardhat run --network sepolia scripts/createAccount.js
上述脚本中主要的组件以下代码:
// 创建注册表合约的实例
const RegistryContract = await hre.ethers.getContractAt('ERC6551Registry', registryAddress, signer);
...
// 部署并铸造我们稍后将转移到 TBA 的 NFT
const nftTokenContract = await hre.ethers.deployContract('MyToken', signer);
...
const mintTxn = await nftTokenContract.safeMint(signer.address);
...
// 调用注册表上的 createAccount 函数,创建我们的 TBA 地址
const transaction = await RegistryContract.createAccount(implementationAddress, chainId, nftTokenContract.target, tokenId, salt, initData);
有关完整代码,请参考 GitHub 仓库。
一旦挖矿,你将会在控制台中看到类似以下的输出:
正在部署合约...
ERC-721 合约部署到 0x8953A287122c2f76d077A94E9E007ABACF87B7C7
铸造成功: 0x49c157ab9b8fbd2e6fc2aaf8ef429b55d421e6605adf329f1ac7cc53a1f79a56
createAccount 调用成功。交易哈希: 0xb8847e5a6d1687bad38f1303c31ef707a700714a45d6d785e061c6130cf800ac
Token Bound Account: 0xB992415D3BC7b19323bfE84c1A033303Ffe60E37
通过上述脚本,我们首先部署了一个 ERC-721 合约,然后从中铸造了一个 NFT。接着,我们在注册表合约上调用了 createAccount
函数以生成我们的 Token Bound Account (TBA)。请记住,注册表合约已经在多个 EVM 链 上部署,因此除非你需要自己的私有或自定义实现,否则可以与主注册表合约交互。
你现在拥有一个可以与区块链互动并拥有资产的 Token Bound Account。请查看 Etherscan 验证交易是否已确认(这是一个 示例交易)。如果你导航到 日志 选项卡,你将看到 AccountCreation
事件显示 Token Bound Account 地址(例如,account)。
在下一部分中,我们将介绍如何与新创建的 Token Bound Account 进行交互。
随着我们的 NFT 和 Token Bound Account 创建完成,让我们从个人钱包向 Token Bound Account 合约发起转移我们铸造的 NFT。
打开 interactAccount.js
文件并填写 nftContractAddress
(例如,ERC-721 合约)和 tokenBoundAccountAddress
变量的正确值。这些值在上一个脚本的终端中已记录。完成后,保存文件。
现在,运行以下 interactAccount.js
脚本,向你的 Token Bound Account 发送 ETH(用于Gas费用),然后将 NFT 从你的钱包转移到你的 Token Bound Account。
npx hardhat run --network sepolia scripts/interactAccount.js
上述脚本中主要的组件以下代码:
// 通过调用注册表合约的 account 函数计算 TBA 地址
ERC6551Registry.account(accountImplementationAddress, chainId, nftContractAddress, tokenId, salt);
...
// 向 TBA 地址发送资金,并记录转移前后的余额
sendFundsToTokenAccount()
...
// 批准,然后将铸造的 NFT 从我们的个人钱包转移到 TBA 地址
transferToken()
有关完整代码,请参考 GitHub 仓库。
你应该看到如下输出:
Token Bound Account Address: 0xd42d61CFDfce1557d02bA6CE83Dc2583586A4c12
Token account has 0 ETH before transfer
Token account has .01 ETH after transfer
当前 tokenId 0 的所有者是 0x351132C80b1e1De690A6dAe996649E0b2a1E7888
批准交易成功。哈希: 0x426fa733d960a8f02e3291a6d318417d5de003e7f59ba466b9da526c11e53491
转移交易成功。哈希: 0x2b328af9eb0415801f57967ab541e6b0089ada2d549e5b232a0e25585ea0e981
tokenId 0 的新所有者是 0xd42d61CFDfce1577d02bB5CE83Dc2583586A4c12
发生了什么?回顾一下,在此脚本中,我们:
account
函数计算了 TBA 地址approve
函数,批准从我们个人钱包转移到 TBA 地址在脚本的执行过程中,我们记录了数据点以确保余额正确。
在 Etherscan 上检查我们的 TBA 后,我们可以确认 NFT(例如,MyToken)现已在我们的 TBA 中:
接下来是什么?通过你学到的知识,你可以尝试与你的 Token Bound Account 交互,将 NFT 转移到另一个地址。
通过你在本指南中所做的一切,让我们测试一下你的知识。尝试完成以下简短测验!
🧠知识测试
在单例注册表合约中,createAccount
函数的作用是什么?
更新代币元数据 转移 NFT 的所有权 创建 NFT 的 Token Bound Account 验证 NFT 签名
到这里为止!到目前为止,你已经了解了更多关于 ERC-6551 的内容以及如何实现它。如果你有任何问题,请随时使用我们在 Discord 上的专用频道或通过下面的表单提供反馈。通过关注我们的 Twitter 和 Telegram 公告频道,保持与最新动态的同步。
让我们知道 如果你有任何反馈或新主题的请求。我们非常乐意倾听你的意见。
- 原文链接: quicknode.com/guides/eth...
- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!