该UMIP (UMA改进提案) 定义了Across V3的协议规范更新,旨在改进跨链桥接的功能,包括支持用户和中继者之间有时间限制的协议,使第三方能够使用Across作为高效的跨链结算系统,允许在未完成中继的情况下直接在原始链上退款,并降低中继者的风险。此次更新需要更新ACROSS-V2价格标识符的规范,以便UMA DVM验证Across v3提议的结算包是否有效。
此 UMIP 定义了 Across V3 的更新协议规范。它弃用了现有 Across 协议规范的特定部分,如 UMIP-157 中所述。
Across v3 是对 Across v2 规范的重大改进,在简化现有协议的同时,增加了对新功能的支持。
Across 协议建议更新其规范,以更好地支持跨链桥接的基于意图的未来。这包括:
realizedLpFeePct
组件的需求来降低中继者的风险敞口。需要更新 ACROSS-V2 价格标识符的规范,UMA DVM 才能验证 Across v3 提出的结算包是否有效。
UMIP-157 中的以下部分明确保留供 Across v3 使用:
UMIP-157 中的所有全局常量都将保留供 Across v3 使用。AcrossConfigStore 中除了 UMIP-157 之外存储的全局变量是:
CHAIN_ID_INDICES
列表中。此列表中不应有重复项;任何包含重复项的 LITE_CHAIN_ID_INDICES
更新都可能被忽略。可以从 LITE_CHAIN_ID_INDICES
列表中删除链,以删除其“轻量级”链指定。Across v3 定义了以下数据类型:
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
指定地址(depositor
、recipient
等)的 bytes32
表示形式,以便与非 EVM 链交互。提供的 EVM 地址应提升为 bytes32
类型,并将上面的 12 个字节清零。V3RelayDataLegacy 类型支持向后兼容,但计划弃用。V3RelayDataLegacy 与 V3RelayData 类型的差异如下: |
名字 | 类型 |
---|---|---|
depositor | address | |
recipient | address | |
exclusiveRelayer | address | |
inputToken | address | |
outputToken | address | |
depositId | uint32 |
V3RelayExecutionParams 类型由中继者或执行者在完成填充时提供。V3RelayExecutionParams 定义如下: |
名字 | 类型 | 描述 |
---|---|---|---|
relay | V3RelayData | 与要填充的源链存款对应的 V3RelayData 对象。 | |
relayHash | bytes32 | V3RelayData 对象的 keccak256 哈希。另请参见计算 RelayData 哈希。 | |
updatedOutputAmount | uint256 | 接收者要收到的有效金额。这可能与存款 outputAmount 不同。 | |
updatedRecipient | bytes32 | 有效的接收者地址。这可能与存款接收者不同。 | |
updatedMessage | bytes | 要由目标 SpokePool 执行的有效消息(如果有)。 | |
repaymentChainId | uint256 | 中继者完成填充请求的偿还链 ID。此字段与慢速填充无关。 |
RelayData
-> FillStatus
的映射存储在每个 SpokePool 实例中。可以使用存款的哈希 V3RelayData
查询此映射,从而可以查询相应填充的状态。
名字 | 值 | 描述 |
---|---|---|
Unfilled | 0 | SpokePool 没有对应于 V3RelayData 哈希的已知状态。 |
RequestedSlowFill | 1 | 已为此 V3RelayData 哈希发出慢速填充请求。先前已发出相应的 RequestedV3SlowFill 事件。 |
Filled | 2 | 已完成此 V3RelayData 哈希的填充(快速或慢速)。 |
每个 FilledV3Relay
事件都会发出一个 FillType 实例(请参见下文)。
名字 | 值 | 描述 |
---|---|---|
FastFill | 0 | 中继由中继者作为快速填充完成。 |
ReplacedSlowFill | 1 | 最初请求通过慢速填充完成中继,但随后由中继者快速填充。 |
SlowFill | 2 | 中继通过慢速填充完成。 |
每个 FilledV3Relay
事件都会发出一个 V3RelayExecutionEventInfo 实例(请参见下文)。
名字 | 类型 | 描述 |
---|---|---|
updatedRecipient | bytes32 | 正在转移的资金的接收者。这可以是原始存款中标识的 recipient ,也可以是 RequestedSpeedUpV3Deposit 事件之后的更新 recipient 。 |
updatedOutputAmount | uint256 | 完成填充的中继者发送给 updatedRecipient 的金额。 |
updatedMessage | bytes | 作为中继的一部分转发给接收者的数据。 |
repaymentChainId | uint256 | 存款人指定的用于填充偿还的链。 |
fillType | FillType | 完成的填充类型(请参见上面的 FillType )。 |
每个 FilledV3Relay
事件都会发出一个 V3SlowFill 实例(请参见下文)。
名字 | 类型 | 描述 |
---|---|---|
relayData | V3RelayData | V3RelayData 实例,用于将 SlowFill 与 V3FundsDeposited 和 RequestedV3SlowFill 事件唯一关联。 |
chainId | uint256 | 完成 SlowFill 的 SpokePool 的链 ID。 |
updatedOutputAmount | uint256 | 作为 SlowFill 的一部分发送给 recipient 的金额。这通常应等于或大于 V3FundsDeposited outputAmount 。 |
repaymentChainId | uint256 | 存款人指定的用于填充偿还的链。 |
fillType | FillType | 完成的填充类型。 |
通常将 updatedRecipient
字段设置为来自相应 V3FundsDeposited
事件的 recipient
。如果中继者使用随附的 RequestedSpeedUpV3Deposit
事件完成填充,则 updatedRecipient
将设置为更新批准的地址。
Across V3 定义了以下事件:
以下事件标记为已弃用。有关更多信息,请参见迁移。
FundsDeposited
事件为单个存款发出唯一 V3RelayData
。未定义其他字段。
V3FundsDeposited
事件为单个存款发出唯一 V3RelayDataLegacy
。未定义其他字段。
originChainId
,以避免无意中混合来自不同链的事件。FundsDeposited
和 V3FundsDeposited
outputToken
字段是已知的 HubPool l1Token
。Across v3 支持协议内任意 token 交换。exclusiveRelayer
标识的地址有权在 exclusivityDeadline
过去之前在目标链上完成中继。exclusivityDeadline
设置为过去的时间戳,则任何地址都有资格填充中继。fillDeadline
之后仍然未完成的任何存款应通过后续结算包退还给源 SpokePool 上的 depositor
地址。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 | 存款人授权上述更新字段的签名。 |
RequestedSpeedUpDeposit
或 RequestedSpeedUpV3Deposit
事件的更新请求,但没有义务使用更新请求。RequestedSlowFill
事件通过应用以下调整来扩展 V3RelayData
类型:
名字 | 类型 | 描述 |
---|---|---|
message | omitted | 为了支持 messageHash 字段,省略了此字段。 |
messageHash | bytes32 | V3RelayData message 字段的 keccak256 哈希,其中消息为非空,对于空消息,为 bytes32(0) 。此字段包含在 V3RelayData message 字段的位置。 |
RequestedV3SlowFill
事件发出一个 V3RelayDataLegacy
实例。
FilledRelay 事件通过应用以下调整来扩展 V3RelayData 类型: |
名字 | 类型 | 描述 |
---|---|---|---|
message | omitted | 为了支持 messageHash 字段,从 FilledRelay 事件中省略了此字段。 |
|
messageHash | bytes32 | V3RelayData message 字段的 keccak256 哈希,其中消息为非空,对于空消息,为 bytes32(0) 。此字段包含在 V3RelayData message 字段的位置。 |
|
relayer | bytes32 | 在目标 SpokePool 上完成中继的地址。 | |
repaymentChainId | uint256 | 要更新的相应 V3FundsDeposited 事件的 depositId。 |
|
relayExecutionInfo | V3RelayExecutionEventInfo | 有效的 recipient 、message 和 outputAmount ,以及执行的 FillType (FastFill、ReplacedSlowFill、SlowFill)。 |
FilledV3Relay 事件通过添加以下字段来扩展 V3RelayDataLegacy 类型: |
名字 | 类型 | 描述 |
---|---|---|---|
relayer | address | 在目标 SpokePool 上完成中继的地址。 | |
repaymentChainId | uint256 | 要更新的相应 V3FundsDeposited 事件的 depositId。 |
|
relayExecutionInfo | V3RelayExecutionEventInfo | 有效的 recipient 、message 和 outputAmount ,以及执行的 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[] | 相应 l1Token 的 bungleLpFee 值的有序数组。 |
netSendAmounts | uint256[] | 相应 l1Token 的 netSendAmount 值的有序数组。 |
runningBalances | uint256[] | 相应 l1Token 的 runningBalance 值的有序数组。 |
groupIndex | uint256 | 指示是否应将相应的 RelayerRefund 和 SlowRelay 根中继到相应的 SpokePool。 |
leafId | uint8 | PoolRebalanceLeaves 有序数组中 PoolRebalanceLeaf 的索引。 |
l1Tokens | address[] | HubPool l1Token 地址的有序数组。 |
名字 | 类型 | 描述 |
---|---|---|
chainId | uint256 | RelayerRebalanceLeaf 引用的 SpokePool chainId 。 |
leafId | uint8 | RelayerRefundLeaves 有序数组中 RelayerRefundLeaf 的索引。 |
l2TokenAddress | address[] | 此 RelayerRefundLeaf 使用的 SpokePool token。 |
amountToReturn | uint256 | 要返回到 HubPool 的 l2TokenAddress 的数量。 |
refundAddresses | uint256[] | 要由此 RelayerRefundLeaf 退款的地址的有序数组。 |
refundAmounts | uint256[] | 要退还给相应 refundAddress 的 l2TokenAddress 数量的有序数组。 |
RelayerRefundLeaf
的实用程序,以包括在 V3FundsDeposited
fillDeadline
过期时在源链上发放存款人退款。Across v3 SlowRelayLeaf
对象由 V3SlowFill
数据类型定义。
Deposit
定义为以下事件之一的实例:
FundsDeposited
。V3FundsDeposited
。Fill
定义为以下事件之一的实例:
FilledRelay
。FilledV3Relay
。Slow Fill
定义为以下事件之一的实例:
RequestedSlowFill
。RequestedV3SlowFill
。RelayData
定义为以下数据类型之一的实例:
V3RelayData
。V3RelayDataLegacy
。Bundle Block Range
是给定提案的开始和结束区块对。有关标识 Bundle Block Range
的指导,请参见标识包区块范围。
当目标 SpokePool
FillStatus
映射显示 Deposit
RelayData
哈希的状态 Filled
时,Deposit
被认为已在目标链上被 Filled
。
可以通过查询 HubPool.crossChainContracts()
来获取特定链的当前 SpokePool 地址。必须在查询中指定 chainId。如果发生 SpokePool 迁移,可以通过抓取 HubPool CrossChainContractsSet
事件来识别历史 SpokePool 地址。
如果 AcrossConfigStore 中的 LITE_CHAIN_ID_INDICES
值包括存款的原始链或目标链(分别截至存款的 quoteTimestamp
字段),那么我们认为存款“始发于”或“目的地为”“轻量级链”。这些链对中继者偿还和慢速填充施加了约束。
为了在给定 SpokePool token 的情况下识别等效的 HubPool token,应遵循以下步骤:
SetRebalanceRoute
事件,其区块时间戳位于或早于相关的 HubPool 区块编号,其中相关的 SpokePool 链 ID 和 token 地址与 SetRebalanceRoute
destinationChainId
和 destinationToken
字段匹配。
Deposit
事件,则通过将 quoteTimestamp
解析为 HubPool 区块编号来标识相关的 HubPool 区块编号。SetRebalanceRoute
事件中,选择关联的 l1Token
字段。SetPoolRebalanceRoute
事件中搜索相同的 l1Token
和 destinationChainId
,其时间位于或早于适用的 HubPool 区块编号。l1Token
值,搜索最新的 SetRebalanceRoute
事件,其时间位于或早于适用的 HubPool 区块编号,其中 l1Token
和 destinationChainId
与提取的 l1Token
和 SpokePool 链 ID 匹配。如果找到匹配项,则地址匹配并被视为跨链等效项。除了 UMIP-157 中的描述之外:
FilledRelay
事件会发出 messsageHash
字段。此字段设置如下:
RelayData
message
字段为空 (0x
) 时:bytes32(0)
,或者RelayData
message
字段为非空 (0x...
) 时:keccak256(message)
。RelayData
哈希计算为参数 relayData
、destinationChainId
的 ABI 编码表示形式上的 keccak256
哈希,其中:
relayData
的类型为 V3RelayData
或 V3RelayDataLegacy
。
destinationChainId
的类型为 uint256
。
当 FilledRelay
事件数据省略 message
字段时,应根据重构 FilledRelay 消息中指定的步骤填充 message
字段。
bytes32
,因此此方法为同一输入数据生成相同的 V3RelayData
和 V3RelayDataLegacy
哈希。为了计算中继者偿还,将完成以下步骤:
应通过验证以下内容来认为在目标 SpokePool 上的 Bundle Block Range
内发出的每个 Fills
有效:
Fill
事件 FillType
字段未设置为 SlowFill
,并且RelayData
精确映射到一个或多个在相关的 originChainId
上发出的相应 Deposit
事件,并且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
:
FillStatus
(对于Deposit
RelayData
)是否于提案的目标链结束区块编号为 Filled
。Fill
。FillType
是FastFill
或ReplacedSlowFill
以及Fill
当前Bundle Block Range
的在目标链SpokePool之前。Fill
的具体方法。eth_getLogs
请求可以促进此操作,如果需要,可以通过二进制搜索FillStatus
字段来缩小Bundle Block Range
。这留作实施决定。为了计算存款人退款,应通过验证以下内容来认为每个 Deposit
已过期:
fillDeadline
时间戳在目标SpokePool上的Bundle Block Range
内已过(即fillDeadline
在前述起始和结束区块的目标链包的区块时间戳(block.timestamp
)之间),FillStatus
在 Bundle Block Range
结束时设置为 Unfilled
或 SlowFillRequested
。depositor
地址。fillDeadline
时间戳解析为目标链上的区块编号,以确定是否包含在Bundle Block Range
。为了计算存款人退款,通过验证以下内容来认为每个重复的Deposit
不可填充:
Deposit
与另一个Deposit
相同。Deposit
目标链FillStatus
设置为Filled
。Fill``FillType
为SlowFill
。BundleBlockRange
发生了目标链Fill
或发生在当前BundleBlockRange
发生了Deposit
。RelayData
匹配时,认为Deposits
是相同的。Deposits
,那么认为Deposits
是重复。针对每个在目标 SpokePool 上的 Bundle Block Range
内发出的 Slow Fill Request
,通过验证以下内容来认为有效:
fillDeadline
大于destinationChainId
包结束区块的时间戳block.timestamp
,Bundle Block Range
之前的对应 Deposit
事件匹配Slow Fill Request
RelayData
,quoteTimestamp
,inputToken
和outputToken
地址是等效的,Bundle Block Range
结束时,相关RelayData
哈希的目标 SpokePool FillStatus
映射是SlowFillRequested
,originChainId
和destinationChainId
不是轻量级链。Deposit
发出的相关RelayData
完整副本实现Slow Fill Request
。Slow Fill Request
集应包含在后续根包提案中作为慢速填充。Slow Fill Request
可以与之前的包的Deposit
对应。在隐含提前的Slow Fill Request
时,应如下验证Slow Fill Request
:
destinationChainId
包结束区块编号,fillDeadline
已经过时,quoteTimestamp
,inputToken
和outputToken
地址是等效的,originChainId
和destinationChainId
都不是Lite
链。Bundle Block Range
没有标识Slow Fill Request
,那么在其中FillStatus
为SlowFillRequested
情况下,隐含提前的Slow Fill Request
的表示当前Bundle Block Range
内发出一个Deposit
相当于目标链上在当前Bundle Block Range
结束日期。这可能是由于在当前包之前提交Slow Fill Request
。每个有效Fill
都需要缴纳 LP 费用。计算 LP 费用的步骤如UMIP-136 添加 IS_RELAY_VALID 作为支持的价格标识符中定义,并作以下修改:
AcrossConfigStore
合约确定正确的速率模型,而不是使用RateModelStore
合约。HubPool``liquidityUtilizationCurrent()
和liquidityUtilizationPostRelay()
函数而不是BridgePool
变体。inputToken
从 SpokePool 地址映射到 HubPool l1Token
地址。Deposit
和由中继者指定repaymentChainId
指定originChainId
计算, 其中relayExecutionInfo.FillType != SlowFill
然后destinationChainId
在其它地方填写Fill
。 如果originChainId
等于repaymentChainId
那么 LP 费用应为 0%。Deposit
inputAmount
的乘数,并在本文档中的其他地方命名为realizedLpFeePct
。SpokePool 和 token 对的Bundle Block Range
的捆绑 LP 费用可以通过将每个以下验证事件的适用 LP 费用相加来确定:
FilledRelay
。FilledV3Relay
。可以为每个FilledRelay
或FilledV3Relay
设置多个关联的存款事件。如果没有慢速填充,如果存在多个匹配的存款事件,那么将为每个事件支付多个 LP 费用。
对于每个已验证匹配的Deposit
事件,中继人偿还金额应如下计算:
(inputAmount * (1 - realizedLpFeePct)) / 1e18
,其中在 HubPool 区块编号(与相关Deposit``quoteTimestamp
相对应)的 HubPool l1Token
、originChainId
和repaymentChainId
集中计算realizedLpFeePct
。l1Token
的AcrossConfigStore合约中寻找。Fill
,每个匹配的Deposit
会根据其quoteTimestamp
生成不同的偿还。应用于repaymentToken
将等于在repaymentChainId
与inputToken
一样的等效token,其中按以下方式确定应用的repaymentChainId
(全部在相关包的中心链接结束区块时):
originChainId
是Lite chain
:originChainId
,ELSEPoolRebalanceRoute
存在于Fill``originChainId
的inputToken
中:originChainId
,ELSEPoolRebalanceRoute
存在于Fill``repaymentChainId
的repaymentToken
中:originChainId
,ELSEFill
中指定的repaymentChainId
。应用于repayment address
应如下判定:
Fill``relayer
地址在用于应用于repaymentChainId
有效:relayer
,ELSEFill``msg.sender
地址。在这种情况下,如果PoolRebalanceRoute
存在于Fill``originChainId
输出链接上的outputToken
和inputToken
两者上,那么可以将应用于repaymentChainId
修改为 destinationChainId
。由于不应用于repaymentChainId
,那么如果应用于repayment address
无效,偿还应该被放弃。
以上规则强制执行中继人偿还的repaymentToken
始终等于在相关包的中心链接结束区块时等于一个等效原始inputToken
#### 计算慢速填充更新后的输出金额
为了计算为 SlowFill 向接收者发行的金额,中继器费用应通过应用以下程序置零:
updatedOutputAmount = (inputAmount * (1 - realizedLpFeePct)) / 1e18
,其中 realizedLpFeePct
在 originChainId
和 destinationChainId
之间最早匹配的存款的 quoteTimestamp
计算。Deposit
的 outputAmount
。Deposit
的 outputAmount
指定 recipient
将收到的确切金额,用于标准填充,因此不包括任何中继器或 LP 费用。起始运行余额定义为之前的成功(无争议)Root Bundle Proposal 的累计运行余额。
每个唯一的 l1Token
和 repaymentChainId
对的起始运行余额应按如下方式确定:
RootBundleExecuted
的 chainId
与 repaymentChainId
匹配,并且l1Token
地址出现在 l1Tokens
数组中。l1Tokens
数组中 l1Token
的索引,并在 runningBalances
数组中查找对应的索引。计算 l1Token
和 chainId
对的运行余额的程序应执行以下步骤:
将运行余额初始化为 0。
添加中继器退款:
Fill
和 Pre-fill
事件,初始化一个运行余额为 0,并添加中继器还款。添加存款退款:
Bundle Block Range
内过期或被认为无法填充的每组 Deposit
事件,对原始链上的总存款退款求和。将金额添加到该链的现有中继器退款中。添加慢速填充:
Slow Fill Requests
,将每个慢速中继的 updatedOutputAmount
添加到该组的运行余额中。减去未执行的慢速填充的超额部分:
Fills
,其中 FillType
为 ReplacedSlowFill
,并且当前 bundle 数据中没有具有相同中继数据哈希的有效 Slow Fill Request
,则从运行余额中减去 SlowFill 的 updatedOutputAmount
,以确认 SlowFill 将永远不会被执行,因为填充金额已经被转移。FillStatus
为 RequestedSlowFill
,并且匹配的 slow fill request 不在当前的 bundle 范围内,则从运行余额中减去相关的 SlowFill updatedOutputAmount
,以确认 SlowFill 无法超过 fillDeadline
执行。添加所选 l1Token
和 chainId
对的起始运行余额。
根据 计算净发送金额
的结果,为每个 l1Token
和 chainId
对设置净发送金额并更新运行余额,如下面的算法所述:
spoke_balance_threshold = 此 token 的 `spokeTargetBalances` 中的 "threshold" 值
spoke_balance_target = 此 token 的 `spokeTargetBalances` 中的 "target" 值
net_send_amount = 0
if running_balance > 0: net_send_amount = running_balance running_balance = 0
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 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!