Alert Source Discuss
Standards Track: ERC

ERC-6809: 非同质化密钥绑定代币

非同质化密钥绑定代币(Non-Fungible Key Bound Tokens,也称为 NFKBT)的接口。

Authors Mihai Onila (@MihaiORO), Nick Zeman (@NickZCZ), Narcis Cotaie (@NarcisCRO)
Created 2023-03-31
Requires EIP-721

摘要

非同质化密钥绑定代币 (NFKBT/s) 的标准接口,是更通用的密钥绑定代币 (KBT/s) 的一个子集。

以下内容标准化了智能合约中代币的 API,并为 addBindings 函数提供了基本功能。此函数指定密钥钱包1,负责执行安全转移2。在此过程中,NFKBT 被安全地批准,以便用户或链上第三方实体可以使用它们。

NFKBT 的前提是通过 allowTransferallowApproval 函数中的 allow 概念,直接在非同质化资产中构建完全可选的安全功能。这些函数由其中一个密钥钱包1 调用,并 允许 持有钱包3 调用在 ERC-721 中已经熟悉的 transferFromapprove 函数。因此,NFKBT 的责任被分开。持有钱包 包含资产,而密钥钱包 有权决定如何使用或批准这些资产。传统的非同质化 ERC-721 的 默认行为4 可以通过简单地从不使用 addBindings 函数来实现。

我们考虑将 NFKBT 用于希望为其非同质化资产增加额外安全性的每个人,以及寄售给第三方钱包/经纪人/银行/保险公司/画廊。通过在自托管级别为资产本身提供额外的保护,NFKBT 可以抵御攻击/盗窃。

动机

在这个快节奏的技术进步世界中,人们以不同的速度学习和成熟。全球采用的目标必须考虑到目标受众是所有年龄和背景的人。不幸的是,对于自托管资产而言,最大的优点之一也是其最大的缺点之一。个人对其行为和充分保护其资产负全部责任。如果犯了一个导致资金损失的错误,没有人能够保证他们的回报。

从 2021 年 1 月到 2022 年 3 月,美国联邦贸易委员会收到了超过 46,0005 份加密货币诈骗报告。这直接影响了加密货币用户,导致消费者净损失超过 10 亿美元6。盗窃和恶意诈骗是任何金融领域的问题,并且常常导致更严格的监管。但是,政府实施的监管违背了这个空间的核心价值之一。已经通过集中式和分散式方式努力提高空间内的安全性。到目前为止,还没有人提供一种解决方案,既保留了两者的优势,又消除了它们的缺点。

我们像过去许多人一样问自己同样的问题,“如何保护钱包?”。过了一段时间,意识到应该问的问题是“如何保护资产?”。创建钱包是免费的,资产才有价值,值得保护。这个问题导致了 KBT 的开发。这是一个完全可选的解决方案,可以根据用户的需求进行定制。即使种子短语或私钥被公开,只要安全功能被激活,个人资产仍然受到保护。

NFKBT 意识到需要改进广泛使用的非同质化 ERC-721 代币标准。非同质化资产的安全性是加密货币领域中每个实体都关心的话题,因为它们当前和未来的用例正在不断探索中。NFKBT 提供了一种可扩展的去中心化安全解决方案,该解决方案使安全性超越了钱包安全性,专注于代币保持安全的能力。安全性位于区块链本身上,这使每个可以访问互联网的人都可以保护其资产,而无需当前的硬件或集中式解决方案。作为一种有希望的替代方案,NFKBT 继承了 ERC-721 的所有特性。这样做是为了使 NFKBT 可以在配置为使用传统非同质化代币的每个 dApp 上使用。

在开发过程中,KBT 探索的潜在优势是导致其创建的主要动机因素。

  1. 完全去中心化: 安全功能是完全去中心化的,这意味着激活后,任何第三方都无法访问用户资金。这样做是为了真正遵守自托管资产的前提、责任和价值。

  2. 无限的可扩展性: 集中式解决方案需要创建帐户,并且其可用性可能会因位置而受到限制。NFKBT 没有区域限制或帐户创建。诸如硬件选项之类的去中心化安全解决方案面临可扩展性问题,需要运输物流、安全运输和供应商。只要有互联网访问权限,世界各地的任何人都可以使用 NFKBT

  3. 完全可选的安全性: 安全功能是可选的、可定制的和可移除的。完全由用户决定在使用 NFKBT 时希望获得的安全性级别。

  4. 默认功能: 如果用户希望将 NFKBT 用作传统的 ERC-721,则不必激活安全功能。由于代币继承了所有相同的特性,因此代币的行为与传统的非同质化 默认行为4 相同。但是,即使激活了安全功能,用户仍然可以根据其期望的结果自定义各种功能的功能。用户可以手动或通过 dApp 传递一组自定义和/或 默认值7

  5. 无与伦比的安全性: 通过调用 addBindings 函数,现在需要一个 密钥钱包1 才能使用 allowTransferallowApproval 函数。allowTransfer 函数需要 4 个参数,_tokenId8_time9_address10_anyToken11,而 allowApproval 函数有 2 个参数,_time12_numberOfTransfers13。除此之外,NFKBT 还有一个 safeFallbackresetBindings 函数。所有这些功能的结合可以防止并几乎涵盖了传统 ERC-721 中存在的每一个故障点(如果使用得当)。

  6. 安全故障保护: 通过 NFKBT,用户可以确信他们的代币是安全可靠的,即使 持有钱包3 或其中一个 密钥钱包1 受到威胁也是如此。如果所有者怀疑 持有钱包 受到威胁或无法访问,他们可以从其中一个 密钥钱包 调用 safeFallback 函数。这会将资产移动到另一个 密钥钱包,从而防止单点故障。如果所有者怀疑其中一个 密钥钱包 受到威胁或无法访问,则所有者可以从 _keyWallet114_keyWallet215 调用 resetBindings 函数。这将重置 NFKBT 的安全功能,并允许 持有钱包 再次调用 addBindings 函数。因此,可以添加新的 密钥钱包,并且可以防止单点故障。

  7. 匿名安全性: 通常,集中式解决方案会要求存储的个人信息并受到窥探。购买去中心化硬件解决方案也容易出现相同的问题,例如,送货地址、付款信息或在物理现金取货期间的摄像机记录。有些人可能认为这侵犯了他们的隐私和资产匿名性。NFKBT 确保用户机密性,因为一切都可以在区块链上以笔名远程完成。

  8. 低成本安全性: 使用 NFKBT 安全功能的成本与链上费用相关,即当时当前的 GWEI。作为一种独立的解决方案,它们是一种可行的、具有成本效益的安全措施,对大多数人来说是可行的。

  9. 环境友好: 由于安全功能已编码到 NFKBT 中,因此无需集中式服务器、运输或生产物理对象。因此,通过使用 NFKBT,与以太坊更改为 PoS16 网络携手合作,从而使碳足迹最小。

  10. 用户体验: 可以通过简单地调用 addBindings 函数来激活安全功能。用户只需要另外两个钱包(将充当 _keyWallet114_keyWallet215)即可访问 NFKBT 提供的所有好处。可选的安全功能通过确保为那些决定使用它的用户提供安全网来改善整体用户体验和以太坊生态系统。那些不使用安全功能的人不会受到任何阻碍。这种安全网可以增加全球采用率,因为即使在钱包受到威胁的情况下,人们也可以对其资产的安全性保持信心。

规范

IKBT721 (代币合约)

注意:

  • 以下规范使用 Solidity 0.8.17(或更高版本)的语法
  • 调用者必须处理 returns (bool success) 返回的 false。调用者不得假设永远不会返回 false
interface IKBT721 {
    event AccountSecured(address indexed _account, uint256 _noOfTokens);
    event AccountResetBinding(address indexed _account);
    event SafeFallbackActivated(address indexed _account);
    event AccountEnabledTransfer(
        address _account,
        uint256 _tokenId,
        uint256 _time,
        address _to,
        bool _anyToken
    );
    event AccountEnabledApproval(
        address _account,
        uint256 _time,
        uint256 _numberOfTransfers
    );
    event Ingress(address _account, uint256 _tokenId);
    event Egress(address _account, uint256 _tokenId);

    struct AccountHolderBindings {
        address firstWallet;
        address secondWallet;
    }

    struct FirstAccountBindings {
        address accountHolderWallet;
        address secondWallet;
    }

    struct SecondAccountBindings {
        address accountHolderWallet;
        address firstWallet;
    }

    struct TransferConditions {
        uint256 tokenId;
        uint256 time;
        address to;
        bool anyToken;
    }

    struct ApprovalConditions {
        uint256 time;
        uint256 numberOfTransfers;
    }

    function addBindings(
        address _keyWallet1,
        address _keyWallet2
    ) external returns (bool);

    function getBindings(
        address _account
    ) external view returns (AccountHolderBindings memory);

    function resetBindings() external returns (bool);

    function safeFallback() external returns (bool);

    function allowTransfer(
        uint256 _tokenId,
        uint256 _time,
        address _to,
        bool _allTokens
    ) external returns (bool);

    function getTransferableFunds(
        address _account
    ) external view returns (TransferConditions memory);

    function allowApproval(
        uint256 _time,
        uint256 _numberOfTransfers
    ) external returns (bool);

    function getApprovalConditions(
        address account
    ) external view returns (ApprovalConditions memory);

    function getNumberOfTransfersAllowed(
        address _account,
        address _spender
    ) external view returns (uint256);

    function isSecureWallet(address _account) external returns (bool);

    function isSecureToken(uint256 _tokenId) external returns (bool);
}

事件

AccountSecured 事件

_account 通过调用 addBindings 函数来保护其帐户时触发。

_amount_account 的当前余额。

event AccountSecured(address _account, uint256 _amount)

AccountResetBinding 事件

当持有者通过调用 resetBindings 函数来重置其 keyWallets 时触发。

event AccountResetBinding(address _account)

SafeFallbackActivated 事件

当持有者选择通过调用 safeFallback 函数将所有资金转移到其中一个 keyWallets 时触发。

event SafeFallbackActivated(address _account)

AccountEnabledTransfer 事件

_account 允许将 _amount 的代币转移 _timeblock 秒数到 _to 地址(或者如果通过将 _anyToken 设置为 true 允许转移所有资金)时,通过调用 allowTransfer 函数触发。

event AccountEnabledTransfer(address _account, uint256 _amount, uint256 _time, address _to, bool _allFunds)

AccountEnabledApproval 事件

_account 允许批准 _timeblock 秒数时,通过调用 allowApproval 函数触发。

event AccountEnabledApproval(address _account, uint256 _time)

Ingress 事件

_account 成为持有者时触发。_amount_account 的当前余额。

event Ingress(address _account, uint256 _amount)

Egress 事件

_account 转移所有代币并且不再是持有者时触发。_amount_account 的先前余额。

event Egress(address _account, uint256 _amount)

接口函数

必须实现以下详细函数。

addBindings 函数

使用其他两个名为 _keyWallet1_keyWallet2 的钱包保护发送者帐户,并且必须触发 AccountSecured 事件。

如果发生以下情况,该函数应 revert

  • 发送者帐户不是持有者
  • 或者发送者已被保护
  • 或者 keyWallets 相同
  • 或者其中一个 keyWallets 与发送者相同
  • 或者一个或两个 keyWallets 都是零地址 (0x0)
  • 或者一个或两个 keyWallets 已经是另一个持有者帐户的 keyWallets
function addBindings (address _keyWallet1, address _keyWallet2) external returns (bool)

getBindings 函数

该函数以 struct 格式返回 _accountkeyWallets

struct AccountHolderBindings {
    address firstWallet;
    address secondWallet;
}
function getBindings(address _account) external view returns (AccountHolderBindings memory)

resetBindings 函数

注意: 当两个 keyWallets 之一被盗用时,此函数很有用。

keyWallet 调用,该函数重置 holder 帐户的 keyWallets。必须触发 AccountResetBinding 事件。

如果发送者不是 keyWallet,该函数应 revert

function resetBindings() external returns (bool)

safeFallback 函数

注意:holder 帐户被盗用时,此函数很有用。

keyWallet 调用,此函数将所有代币从 holder 帐户转移到另一 keyWallet,并且必须触发 SafeFallbackActivated 事件。

如果发送者不是 keyWallet,该函数应 revert

function safeFallback() external returns (bool);

allowTransfer 函数

keyWallet 调用,此函数在调用 transferFromsafeTransferFrom 函数之前调用。

它允许在特定时间范围内将 tokenId 转移到特定地址。

如果 tokenId 为 0,则 tokenId 没有限制。 如果时间为 0,则时间没有限制。 如果 to 地址为零地址,则 to 地址没有限制。 或者,如果 _anyTokentrue,则无论其他参数如何,它都允许持有者的任何代币随时转移给任何人。

该函数必须触发 AccountEnabledTransfer 事件。

如果发送者不是持有人的 keyWallet,或者 _tokenId 的所有者与 holder 不同,则该函数应 revert

function allowTransfer(uint256 _tokenId, uint256 _time, address _to, bool _anyToken) external returns (bool);

getTransferableFunds 函数

该函数以 struct 格式返回 _account 的转移条件。

struct TransferConditions {
    uint256 tokenId;
    uint256 time;
    address to;
    bool anyToken;
}
function getTransferableFunds(address _account) external view returns (TransferConditions memory);

allowApproval 函数

keyWallet 调用,此函数在调用 approvesetApprovalForAll 函数之前调用。

它允许 holder 在特定时间 _time 内执行 approvesetApprovalForAll,并限制 spender 通过 _numberOfTransfers 允许执行的转移次数(0 - 允许转移次数不限)。

该函数必须触发 AccountEnabledApproval 事件。

如果发送者不是 keyWallet,该函数应 revert

function allowApproval(uint256 _time) external returns (bool)

getApprovalConditions 函数

该函数以 struct 格式返回批准条件。其中 timeblock.timestamp,直到可以调用 approvesetApprovalForAll 函数为止,而 numberOfTransfers 是 spender 将被允许的转移次数。

struct ApprovalConditions {
    uint256 time;
    uint256 numberOfTransfers;
}
function getApprovalConditions(address _account) external view returns (ApprovalConditions memory);

transferFrom 函数

该函数将 _tokenId 代币从 _from 地址转移到 _to 地址。

每次 spender 调用该函数时,合约都会减去并检查该 spender 的允许转移次数是否已达到 0, 当发生这种情况时,将使用 set approval for all to false 撤消批准。

该函数必须触发 Transfer 事件。

如果出现以下情况,该函数应 revert

  • 发送者不是所有者,或者未被批准转移 _tokenId
  • 或者 _from 地址不是 _tokenId 的所有者
  • 或者发送者是一个安全帐户,并且尚未允许通过 allowTransfer 函数转移此 _tokenId
function transferFrom(address _from, address _to, uint256 _tokenId) external returns (bool)

safeTransferFrom 函数

该函数将 _tokenId 代币从 _from 地址转移到 _to 地址。

该函数必须触发 Transfer 事件。

如果出现以下情况,该函数应 revert

  • 发送者不是所有者,或者未被批准转移 _tokenId
  • 或者 _from 地址不是 _tokenId 的所有者
  • 或者发送者是一个安全帐户,并且尚未允许通过 allowTransfer 函数转移此 _tokenId
function safeTransferFrom(address _from, address _to, uint256 _tokenId, bytes memory data) external returns (bool)

带有数据参数的 safeTransferFrom 函数

这与其他具有额外数据参数的函数的工作方式相同,除了此函数仅将数据设置为“”。

function safeTransferFrom(address _from, address _to, uint256 _tokenId) external returns (bool)

approve 函数

该函数允许 _to 帐户从发送者帐户转移 _tokenId

该函数还将 _to 帐户限制为该 holder 帐户的 ApprovalConditions 中设置的特定转移次数。如果值为 0,则 _spender 可以多次转移。

该函数必须触发 Approval 事件。

如果再次调用该函数,它将覆盖 allowApproval 函数中设置的允许转移次数 _numberOfTransfers

如果出现以下情况,该函数应 revert

  • 发送者不是当前的 NFT 所有者,也不是当前所有者的授权运营商
  • NFT 所有者受到保护,并且尚未调用 allowApproval 函数
  • 或者 allowApproval 函数中设置的 _time 已过去。
function approve(address _to, uint256 _tokenId) public virtual override(ERC721, IERC721)

setApprovalForAll 函数

该函数启用或禁用对另一个帐户 _operator 的批准,以管理发送者的所有资产。

该函数还将 _to 帐户限制为该 holder 帐户的 ApprovalConditions 中设置的特定转移次数。如果值为 0,则 _spender 可以多次转移。

该函数发出一个 Approval 事件,指示更新的津贴。

如果再次调用该函数,它将覆盖 allowApproval 函数中设置的允许转移次数 _numberOfTransfers

如果出现以下情况,该函数应 revert

  • 发送者帐户受到保护,并且尚未调用 allowApproval 函数
  • 或者 _spender 是零地址 (0x0)
  • 或者 allowApproval 函数中设置的 _time 已过去。
function setApprovalForAll(address _operator, bool _approved) public virtual override(ERC721, IERC721)

基本原理

NFKBT 的开发过程中做出的个人技术决策意图是专注于保持与 ERC-721 的一致性和向后兼容性,同时为用户提供自托管安全功能。重要的是,NFKBT 继承了 ERC-721 的所有特性,以符合在其平台上使用非同质化代币的 dApp 中找到的要求。这样做可以实现完美的向后兼容性,并让用户选择是否希望他们的 NFKBT 具有 默认行为4。我们希望确保 NFKBT 的大规模实施和采用可以立即进行,而无需更大的集体适应和更改已经蓬勃发展的去中心化生态系统。

对于开发人员和用户而言,allowTransferallowApproval 函数在成功时都返回布尔值,而在失败时则恢复。做出此决定的目的是保持与已经熟悉的 ERC-721 的一致性。与自托管安全功能相关的其他技术决策已分解并位于 安全注意事项 部分中。

向后兼容性

KBT 旨在与现有的代币标准和钱包向后兼容。现有的代币和钱包将继续正常运行,并且不会受到 NFKBT 的实施的影响。

测试用例

assets 目录包含所有测试

使用的平均 Gas (GWEI):

  • addBindings - 155,096
  • resetBindings - 30,588
  • safeFallback - 72,221(取决于持有人拥有的 NFT 数量)
  • allowTransfer - 50,025
  • allowApproval - 44,983

参考实现

该实现位于 assets 目录中。还有一个图表,其中包含合约交互。

安全注意事项

NFKBT 的设计在每一步都考虑了安全性。以下是在开发过程中经过严格讨论和思考的一些设计决策。

密钥钱包1:在调用 NFKBTaddBindings 函数时,用户必须输入 2 个钱包,然后将其用作 _keyWallet114_keyWallet215。它们同时添加以减少用户费用,最大程度地减少人为错误的机会并防止出现陷阱情况。如果用户能够添加多个钱包,则不仅会导致额外费用和可避免的混乱,而且还会导致潜在的灾难性 safeFallback 情况发生。因此,当安全功能被激活时,所有 KBT 都在 3 钱包系统下工作。

通常,如果钱包被盗用,则其中的非同质化资产将面临风险。通过 NFKBT,可以从 密钥钱包1 调用两个不同的函数,具体取决于哪个钱包被盗用。

场景:持有钱包3 被盗用,调用 safeFallback

safeFallback:创建此函数是为了防止所有者认为 持有钱包3 已被盗用。如果所有者失去了对 持有钱包 的访问权限,也可以使用它。在这种情况下,用户可以从其中一个 密钥钱包1 调用 safeFallback。然后将 NFKBT持有钱包 重定向到另一个 密钥钱包

通过重定向 NFKBT,可以防止单点故障。如果攻击者调用 safeFallback 并且 NFKBT 重定向到调用该函数的 密钥钱包1,他们将获得对所有 NFKBT 的访问权限。

场景:密钥钱包1 被盗用,调用 resetBindings

resetBindings:创建此函数是为了防止所有者认为 _keyWallet114_keyWallet215 已被盗用。如果所有者失去了对其中一个 密钥钱包1 的访问权限,也可以使用它。在这种情况下,用户可以调用 resetBindings,删除绑定的 密钥钱包 并重置安全功能。在再次调用 addBindings 并添加一组新的 密钥钱包 之前,NFKBT 现在将像传统的 ERC-721 一样运行。

_keyWallet114_keyWallet215 需要调用 resetBindings 函数的原因是,持有钱包3 能够调用 resetBindings 可能会导致 NFKBT 立即丢失。攻击者只需要获得对 持有钱包 的访问权限并调用 resetBindings

如果 3 个钱包中的 2 个钱包被盗用,那么如果攻击是恶意的,NFKBT 的所有者就无能为力了。但是,通过允许 1 个钱包被盗用,与当前其他标准不同,使用 NFKBT 标准构建的非同质化代币的持有者将获得第二次机会。

allowTransfer 函数的目的是保证安全转移2,但也可以通过 dApp 设置 默认值7 来模拟传统 ERC-721 的 默认行为3。它使用户可以高度指定他们即将进行的转移类型,同时允许用户在无限的时间内向任何人解锁所有 NFKBT。所需的安全性完全取决于用户。

此函数需要填写 4 个参数,这些参数的不同组合会导致不同的安全性级别。

参数 1 _tokenId8:这是将在转移中使用的 NFKBT 的 ID。

参数 2 _time9NFKBT 可以从当前区块时间戳开始转移的区块数。

参数 3 _address10NFKBT 将发送到的目的地。

参数 4 _anyToken11:这是一个布尔值。当为 false 时,transferFrom 函数会考虑参数 1、2 和 3。如果值为 true,则 transferFrom 函数将恢复为 默认行为4,与传统的 ERC-721 相同。

allowTransfer 函数需要 _keyWallet114_keyWallet215,并允许 持有钱包3 在先前指定的参数内进行 transferFrom。添加这些参数是为了在 持有钱包 在用户不知情的情况下被盗用时,通过限制 持有钱包 来提供额外的安全性。

allowApproval 函数在允许链上第三方代表您使用您的 NFKBT 时提供了额外的安全性。当用户遇到常见的恶意攻击(例如,耗尽 dApp)时,这尤其有用。

此函数需要填写 2 个参数,这些参数的不同组合会导致不同的安全性级别。

参数 1 _time12:批准第三方服务可以进行的时间块数,从当前块时间戳开始。

参数 2 _numberOfTransfers_13:第三方服务可以代表用户进行的交易次数。

allowApproval 函数需要 _keyWallet114_keyWallet215,并允许 持有钱包3 通过使用 approve 函数来允许第三方服务。添加这些参数是为了在授予第三方权限(该第三方代表用户使用资产)时提供额外的安全性。参数 1 _time12 限制了 持有钱包 何时可以 approve 第三方服务。参数 2 _numberOfTransfers13 限制了经批准的第三方服务可以在撤销批准之前代表用户进行的交易次数。

版权

版权和相关权利通过 CC0 放弃。

  1. 密钥钱包指的是可以调用 safeFallbackresetBindingsallowTransferallowApproval 函数的 _keyWallet1_keyWallet2。  2 3 4 5 6 7 8 9 10

  2. 当 1 个 密钥钱包 安全地批准使用 NFKBT 时,即为安全转移。  2

  3. 持有钱包是指包含 NFKBT 的钱包。  2 3 4 5 6 7 8

  4. 默认行为指的是预先存在的非同质化 ERC-721 标准中存在的行为。  2 3 4

  5. 美国联邦贸易委员会从 2021 年 1 月到 2022 年 3 月收到的加密货币诈骗报告的数量。 

  6. 根据美国联邦贸易委员会的数据,从 2021 年 1 月到 2022 年 3 月,通过加密货币诈骗窃取的金额。 

  7. 默认值是指模拟非同质化 ERC-721 默认行为 的值。  2

  8. _tokenId 表示要使用的 NFKBT 的 ID。  2

  9. allowTransfer 中的 _time 表示可以进行 transferFrom 的块数。  2

  10. _address 表示 NFKBT 将发送到的地址。  2

  11. _anyToken 是一个可以设置为 true 或 false 的布尔值。  2

  12. allowApproval 中的 _time 表示可以进行 approve 的块数。  2 3

  13. _numberOfTransfers 是第三方实体可以通过 transferFrom 代表用户进行的转移次数。  2 3

  14. _keyWallet1 是调用 addBindings 函数时设置的 2 个 密钥钱包 之一。  2 3 4 5 6 7

  15. _keyWallet2 是调用 addBindings 函数时设置的 2 个 密钥钱包 之一。  2 3 4 5 6 7

  16. PoS 协议,权益证明协议,是一种加密货币共识机制,用于处理交易并在区块链中创建新区块。 

Citation

Please cite this document as:

Mihai Onila (@MihaiORO), Nick Zeman (@NickZCZ), Narcis Cotaie (@NarcisCRO), "ERC-6809: 非同质化密钥绑定代币," Ethereum Improvement Proposals, no. 6809, March 2023. [Online serial]. Available: https://eips.ethereum.org/EIPS/eip-6809.