DEFI - 标题 - Umaprotocol

该UMIP (UMA改进提案) 定义了Across V3的协议规范更新,旨在改进跨链桥接的功能,包括支持用户和中继者之间有时间限制的协议,使第三方能够使用Across作为高效的跨链结算系统,允许在未完成中继的情况下直接在原始链上退款,并降低中继者的风险。此次更新需要更新ACROSS-V2价格标识符的规范,以便UMA DVM验证Across v3提议的结算包是否有效。

标题

  • UMIP 179
  • 标题:更新 ACROSS-V2 价格标识符以支持 Across v3
  • 作者:paul@across.to
  • 状态:最后征求意见
  • 创建时间:2023-02-17
  • Discourse 链接:N/A

摘要

此 UMIP 定义了 Across V3 的更新协议规范。它弃用了现有 Across 协议规范的特定部分,如 UMIP-157 中所述。

动机

Across v3 是对 Across v2 规范的重大改进,在简化现有协议的同时,增加了对新功能的支持。

基本原理

Across 协议建议更新其规范,以更好地支持跨链桥接的基于意图的未来。这包括:

  • 支持用户和中继者之间可执行的、有时限的协议,以执行意图。
  • 使第三方能够使用 Across 作为高效、模块化的跨链结算系统。
  • 允许存款人在他们的中继未在存款时指定的时间范围内完成的情况下,直接在源链上获得退款。
  • 通过消除对链上发布桥转移的 realizedLpFeePct 组件的需求来降低中继者的风险敞口。
  • 支持中继者独占性,以减少链上 Gas 拍卖。

需要更新 ACROSS-V2 价格标识符的规范,UMA DVM 才能验证 Across v3 提出的结算包是否有效。

技术规范

概述

UMIP-157 中的以下部分明确保留供 Across v3 使用:

全局常量

UMIP-157 中的所有全局常量都将保留供 Across v3 使用。AcrossConfigStore 中除了 UMIP-157 之外存储的全局变量是:

  • "LITE_CHAIN_ID_INDICES"
    • 这应该是链 ID 号码的字符串化列表。此列表中的每个链也应出现在 CHAIN_ID_INDICES 列表中。此列表中不应有重复项;任何包含重复项的 LITE_CHAIN_ID_INDICES 更新都可能被忽略。可以从 LITE_CHAIN_ID_INDICES 列表中删除链,以删除其“轻量级”链指定。

数据类型

Across v3 定义了以下数据类型:

V3RelayData

V3RelayData 类型支持资金进出 SpokePool 实例的转移。V3RelayData 定义如下: 名字 类型 描述
depositor bytes32 在源链上进行存款的地址。
recipient bytes32 目标链上的接收者地址。
exclusiveRelayer bytes32 可选择的独家中继者,可以在排他性截止日期之前完成存款。
inputToken bytes32 存款人存在源链上的 token。
outputToken bytes32 接收人在目标链上收到的 token。
inputAmount uint256 存款人存入的 inputToken 数量。
outputAmount uint256 接收人收到的 outputToken 数量。
originChainId uint256 源 SpokePool 的链 ID。
depositId uint256 用于标识源链上存款的 ID。
fillDeadline uint32 目标链上的 Unix 时间戳,在此之后将无法再完成存款。
exclusivityDeadline uint32 目标链上的可选 Unix 时间戳,在此之后任何中继者都可以完成存款。
message bytes 作为中继的一部分转发给接收者的可选数据。
注意
  • V3RelayData 指定地址(depositorrecipient 等)的 bytes32 表示形式,以便与非 EVM 链交互。提供的 EVM 地址应提升为 bytes32 类型,并将上面的 12 个字节清零。

V3RelayDataLegacy

V3RelayDataLegacy 类型支持向后兼容,但计划弃用。V3RelayDataLegacyV3RelayData 类型的差异如下: 名字 类型
depositor address
recipient address
exclusiveRelayer address
inputToken address
outputToken address
depositId uint32

V3RelayExecutionParams

V3RelayExecutionParams 类型由中继者或执行者在完成填充时提供。V3RelayExecutionParams 定义如下: 名字 类型 描述
relay V3RelayData 与要填充的源链存款对应的 V3RelayData 对象。
relayHash bytes32 V3RelayData 对象的 keccak256 哈希。另请参见计算 RelayData 哈希
updatedOutputAmount uint256 接收者要收到的有效金额。这可能与存款 outputAmount 不同。
updatedRecipient bytes32 有效的接收者地址。这可能与存款接收者不同。
updatedMessage bytes 要由目标 SpokePool 执行的有效消息(如果有)。
repaymentChainId uint256 中继者完成填充请求的偿还链 ID。此字段与慢速填充无关。

FillStatus

RelayData -> FillStatus 的映射存储在每个 SpokePool 实例中。可以使用存款的哈希 V3RelayData 查询此映射,从而可以查询相应填充的状态。

名字 描述
Unfilled 0 SpokePool 没有对应于 V3RelayData 哈希的已知状态。
RequestedSlowFill 1 已为此 V3RelayData 哈希发出慢速填充请求。先前已发出相应的 RequestedV3SlowFill 事件。
Filled 2 已完成此 V3RelayData 哈希的填充(快速或慢速)。

FillType

每个 FilledV3Relay 事件都会发出一个 FillType 实例(请参见下文)。

名字 描述
FastFill 0 中继由中继者作为快速填充完成。
ReplacedSlowFill 1 最初请求通过慢速填充完成中继,但随后由中继者快速填充。
SlowFill 2 中继通过慢速填充完成。

V3RelayExecutionEventInfo

每个 FilledV3Relay 事件都会发出一个 V3RelayExecutionEventInfo 实例(请参见下文)。

名字 类型 描述
updatedRecipient bytes32 正在转移的资金的接收者。这可以是原始存款中标识的 recipient,也可以是 RequestedSpeedUpV3Deposit 事件之后的更新 recipient
updatedOutputAmount uint256 完成填充的中继者发送给 updatedRecipient 的金额。
updatedMessage bytes 作为中继的一部分转发给接收者的数据。
repaymentChainId uint256 存款人指定的用于填充偿还的链。
fillType FillType 完成的填充类型(请参见上面的 FillType)。

V3SlowFill

每个 FilledV3Relay 事件都会发出一个 V3SlowFill 实例(请参见下文)。

名字 类型 描述
relayData V3RelayData V3RelayData 实例,用于将 SlowFill 与 V3FundsDepositedRequestedV3SlowFill 事件唯一关联。
chainId uint256 完成 SlowFill 的 SpokePool 的链 ID。
updatedOutputAmount uint256 作为 SlowFill 的一部分发送给 recipient 的金额。这通常应等于或大于 V3FundsDeposited outputAmount
repaymentChainId uint256 存款人指定的用于填充偿还的链。
fillType FillType 完成的填充类型。
注释

通常将 updatedRecipient 字段设置为来自相应 V3FundsDeposited 事件的 recipient。如果中继者使用随附的 RequestedSpeedUpV3Deposit 事件完成填充,则 updatedRecipient 将设置为更新批准的地址。

事件

Across V3 定义了以下事件:

  • FundsDeposited
  • V3FundsDeposited
  • RequestedSpeedUpDeposit
  • RequestedSpeedUpV3Deposit
  • FilledRelay
  • FilledV3Relay
  • RequestedSlowFill
  • RequestedV3SlowFill
  • ClaimedRelayerRefund

事件弃用

以下事件标记为已弃用。有关更多信息,请参见迁移

  • V3FundsDeposited
  • RequestedSpeedUpV3Deposit
  • FilledV3Relay
  • RequestedV3SlowFill

FundsDeposited, V3FundsDeposited

FundsDeposited 事件为单个存款发出唯一 V3RelayData。未定义其他字段。 V3FundsDeposited 事件为单个存款发出唯一 V3RelayDataLegacy。未定义其他字段。

注意
  • 这些事件的消费者应附加 originChainId,以避免无意中混合来自不同链的事件。
  • 不需要 FundsDepositedV3FundsDeposited outputToken 字段是已知的 HubPool l1Token。Across v3 支持协议内任意 token 交换。
  • exclusiveRelayer 标识的地址有权在 exclusivityDeadline 过去之前在目标链上完成中继。
  • 如果 exclusivityDeadline 设置为过去的时间戳,则任何地址都有资格填充中继。
  • 在指定的 fillDeadline 之后仍然未完成的任何存款应通过后续结算包退还给源 SpokePool 上的 depositor 地址。

RequestedSpeedUpDeposit, RequestedSpeedUpV3Deposit

RequestedSpeedUpDeposit 事件发出以下数据。

名字 类型 描述
depositId uint256 要更新的相应 FundsDeposited 事件的 depositId。
depositor bytes32 要更新的相应 FundsDeposited 事件的存款人。
updatedOutputAmount uint256 存款人批准的新 outputAmount。这应低于原始存款 outputAmount
updatedRecipient bytes32 接收资金的新接收者。
updatedMessage bytes 要提供给接收者的新消息。
depositorSignature bytes 存款人授权上述更新字段的签名。

RequestedSpeedUpV3Deposit 事件发出以下数据:

名字 类型 描述
depositId uint32 要更新的相应 V3FundsDeposited 事件的 depositId。
depositor address 要更新的相应 V3FundsDeposited 事件的存款人。
updatedOutputAmount uint256 存款人批准的新 outputAmount。这应低于原始存款 outputAmount
updatedRecipient address 接收资金的新接收者。
updatedMessage bytes 要提供给接收者的新消息。
depositorSignature bytes 存款人授权上述更新字段的签名。
注意
  • 中继者可以选择在填充中继时附加来自 RequestedSpeedUpDepositRequestedSpeedUpV3Deposit 事件的更新请求,但没有义务使用更新请求。

RequestedSlowFill, RequestedV3SlowFill

RequestedSlowFill 事件通过应用以下调整来扩展 V3RelayData 类型:

名字 类型 描述
message omitted 为了支持 messageHash 字段,省略了此字段。
messageHash bytes32 V3RelayData message 字段的 keccak256 哈希,其中消息为非空,对于空消息,为 bytes32(0)。此字段包含在 V3RelayData message 字段的位置。

RequestedV3SlowFill 事件发出一个 V3RelayDataLegacy 实例。

注意
  • 这些事件在目标链上发出,并向提案人发出信号,表明已为特定存款请求慢速填充。

FilledRelay, FilledV3Relay

FilledRelay 事件通过应用以下调整来扩展 V3RelayData 类型: 名字 类型 描述
message omitted 为了支持 messageHash 字段,从 FilledRelay 事件中省略了此字段。
messageHash bytes32 V3RelayData message 字段的 keccak256 哈希,其中消息为非空,对于空消息,为 bytes32(0)。此字段包含在 V3RelayData message 字段的位置。
relayer bytes32 在目标 SpokePool 上完成中继的地址。
repaymentChainId uint256 要更新的相应 V3FundsDeposited 事件的 depositId。
relayExecutionInfo V3RelayExecutionEventInfo 有效的 recipientmessageoutputAmount,以及执行的 FillType(FastFill、ReplacedSlowFill、SlowFill)。
FilledV3Relay 事件通过添加以下字段来扩展 V3RelayDataLegacy 类型: 名字 类型 描述
relayer address 在目标 SpokePool 上完成中继的地址。
repaymentChainId uint256 要更新的相应 V3FundsDeposited 事件的 depositId。
relayExecutionInfo V3RelayExecutionEventInfo 有效的 recipientmessageoutputAmount,以及执行的 FillType(FastFill、ReplacedSlowFill、SlowFill)。
注意
  • 这些事件的消费者应附加 destinationChainId 属性,以避免无意中混合来自不同链的事件。

根包提案

要求

根包提案应包含以下内容: 名字 类型 描述
bundleEvaluationBlockNumbers uint256[] 有序的区块编号数组,表示每个相应 chainId 的提案的结束区块。
poolRebalanceLeafCount uint8 poolRebalanceRoot 表示的 PoolRebalanceLeaf 实例的数量。
poolRebalanceRoot bytes32 表示包含提案的 PoolRebalanceLeaf 对象有序数组的树的 Merkle 根。
relayerRefundRoot bytes32 表示包含提案的 RelayerRefundLeaf 对象有序数组的树的 Merkle 根。
slowRelayRoot bytes32 表示包含提案的 SlowFillLeaf 对象有序数组的树的 Merkle 根。

池重新平衡叶

PoolRebalanceLeaf 应包含以下内容:

名字 类型 描述
chainId uint256 PoolRebalanceLeaf 引用的 SpokePool chainId
bundleLpFees uint256[] 相应 l1TokenbungleLpFee 值的有序数组。
netSendAmounts uint256[] 相应 l1TokennetSendAmount 值的有序数组。
runningBalances uint256[] 相应 l1TokenrunningBalance 值的有序数组。
groupIndex uint256 指示是否应将相应的 RelayerRefundSlowRelay 根中继到相应的 SpokePool。
leafId uint8 PoolRebalanceLeaves 有序数组中 PoolRebalanceLeaf 的索引。
l1Tokens address[] HubPool l1Token 地址的有序数组。
注意
  • 池重新平衡叶的格式与 Across v2 中的格式相同。

中继者退款叶

名字 类型 描述
chainId uint256 RelayerRebalanceLeaf 引用的 SpokePool chainId
leafId uint8 RelayerRefundLeaves 有序数组中 RelayerRefundLeaf 的索引。
l2TokenAddress address[] RelayerRefundLeaf 使用的 SpokePool token。
amountToReturn uint256 要返回到 HubPool 的 l2TokenAddress 的数量。
refundAddresses uint256[] 要由此 RelayerRefundLeaf 退款的地址的有序数组。
refundAmounts uint256[] 要退还给相应 refundAddressl2TokenAddress 数量的有序数组。
注意
  • 中继者退款叶的格式与 Across v2 中的格式相同。
  • Across v3 扩展了 RelayerRefundLeaf 的实用程序,以包括在 V3FundsDeposited fillDeadline 过期时在源链上发放存款人退款。

慢速中继叶

Across v3 SlowRelayLeaf 对象由 V3SlowFill 数据类型定义。

注意
  • 慢速中继叶的格式已从 Across v2 更新。

定义

存款

Deposit 定义为以下事件之一的实例:

  • FundsDeposited
  • V3FundsDeposited

填充

Fill 定义为以下事件之一的实例:

  • FilledRelay
  • FilledV3Relay

慢速填充请求

Slow Fill 定义为以下事件之一的实例:

  • RequestedSlowFill
  • RequestedV3SlowFill

RelayData

RelayData 定义为以下数据类型之一的实例:

  • V3RelayData
  • V3RelayDataLegacy

包区块范围

Bundle Block Range 是给定提案的开始和结束区块对。有关标识 Bundle Block Range 的指导,请参见标识包区块范围

填充状态

当目标 SpokePool FillStatus 映射显示 Deposit RelayData 哈希的状态 Filled 时,Deposit 被认为已在目标链上被 Filled

方法

标识 SpokePool 合约

可以通过查询 HubPool.crossChainContracts() 来获取特定链的当前 SpokePool 地址。必须在查询中指定 chainId。如果发生 SpokePool 迁移,可以通过抓取 HubPool CrossChainContractsSet 事件来识别历史 SpokePool 地址。

标识“轻量级”部署

如果 AcrossConfigStore 中的 LITE_CHAIN_ID_INDICES 值包括存款的原始链或目标链(分别截至存款的 quoteTimestamp 字段),那么我们认为存款“始发于”或“目的地为”“轻量级链”。这些链对中继者偿还和慢速填充施加了约束。

将 SpokePool token 解析为其 HubPool 等效项

为了在给定 SpokePool token 的情况下识别等效的 HubPool token,应遵循以下步骤:

  1. 查找最新的 SetRebalanceRoute 事件,其区块时间戳位于或早于相关的 HubPool 区块编号,其中相关的 SpokePool 链 ID 和 token 地址与 SetRebalanceRoute destinationChainIddestinationToken 字段匹配。
    • 如果是 Deposit 事件,则通过将 quoteTimestamp 解析为 HubPool 区块编号来标识相关的 HubPool 区块编号。
  2. 从生成的 SetRebalanceRoute 事件中,选择关联的 l1Token 字段。
  3. SetPoolRebalanceRoute 事件中搜索相同的 l1TokendestinationChainId,其时间位于或早于适用的 HubPool 区块编号。
  4. 使用在步骤 2 中找到的 l1Token 值,搜索最新的 SetRebalanceRoute 事件,其时间位于或早于适用的 HubPool 区块编号,其中 l1TokendestinationChainId 与提取的 l1Token 和 SpokePool 链 ID 匹配。如果找到匹配项,则地址匹配并被视为跨链等效项。

标识包区块范围

除了 UMIP-157 中的描述之外:

  • 如果检测到 RPC 提供程序数据不一致,提案人可以选择减小每个链的提案区块范围,并且
  • 如果提案人无法安全地增加包区块范围,或者没有事件可以提出超出先前的包区块范围,则允许“软暂停”链。在这种情况下,提案人可以重复 DISABLED_CHAINS 的过程,从先前包的结束区块提出提案。

重构 FilledRelay 消息

FilledRelay 事件会发出 messsageHash 字段。此字段设置如下:

  • RelayData message 字段为空 (0x) 时:bytes32(0),或者
  • RelayData message 字段为非空 (0x...) 时:keccak256(message)

计算 RelayData 哈希

RelayData 哈希计算为参数 relayDatadestinationChainId 的 ABI 编码表示形式上的 keccak256 哈希,其中:

  • relayData 的类型为 V3RelayDataV3RelayDataLegacy

  • destinationChainId 的类型为 uint256

  • FilledRelay 事件数据省略 message 字段时,应根据重构 FilledRelay 消息中指定的步骤填充 message 字段。

注意
  • 由于在哈希之前地址提升为 bytes32,因此此方法为同一输入数据生成相同的 V3RelayDataV3RelayDataLegacy 哈希。

计算中继者偿还和存款人退款

为了计算中继者偿还,将完成以下步骤:

  • 验证填充
  • 验证预填充
  • 查找过期的存款
  • 查找无法填充的存款
注意
  • 存款人退款通过中继者退款工作流程发放。

验证填充

应通过验证以下内容来认为在目标 SpokePool 上的 Bundle Block Range 内发出的每个 Fills 有效:

  1. Fill 事件 FillType 字段未设置为 SlowFill,并且
  2. 组件 RelayData 精确映射到一个或多个在相关的 originChainId 上发出的相应 Deposit 事件,并且
  3. 相应的 Deposit 事件发生在源链 SpokePool 上的 Bundle Block Range 内或之前。
注意
  • 如果 Deposit 事件指定 outputToken bytes32(0)(即零地址),则应替换为目标链上的等效 SpokePool token。为了确定 RelayData 等效性,应使用更新/替换的 outputToken 代替零地址。如果在 Deposit quoteTimestamp 时目标链上没有等效的 SpokePool token,则无论此存款是否可在目标链上填充,都不会偿还此存款的任何填充。
  • 可以通过比较两个 RelayData 对象的 keccak256 哈希来确定 RelayData 相等性。
  • SlowFill 类型的填充有效,但与计算中继者偿还无关。

验证预填充

对于在 Bundle Block Range 内发出的每个 Deposits,其中没有在目标链上的 Bundle Block Range 内标识相应的 Fill,请根据以下标准标识有效的 Fill

  1. 验证目标链FillStatus(对于Deposit RelayData)是否于提案的目标链结束区块编号为 Filled
  2. 解析在目标链上对应的Fill
  3. 验证FillTypeFastFillReplacedSlowFill以及Fill当前Bundle Block Range的在目标链SpokePool之前。
注意
  • 未规定用于解析目标链上 Fill 的具体方法。eth_getLogs请求可以促进此操作,如果需要,可以通过二进制搜索FillStatus字段来缩小Bundle Block Range。这留作实施决定。

查找过期的存款

为了计算存款人退款,应通过验证以下内容来认为每个 Deposit 已过期:

  1. fillDeadline时间戳在目标SpokePool上的Bundle Block Range内已过(即fillDeadline在前述起始和结束区块的目标链包的区块时间戳(block.timestamp)之间),
  2. 目标 SpokePool 上的 FillStatusBundle Block Range 结束时设置为 UnfilledSlowFillRequested
注意
  • 过期的存款应退还给源 SpokePool 上的 depositor 地址。
  • 存款人退款将作为中继者退款程序的一部分发布。
  • 应该将fillDeadline时间戳解析为目标链上的区块编号,以确定是否包含在Bundle Block Range

查找无法填充的存款

为了计算存款人退款,通过验证以下内容来认为每个重复的Deposit不可填充:

  1. Deposit与另一个Deposit相同。
  2. 对于Deposit目标链FillStatus设置为Filled
  3. 目标链Fill``FillTypeSlowFill
  4. 在当前BundleBlockRange发生了目标链Fill或发生在当前BundleBlockRange发生了Deposit
注意
  • 当其RelayData匹配时,认为Deposits是相同的。
  • 当在初始相同实例发出Deposits,那么认为Deposits是重复。

查找慢速填充请求

针对每个在目标 SpokePool 上的 Bundle Block Range 内发出的 Slow Fill Request,通过验证以下内容来认为有效:

  1. fillDeadline大于destinationChainId包结束区块的时间戳block.timestamp
  2. 通过在一个或多个发生在源 SpokePool 上或 Bundle Block Range 之前的对应 Deposit 事件匹配Slow Fill Request RelayData
  3. 在最早匹配的存款quoteTimestampinputTokenoutputToken地址是等效的,
  4. Bundle Block Range结束时,相关RelayData哈希的目标 SpokePool FillStatus 映射是SlowFillRequested
  5. 相应的 originChainIddestinationChainId不是轻量级链。
注意
  • 通过提供 Deposit发出的相关RelayData完整副本实现Slow Fill Request
  • 生成的经验证的Slow Fill Request集应包含在后续根包提案中作为慢速填充。
  • Slow Fill Request可以与之前的包的Deposit对应。

查找提前慢速填充请求

在隐含提前的Slow Fill Request时,应如下验证Slow Fill Request

  1. 相对于destinationChainId包结束区块编号,fillDeadline已经过时,
  2. 在最早匹配的存款quoteTimestampinputTokenoutputToken地址是等效的,
  3. originChainIddestinationChainId都不是Lite链。
注意
  • 如果在当前Bundle Block Range没有标识Slow Fill Request,那么在其中FillStatusSlowFillRequested情况下,隐含提前的Slow Fill Request的表示当前Bundle Block Range内发出一个Deposit相当于目标链上在当前Bundle Block Range结束日期。这可能是由于在当前包之前提交Slow Fill Request

计算 LP 费用

每个有效Fill都需要缴纳 LP 费用。计算 LP 费用的步骤如UMIP-136 添加 IS_RELAY_VALID 作为支持的价格标识符中定义,并作以下修改:

  • 应该使用AcrossConfigStore合约确定正确的速率模型,而不是使用RateModelStore合约。
  • 应该使用HubPool``liquidityUtilizationCurrent()liquidityUtilizationPostRelay()函数而不是BridgePool变体。
  • 通过遵循上面概述的匹配步骤,应该将事件inputToken从 SpokePool 地址映射到 HubPool l1Token地址。
  • LP 费用在由Deposit和由中继者指定repaymentChainId指定originChainId计算, 其中relayExecutionInfo.FillType != SlowFill然后destinationChainId在其它地方填写Fill。 如果originChainId等于repaymentChainId那么 LP 费用应为 0%。
注意
  • LP 费用通常被引用为Deposit inputAmount的乘数,并在本文档中的其他地方命名为realizedLpFeePct

计算捆绑 LP 费用

SpokePool 和 token 对的Bundle Block Range的捆绑 LP 费用可以通过将每个以下验证事件的适用 LP 费用相加来确定:

  • FilledRelay
  • FilledV3Relay
注意

可以为每个FilledRelayFilledV3Relay设置多个关联的存款事件。如果没有慢速填充,如果存在多个匹配的存款事件,那么将为每个事件支付多个 LP 费用。

计算中继人偿还

对于每个已验证匹配的Deposit事件,中继人偿还金额应如下计算:

  • (inputAmount * (1 - realizedLpFeePct)) / 1e18,其中在 HubPool 区块编号(与相关Deposit``quoteTimestamp相对应)的 HubPool l1TokenoriginChainIdrepaymentChainId集中计算realizedLpFeePct
  • 适用的速率模型应从相关l1Token的AcrossConfigStore合约中寻找。
  • 对于满足中继人偿还要求的给定Fill,每个匹配的Deposit会根据其quoteTimestamp生成不同的偿还。

应用于repaymentToken将等于在repaymentChainIdinputToken一样的等效token,其中按以下方式确定应用的repaymentChainId(全部在相关包的中心链接结束区块时):

  • 如果originChainIdLite chainoriginChainId,ELSE
  • 如果没有PoolRebalanceRoute存在于Fill``originChainIdinputToken中:originChainId,ELSE
  • 如果没有PoolRebalanceRoute存在于Fill``repaymentChainIdrepaymentToken中:originChainId,ELSE
  • Fill中指定的repaymentChainId

应用于repayment address应如下判定:

  • 如果Fill``relayer地址在用于应用于repaymentChainId有效:relayer,ELSE
  • Fill``msg.sender地址。在这种情况下,如果PoolRebalanceRoute存在于Fill``originChainId输出链接上的outputTokeninputToken两者上,那么可以将应用于repaymentChainId修改为 destinationChainId

由于不应用于repaymentChainId,那么如果应用于repayment address无效,偿还应该被放弃。

以上规则强制执行中继人偿还的repaymentToken始终等于在相关包的中心链接结束区块时等于一个等效原始inputToken#### 计算慢速填充更新后的输出金额 为了计算为 SlowFill 向接收者发行的金额,中继器费用应通过应用以下程序置零:

  • updatedOutputAmount = (inputAmount * (1 - realizedLpFeePct)) / 1e18,其中 realizedLpFeePctoriginChainIddestinationChainId 之间最早匹配的存款的 quoteTimestamp 计算。
约束
  • 在确定 SlowFill 金额时,应考虑 DepositoutputAmount
注意
  • DepositoutputAmount 指定 recipient 将收到的确切金额,用于标准填充,因此不包括任何中继器或 LP 费用。

查找起始运行余额

起始运行余额定义为之前的成功(无争议)Root Bundle Proposal 的累计运行余额。

每个唯一的 l1TokenrepaymentChainId 对的起始运行余额应按如下方式确定:

  1. 搜索之前的 RootBundleExecuted 事件,其中:
    1. RootBundleExecutedchainIdrepaymentChainId 匹配,并且
    2. l1Token 地址出现在 l1Tokens 数组中。
  2. 对于之前的事件,识别 l1Tokens 数组中 l1Token 的索引,并在 runningBalances 数组中查找对应的索引。
  3. 如果未识别到之前的 runningBalance,则起始运行余额应默认为 0。

计算运行余额和净发送金额

计算 l1TokenchainId 对的运行余额的程序应执行以下步骤:

  1. 将运行余额初始化为 0。

  2. 添加中继器退款:

    • 对于每组经过验证的 FillPre-fill 事件,初始化一个运行余额为 0,并添加中继器还款。
  3. 添加存款退款:

    • 对于在 Bundle Block Range 内过期或被认为无法填充的每组 Deposit 事件,对原始链上的总存款退款求和。将金额添加到该链的现有中继器退款中。
  4. 添加慢速填充:

    • 对于每组经过验证的 Slow Fill Requests,将每个慢速中继的 updatedOutputAmount 添加到该组的运行余额中。
  5. 减去未执行的慢速填充的超额部分:

    • 对于每组经过验证的 Fills,其中 FillTypeReplacedSlowFill,并且当前 bundle 数据中没有具有相同中继数据哈希的有效 Slow Fill Request,则从运行余额中减去 SlowFill 的 updatedOutputAmount,以确认 SlowFill 将永远不会被执行,因为填充金额已经被转移。
    • 对于上面标识的每个过期的存款退款,如果存款目标链上的 FillStatusRequestedSlowFill,并且匹配的 slow fill request 不在当前的 bundle 范围内,则从运行余额中减去相关的 SlowFill updatedOutputAmount,以确认 SlowFill 无法超过 fillDeadline 执行。
  6. 添加所选 l1TokenchainId 对的起始运行余额。

  7. 根据 计算净发送金额 的结果,为每个 l1TokenchainId 对设置净发送金额并更新运行余额,如下面的算法所述:

    
    spoke_balance_threshold = 此 token 的 `spokeTargetBalances` 中的 "threshold" 值
    spoke_balance_target = 此 token 的 `spokeTargetBalances` 中的 "target" 值

net_send_amount = 0

如果运行余额为正,则 hub 欠 spoke 资金。

if running_balance > 0: net_send_amount = running_balance running_balance = 0

如果运行余额为负,则从 spoke 中提取足够的资金到 hub,以使运行余额恢复到其目标值

else if abs(running_balance) >= spoke_balance_threshold: net_send_amount = min(running_balance + spoke_balance_target, 0) running_balance = running_balance - net_send_amount



##### 注意
引用的 `SpokeTargetBalances` 由 [UMIP-157 Token 常量](https://learnblockchain.cn/article/15351#token-constants) 指定:

### 构建 Root Bundles

#### 构建 Pool Rebalance Root
每个唯一的 `chainId` & `l1Token` 对应生成一个 Pool Rebalance Leaf,其中相应的 `Deposit`、`Fill` 或 `Slow Fill Request` 事件是由相关的 SpokePool 在 [Bundle Block Range](#identifying-bundle-block-ranges) 内发出的。以下事件可以丢弃,因为它们对 Pool Rebalance Root 没有影响:
- `Deposit` 事件,其 `inputToken` 未[映射](#resolving-spokepool-tokens-to-their-hubpool-equivalent)到 `l1Token`。
- `Fill` 事件,其在 `repaymentChainId` 上的 `repaymentToken`(如[此处](#computing-relayer-repayments)计算)未[映射](#resolving-spokepool-tokens-to-their-hubpool-equivalent)到 `l1Token`。

每个 Pool Rebalance Leaf 应按如下方式构建:
1. 对于每个唯一的 `chainId` 和 `l1Token` 对:
    1. 根据上述程序计算数组 `runningBalances`、`netSendAmounts` 和 `bundleLpFees`。
    2. 将 `groupIndex` 设置为 0。
2. 在每个 Pool Rebalance Leaf 实例中,数组 `l1Tokens`、`runningBalances`、`netSendAmounts` 和 `bundleLpFees` 应:
    1. 按 `l1Token` 排序,并且
    2. 具有相同的非零长度。

如果单个 Pool Rebalance Leaf 中包含的 `l1Token` 条目数量超过 [`MAX_POOL_REBALANCE_LEAF_SIZE`](https://learnblockchain.cn/article/15350#global-constants):
1. 应生成额外的 Pool Rebalance Leaf 实例以容纳超额部分。
2. `l1Tokens`、`bundleLpFees`、`runningBalance` 和 `neSendAmounts` 的排序应在叶的有序数组中保持。
3. `groupIndex` 应为每个后续叶递增。

Pool Rebalance Leaf 对象的集合应按以下顺序排序:
1. `chainId`,然后
2. `l1Tokens`。

Pool Rebalance Leaf `leafId` 应设置为指示其在有序数组中的位置,从 0 开始。

每个 Pool Rebalance Leaf 的哈希应通过使用 Solidity 的标准过程 `keccak256(abi.encode(poolRebalanceLeaf))` 构建。

Pool Rebalance Merkle Tree 应该被构建成可以使用 [OpenZeppelin 的 MerkleProof](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/742e85be7c08dff21410ba4aa9c60f6a033befb8/contracts/utils/cryptography/MerkleProof.sol) 库进行验证。

##### 注意
- 有关如何构建这些类型的树的示例,请参见[此处](https://github.com/OpenZeppelin/openzeppelin-contracts/blob/742e85be7c08dff21410ba4aa9c60f6a033befb8/test/utils/cryptography/MerkleProof.test.js)。

#### 构建 Relayer Refund Root
对于 SpokePool 和 `repaymentToken` 的每个唯一组合,应至少生成一个 Relayer Refund Leaf,对于以下任何条件:
- 有效的 `Fills`,或
- 过期的 `Deposits`,或
- 无法填充的 `Deposits`,或
- 负运行余额净发送金额。

其中 `repaymentToken` 的确定方式如下
- `Fills`:`Fill` 的 `repaymentChainId` 的[等效](#resolving-spokepool-tokens-to-their-hubpool-equivalent) token 地址,如[此处](#computing-relayer-repayments)计算。
- `Deposits`:`inputToken`
- 负运行余额净发送金额:Pool Rebalance Root 生产中考虑的相应 `l1Token` 的 token 地址

每个 Relayer Refund Leaf 应按如下方式构建:
- `amountToReturn` 应设置为 `max(-netSendAmount, 0)`。
- `l2TokenAddress` 应设置为先前计算的 `repaymentToken`
    - HubPool 和 SpokePool token 映射应根据该组中任何中继的最高 `quoteTimestamp` 进行。
    - 如果不存在中继,则应使用上一个成功 proposal 中的相关 token 映射。
- `refundAddresses` 和 `refundAmounts` 数组的每个元素应根据计算中继器还款的定义过程生成。
    - 每个唯一地址应存在一个条目,其中包含任何未完成的金额总和:
        - 中继器还款,以及
        - 过期的存款,以及
        - `FillType` 为 `SlowFill` 的预填充存款。
- `refundAddresses` 和 `refundAmounts` 数组应根据以下标准排序:
    1. `refundAmount` 降序,然后
    2. `relayerAddress` 升序(以防出现重复的 `refundAmount` 值)。
- 从 `refundAmounts` 中删除金额为 0 的任何元素,并从 `refundAddresses` 中删除相同索引的元素。在此步骤之后,这两个数组应具有相同的长度。

如果 Relayer Refund leaf 中包含的退款数量超过 [`MAX_RELAYER_REPAYMENT_LEAF_SIZE`](https://learnblockchain.cn/article/15350#global-constants) 退款:
1. 应生成额外的 `RelayerRefundLeaf` 实例以容纳超额部分。
2. `refundAddresses` 和 `refundAmounts` 的排序应在叶的有序数组中保持。
3. 只有给定 `l2TokenAddress` 的第一个叶应包含非零的 `amountToReturn`。

中继器退款叶的集合应按以下顺序排序:
- chainId,然后
- `l2TokenAddress`,然后
- 叶子子索引(以防 > [`MAX_RELAYER_REPAYMENT_LEAF_SIZE`](#global-constants) 还款)。

Relayer Refund Leaf 的 `leafId` 字段应根据上面建立的排序进行编号,从 0 开始。

##### 注意
- 构建这些叶子后,可以使用它们来形成一个 merkle root,如上一节所述。

#### 构建 Slow Relay Root
对于在目标 SpokePool 的 `Bundle Block Range` 内发出的每个有效的 `Slow Fill Request`,应生成一个 Slow Relay Leaf。
对于在其对应的 `Deposit` 是在原始 SpokePool 的 `Bundle Block Range` 内发出的每个有效的早期 `Slow Fill Request`,应生成一个 Slow Relay Leaf。

如果相关的 `Slow Fill Request` 的 `inputAmount` 等于 0 并且 `message` 是零字节字符串,则不应生成 Slow Relay Leaf。

当 `Slow Fill Request` 对应于多个相同的 `Deposits` 时,应用的 `quoteTimestamp` 应来源于最早的相同`Deposit`。

每个 Slow Relay Leaf 应按如下方式构建:
1. 将 `relayData` 设置为经过验证的 `Slow Fill Request` 发出的 `RelayData`。
2. 将 `chainId` 设置为来自相应经过验证的 `Slow Fill Request` 的 `destinationChainId`。
3. 将 `updatedOutputAmount` 设置为为 SlowFill 计算的更新金额。

Slow Relay Leaf 实例的数组应按以下顺序排序:
1. `originChainId`,然后
2. `depositId`。

##### 注意
- 构建这些叶子后,可以使用它们来形成一个 merkle root,如上一节所述。
- 具有不同输出 token 的存款(即,其中 outputToken 不是目标链上 inputToken 的等效 token)明确不符合 slow fill 的条件。 对于非等效 token 的任何 `Slow Fill Requests` 实例应被忽略。

## 建议
- 提案者有责任检测和缓解不正确或不一致的 RPC 数据。 提案者应采取措施在提案之前验证其 RPC 数据的正确性。
- 提案者应避免依赖存款的 `outputAmount`,即使对于 `outputToken` 是已知 HubPool token 的存款也是如此。 在计算费用时,请确保 `realizedLpFee` **始终**从 `inputAmount` 中减去,而不是尝试根据 `inputAmount` 和 `outputAmount` 之间的差额来推断它们。
- 建议中继器在目标链上进行填充时,考虑原始链的最终性保证。 原始链重组可能导致存款重新排序,从而使填充无效。

## 迁移
- 自 ConfigStore [VERSION](https://learnblockchain.cn/article/15350#versions) 5 起,需要支持上述逻辑(但**不**支持带有 `bytes32` 类型的更新事件,例如 `FundsDeposited`、`FilledRelay` 等)。
- 为了确保 pre-fill 不会被双重退款,包含从 4 到 5 的版本升级的 `Bundle Block Range` 将遵循此 UMIP 的规则,但不会考虑来自先前 bundle 的任何 `Fill` 事件以生成中继器还款。 同样,任何先前 bundle 中包含的 `Slow Fill Request` 都不会被考虑用于生成 `Slow Relay Leaf`。 所有后续 bundle 将完全按照上述描述执行逻辑。
- 自 ConfigStore [VERSION](https://learnblockchain.cn/article/15350#versions) 6 起,需要支持带有 `bytes32` 类型的 Across 事件(`FundsDeposited`、`FilledRelay` 等)。
- 在 ConfigStore [VERSION](https://learnblockchain.cn/article/15350#versions) 从 5 迁移到 6 之后的 7 天,此 UMIP 中定义的 `Legacy` 事件被标记为已弃用。

## 实施
Across v3 的实施可在 Across [contracts-v2](https://github.com/across-protocol/contracts) 存储库中找到。

## 安全考虑
Across v3 已经过 OpenZeppelin 的审核。

>- 原文链接: [github.com/UMAprotocol/U...](https://github.com/UMAprotocol/UMIPs/blob/master/UMIPs/umip-179.md)
>- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
点赞 0
收藏 0
分享
本文参与登链社区写作激励计划 ,好文好收益,欢迎正在阅读的你也加入。

0 条评论

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