一个地址,一张发票:ERC-8211上的多链对账

  • Biconomy
  • 发布于 20 小时前
  • 阅读 29

ERC-8211 标准通过临时密钥和反事实地址实现“一个发票 = 一个地址”,使多链付款自动对账。

一个地址,一张发票:ERC-8211 上的多链对账

从一个技术角度,看新的以太坊标准如何实现一种以前无法企及的支付原语:一张发票本身就是一个地址,同时存在于每条链上,资金到达时立即自动支付。


想法的形态

对账,其核心问题是:这张具体的发票是否已支付? 每个处理支付的系统都必须回答这个问题,而几乎所有系统都通过维护状态来回答——一个将发票与预期金额映射的数据库表、一个监听传入转账的 Webhook 监听器、一个重试卡住支付的任务调度、一个让人类手动解决自动化失败案例的后台工具。

ERC-8211 使另一种答案成为可能。它让你将整个问题简化为链本身的一个可观测属性:

一张发票 = 一个地址。如果资金落到了地址上,发票就已支付。如果没有,则未支付。

这句话就是整个对账系统。没有数据库。没有支付状态枚举。没有“待处理 → 跨链 → 已结算 → 已对账”的状态机。只有一个地址,以及该地址上的余额。地址就是发票。余额就是答案。

这种设计多年来一直几乎可行——反事实智能账户让你在部署之前就能在每条链上获得一个地址,而账户抽象则提供了从中支出的手段。缺少的是在单个签名意图中表达以下内容的能力:“无论这个地址最终在哪个链上被充值,无论实际到达了多少,都将其转发给商家。” 没有这一点,你仍然需要一个观察者和一个数据库来确定资金落在了哪个链上以及实际有多少。

ERC-8211 正是完成这个闭环的标准。它引入了两个原语——运行时参数注入即弃多链调度——与临时密钥智能账户模式组合,使得“一个地址 = 一张发票”不仅仅是一个口号,而是一个可工作的架构。


技术构件

有四个运动部件。每一个都以某种形式独立存在。ERC-8211 的作用是让它们组合成一个单一、连贯的产品。

1. 临时密钥

每张发票都从一个全新的加密密钥对开始,在商户的浏览器标签页中生成。这个密钥只有一项任务:为这张发票签署结算意图。它永远不会离开浏览器。永不写入磁盘。当标签页关闭时,它就会消失。

伪代码:

发行发票时:
    key = 生成临时密钥对
    invoice_address = 从 key 推导智能账户地址
    向付款人显示 invoice_address

临时密钥是让其余设计安全的关键。如果使用长期密钥,每个发票地址都会从同一个根衍生,单个密钥泄露就会暴露所有发票的资金。使用临时密钥,泄露的爆炸半径就是一张发票。而且由于密钥在结算意图被签署并派遣后立即丢弃,即使那个爆炸半径也会缩小到以秒计的时间窗口。

临时密钥也是商户不托管任何东西的原因。没有需要连接的钱包,没有需要插入的签名设备,没有需要备份的助记词。商户生成密钥,推导地址,然后对该地址不再有任何责任。在发票的生命周期内,这个地址是一个黑洞:资金进入,商户退出。

2. 反事实部署:每条链上的相同地址

智能合约账户地址是部署者、实现和盐值的确定性函数。如果你在链之间固定这三个因素,地址在每条链上都是相同的,在它被部署到任何地方之前就已经相同

商户的发票地址正是以这种方式从临时密钥推导出来的。相同的字符序列在以太坊主网、Arbitrum、Optimism、Base 和 Polygon 上标识同一张发票。不存在“Arbitrum 发票地址”和“Base 发票地址”。只有一个地址。它在数学上存在于每条链上。只是尚未部署。

这是架构的基石。因为地址在每条链上都是相同的,付款人可以从他们偏好的任何链向该地址发送资金,而商户可以提出一个通用问题——有人在这里发送了钱吗?——而不需要知道该问哪个链。

在伪代码中,这个属性如下所示:

invoice_address ==
    derive(key, mainnet)  ==
    derive(key, arbitrum) ==
    derive(key, optimism) ==
    derive(key, base)     ==
    derive(key, polygon)

相同的地址。五条链。一张发票。

3. 运行时参数注入

这是 ERC-8211 的第一个原语,它使得“无论实际到达了多少”成为可表达的。

在传统的批量交易中,每个参数都是一个字面值,在用户签名时就被冻结。要转发的金额、接收者、授权额度——所有这些都固化在 calldata 中。如果世界在签名和执行之间发生了变化(桥接费用变化、中继器填充了略有不同的金额、预言机价格波动),批次要么回滚,要么导致价值残留。

ERC-8211 添加了第二种参数:获取器(fetcher)。获取器不是一个值;它是在执行时读取值的指令。该标准定义了支付关心的常见案例的获取器:读取 ERC-20 余额、读取授权额度、读取原生余额,或者——对于除此之外的任何情况——调用任何合约的视图函数并使用返回结果。

每个获取器都带有约束:比较器(>=, <=, ==),解析后的值必须满足这些约束。约束不是建议性的。它是一个门(gate)。如果获取器读取的值不满足约束,调用不会触发。执行器会等待并重试,直到约束通过或截止时间到期。

对于我们的发票流程,相关的获取器会在执行时读取目标链上 invoice_address 的实时 USDC 余额:

提现步骤:
    from:   invoice_address on payout_chain
    to:     merchant_payout_address
    token:  USDC
    amount: balance_of(USDC, invoice_address) at runtime
    gate:   amount >= invoice_amount_minus_acceptable_slippage

注意两点。第一,amount 字段不是一个数字。它是一个读取操作。地址上实际累积的 USDC——扣除桥接费用、中继器行为、费用漂移后——都会被转发。商户收到的正是实际到达的数量,而不是一个估计值。第二,门本身就是对账谓词:“只有在有足够的钱作为实际支付时才触发。”如果余额不足——因为还没有人支付——门失败,该步骤不触发。如果余额从未到达,该步骤从不触发,截止时间会清理掉它。

运行时读取不是一种变通方法。它是该标准表达支付中一个事实的方式:你可以花费的金额就是你实际拥有的金额。

4. 即弃多链调度

ERC-8211 的第二个原语将对账从一个追踪问题转变为一个调度问题。

一旦步骤的执行可以基于现实世界的状态进行门控,你就可以预先编写多个批次并全部派遣,知道只有相关的批次才会触发。其他的不会嘈杂地回滚;不会留下残留物。它们只会永远无法满足其门控条件,并在截止时间过期。

对于发票流程,这表现为:在结算时,为每个支持的源链构建一个超交易(supertransaction),并并行派遣。每个超交易有效地说:“如果资金到达了这里,在这个链上,做任何必要的事情把它们送到商户那里。”

对于与商户支付链匹配的链,超交易有一个步骤:

批次(同链):
    提现 USDC
        from:   invoice_address on payout_chain
        to:     merchant_payout_address
        amount: balance_of(USDC, invoice_address) at runtime
        gate:   amount >= invoice_amount

对于其他每条链,超交易有三个步骤:

批次(跨链,源链 != 支付链):
    在 origin_chain:
        approve  bridge 花费 invoice_amount
        deposit  invoice_amount into bridge,
                 destination = invoice_address on payout_chain

    在 payout_chain:
        提现 USDC
            from:   invoice_address on payout_chain
            to:     merchant_payout_address
            amount: balance_of(USDC, invoice_address) at runtime
            gate:   amount >= acceptable_amount_after_bridge_fees

所有五个批次都由临时密钥一次性签署并同时派遣。从这一点开始,商户标签页就没事可做了。临时密钥在完成使命后被丢弃。

接下来发生的事情是优雅的部分。付款人从一个链付款——比如 Arbitrum。Arbitrum 批次的门(“资金是否到达了 Arbitrum 上的 invoice_address?”)通过;桥接步骤触发,然后等待跨链填充,然后支付链上的运行时余额读取通过,提现步骤将实际收到的金额交付给商户。

其他四个批次仍然在执行器中存活。它们正在观察各自在 Mainnet、Optimism、Base 和 Polygon 上的 invoice_address。这些地址没有余额。它们的门永远无法通过。当它们的截止时间到来时,这些批次静默过期,不留任何链上残留。

没有中央观察者协调这个。没有服务决定哪个链“胜出”。标准的门控语义在批次级别解决了这个问题,而且是并行的,没有共享状态。


为什么组合才是关键

四个部分——临时密钥、反事实地址、运行时注入、多链调度——每一个单独来看都很有趣。它们之所以重要,是因为它们共同使一个观察成为真实:

资金存在于 invoice_address 就是完整的支付记录。

看看这意味着什么:

检测是隐式的。 你不需要一个链观察者来知道发票已支付。支付行为就是将资金放置到地址的行为。将资金放置到地址的行为会触发门。触发就是检测。

对账是几何的。 商户的问题——“发票 X 已支付了吗?”——简化为在一个已知地址上读取余额。任何 RPC 节点、任何区块浏览器、任何后端服务都可以回答,而无需与任何其他东西协调。没有“真相来源”需要保持同步,因为链就是真相来源,而地址就是索引。

恢复是一等公民。 在生产环境中,invoice_address 背后的智能账户实现可以配置一个指定的恢复地址——一个商户控制的钱包,保留在资金被困时(临时密钥被丢弃后的延迟付款、付款人向已结算的发票发送资金、意外代币落到地址)清空账户的权限。恢复路径存在于账户级别,独立于临时密钥的生命周期,所以资金永远不会永久搁浅。这不是参考演示的一部分(为了保持代码最小),但这是生产级配置:临时密钥用于正常结算,恢复地址作为安全网。

双重花费在协议层面是不可能的。 一张发票只能支付一次,因为提现步骤的门只满足一次。第二次向同一地址的付款会在已派遣的批次已触发或已过期之后到达,而且没有剩余的意图被授权从该地址支出。该地址是一个一次性邮箱。

跨链不会碎片化抽象。 因为地址在每条链上都相同,“在 Arbitrum 上支付”和“在 Base 上支付”不是两种不同的状态。它们是相同的状态——已支付——从不同的链上观察。对账模型没有源链的概念。它只有一张发票和一个支付链。


整个流程的伪代码

剥离到骨架,整个流程只是几十行意图代码。

发行发票:
    key             = 在浏览器标签页中生成临时密钥对
    invoice_address = 从 key 推导智能账户地址
    返回 invoice_address, supported_chains

付款人(带外):
    发送 USDC 到 invoice_address
    在任意方便的 supported_chains 上

结算发票:
    对于每个 supported_chain:
        如果 supported_chain == payout_chain:
            batch = [\
                提现 USDC\
                    从 invoice_address 在 payout_chain\
                    到   merchant_payout\
                    金额: balance_of(USDC, invoice_address) at runtime\
                    门:  amount >= invoice_amount\
            ]
        否则:
            quote = 获取桥接费用报价
            batch = [\
                在 supported_chain 上 approve bridge,\
                向 bridge 存入 invoice_amount,\
                    目标 = invoice_address 在 payout_chain,\
\
                在 payout_chain 上提现 USDC\
                    从 invoice_address\
                    到   merchant_payout\
                    金额: balance_of(USDC, invoice_address) at runtime\
                    门:  amount >= invoice_amount - acceptable_slippage\
            ]

        使用临时密钥签署 batch
        派遣 batch

    丢弃临时密钥

对账:
    invoice_address 在每个链上的余额是否仍为零?
        未支付。
    提现步骤是否在任何地方触发了?
        已支付。

所有原本作为服务的工作——链观察者、支付状态数据库、跨链协调器、粉尘清扫器、卡住支付恢复脚本——都被该标准的一个属性替代了。该标准保证提现步骤在且仅在资金实际存在于发票地址时触发。商户不需要验证这一点;执行器通过构造已经完成了验证。


ERC-8211 的具体贡献

值得精确指出哪些部分是新的。

反事实地址自 CREATE2 以来就已存在。临时密钥是一种模式,而不是一种协议。智能账户和账户抽象已有多年历史。桥接已经运行了很长时间。

ERC-8211 增加的——并且没有之前的标准提供过的——是在签名批次内部表达有条件、状态依赖的执行的能力,这种形式可跨执行器移植,并且不需要为每个用例定制链上合约。具体来说:

  • 签名批次可以包含在执行时解析的参数,而不是签名时解析。
  • 这些参数可以通过执行器强制执行的约束进行门控。
  • 这些约束可以引用实时的链上状态——余额、授权额度、任意视图函数结果。
  • 同一个批次可以跨越多个链,后续步骤的门控取决于前序步骤的结果。
  • 无法满足其约束的批次会过期,而不是嘈杂地回滚。

去掉这些属性,发票即地址的模式就会崩溃。没有运行时注入,提现步骤的金额必须是静态猜测,你又回到了填充滑点、残留粉尘的老路。没有约束,提现步骤无论资金是否到达都会触发,你就会失去使对账成为一个单比特问题的门控。没有多链调度,你必须预先选择一个单一的链,又回到了“请先桥接”的用户体验。没有优雅到期,你必须追踪并清理四个未触发的批次。

ERC-8211 是对批量模型的最小增加集合,使所有这些属性同时可表达。这就是为什么该产品适合在标准之内,而不是围绕标准。


一句话产品

商户生成一个临时密钥,推导出一个地址,并向付款人展示该地址。付款人从任何支持的链向该地址发送 USDC。商户签署五个条件批次——每个链一个——内容为“如果这个地址上有资金,将实际存在的资金转发到我的财库,在财库所在的链上。”无论资金落在哪个链上,该批次触发;其他批次过期。商户的对账就是在一个单一地址上读取一个单一数字的余额。

在“付款人支付”和“商户收到”之间的所有事情,都是标准在履行职责。临时密钥处理授权。反事实地址处理无处不在。运行时参数处理未知金额问题。即弃调度处理未知链问题。组合通过消除对账的需要来处理对账。

erc8211.com 阅读标准。 一张发票就是一个地址。链就是账本。标准就是系统。

  • 原文链接: blog.biconomy.io/one-add...
  • 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

请先 登录 后评论
Biconomy
Biconomy
江湖只有他的大名,没有他的介绍。