该UMIP定义了Across V3的更新协议规范,是对Across V2规范的主要改进。Across V3增加了对新功能的支持,同时简化了现有协议。该协议更新旨在更好地支持跨链桥的意图驱动未来,包括支持用户和中继者之间具有强制执行力的限时协议,使Across可以作为第三方高效、模块化的跨链结算系统使用,并允许存款人在指定时间内未完成中继的情况下直接在原始链上获得退款。
此 UMIP 定义了 Across V3 的更新协议规范。它弃用了现有 Across 协议规范的特定部分,如 UMIP-157 中所述。
Across v3 是对 Across v2 规范的重大改进,在简化现有协议的同时增加了对新功能的支持。
Across 协议提议更新其规范,以更好地支持跨链桥接的基于意图的未来。这包括:
realizedLpFeePct
组件的需求,降低中继者的风险敞口。需要更新 ACROSS-V2 价格标识符的规范,以便 UMA DVM 验证 Across v3 提议的结算包是否有效。
UMIP-157 中的以下部分明确保留供 Across v3 使用:
Across v3 引入了以下新的数据类型:
V3RelayData 类型支持资金流入或流出 SpokePool 实例的转移。V3RelayData 定义如下: | 名称 | 类型 | 描述 |
---|---|---|---|
depositor | address | 在原始链上进行存款的地址。 | |
recipient | address | 目标链上的接收者地址。 | |
exclusiveRelayer | address | 可选择的独家中继者,可在独占截止日期前填补存款。 | |
inputToken | address | 存款人于原始链上存入的代币。 | |
outputToken | address | 接收者于目标链上收到的代币。 | |
inputAmount | uint256 | 存款人存入的 inputToken 数量。 | |
outputAmount | uint256 | 接收者收到的 outputToken 数量。 | |
originChainId | uint256 | 原始 SpokePool 的链 ID。 | |
depositId | uint32 | 唯一标识原始链上存款的 ID。 | |
fillDeadline | uint32 | 目标链上的 Unix 时间戳,在此时间戳之后,存款将不再被填补。 | |
exclusivityDeadline | uint32 | 目标链上的可选 Unix 时间戳,在此时间戳之后,任何中继者都可以填补存款。 | |
message | bytes | 作为中继的一部分转发给接收者的可选数据。 |
RelayData
-> FillStatus
的映射存储在每个 SpokePool 实例中。可以使用存款的哈希 V3RelayData
查询此映射,从而允许查询相应填补的状态。
名称 | 数值 | 描述 |
---|---|---|
Unfilled | 0 | SpokePool 没有相应 V3RelayData 哈希的已知状态。 |
RequestedSlowFill | 1 | 已为此 V3RelayData 哈希发出 SlowFill 请求。先前已发出相应的 RequestedV3SlowFill 事件。 |
Filled | 2 | 已完成此 V3RelayData 哈希的填补(快速或缓慢)。 |
FillType 实例与每个 FilledV3Relay
事件一起发出(见下文)。
名称 | 数值 | 描述 |
---|---|---|
FastFill | 0 | 中继由中继者作为快速填补完成。 |
ReplacedSlowFill | 1 | 最初请求通过慢速填补完成中继,但随后被中继者快速填补。 |
SlowFill | 2 | 中继通过慢速填补完成。 |
V3RelayExecutionEventInfo 实例与每个 FilledV3Relay
事件一起发出(见下文)。
名称 | 类型 | 描述 |
---|---|---|
updatedRecipient | address | 正在转移的资金的接收者。这可能是原始存款中标识的 recipient ,也可能是 RequestedSpeedUpV3Deposit 事件之后的更新 recipient 。 |
updatedOutputAmount | uint256 | 完成填补的中继者发送给 updatedRecipient 的金额。 |
updatedMessage | bytes | 作为中继的一部分转发给接收者的数据。 |
repaymentChainId | uint256 | 存款人为填补还款指定的链。 |
fillType | FillType | 已完成的填补的类型(参见上面的 FillType )。 |
V3SlowFill 实例与每个 FilledV3Relay
事件一起发出(见下文)。
名称 | 类型 | 描述 |
---|---|---|
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 定义了以下新事件:
V3FundsDeposited
事件为单个存款发出唯一的 V3RelayData
。未定义其他字段。此事件的使用者应附加 originChainId
,以避免无意中混合来自不同链的事件。
注意:
V3FundsDeposited
outputToken
不需要是已知的 HubPool l1Token
。在协议中,Across v3 从技术上支持任意代币互换。RequestedSpeedUpV3Deposit 发出指定以下字段: |
名称 | 类型 | 描述 |
---|---|---|---|
depositId | uint32 | 要更新的相应 V3FundsDeposited 事件的 depositId。 |
|
depositor | address | 要更新的相应 V3FundsDeposited 事件的存款人。 |
|
updatedOutputAmount | uint256 | 存款人批准的新 outputAmount。这应低于原始存款 outputAmount 。 |
|
updatedRecipient | address | 接收资金的新接收者。 | |
updatedMessage | bytes | 要提供给接收者的新消息。 | |
depositorSignature | bytes | 存款人授权上述更新字段的签名。 |
注意:
RequestedSpeedUpV3Deposit
的更新请求,但没有义务使用更新请求。exclusiveRelayer
标识的地址有权在 exclusivityDeadline
经过之前在目标链上完成中继。exclusivityDeadline
设置为过去的时间戳,则任何地址都有资格填补中继。fillDeadline
经过后发生的任何尝试填补都应被目标 SpokePool 拒绝。相应的存款应通过相关结算包中的原始 SpokePool 退还。RequestedV3SlowFill
事件发出 V3RelayData
。此事件在目标链上发出,旨在向提案人发出已请求慢速填补的信号。
注意:
exclusivityDeadline
时间戳经过之前,不会发生 RequestedV3SlowFill
事件。fillDeadline
时间戳经过,就无法发出 RequestedV3SlowFill
事件。FilledV3Relay 事件通过添加以下字段来扩展 V3RelayData 类型: |
名称 | 类型 | 描述 |
---|---|---|---|
relayer | address | 在目标 SpokePool 上完成中继的地址。 | |
repaymentChainId | uint256 | 要更新的相应 V3FundsDeposited 事件的 depositId。 |
|
relayExecutionInfo | V3RelayExecutionInfo | 有效的 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 代币。 |
amountToReturn | uint256 | 要返回给 HubPool 的 l2TokenAddress 数量。 |
refundAddresses | uint256[] | 要由此 RelayerRefundLeaf 退款的地址的有序数组。 |
refundAmounts | uint256[] | 要退款给相应 refundAddress 的 l2TokenAddress 数量的有序数组。 |
注意:
RelayerRefundLeaf
的实用性,以包括在 V3FundsDeposited
fillDeadline
过期的情况下在原始链上发放存款人退款。Across v3 SlowRelayLeaf
对象由 V3SlowFill
数据类型 定义。
注意:
可以通过查询 HubPool.crossChainContracts()
获得特定链的当前 SpokePool 地址。必须在查询中指定 chainId。如果发生 SpokePool 迁移,可以通过抓取 HubPool CrossChainContractsSet
事件来识别历史 SpokePool 地址。
为了在给定 SpokePool 代币的情况下识别等效的 HubPool 代币,应遵循以下步骤:
SetRebalanceRoute
事件,该事件的区块时间戳等于或早于相关的 HubPool 区块号,其中相关的 SpokePool 链 ID 和代币地址与 SetRebalanceRoute
destinationChainId
和 destinationToken
字段匹配。
V3FundsDeposited
事件的情况下,通过将 quoteTimestamp
解析为 HubPool 区块号来识别相关的 HubPool 区块号。SetRebalanceRoute
事件中,选择关联的 l1Token
字段。SetPoolRebalanceRoute
事件中搜索相同 l1Token
和 destinationChainId
,其时间等于或早于适用的 HubPool 区块号。l1Token
值,搜索最新的 SetRebalanceRoute
事件,其时间等于或早于适用的 HubPool 区块号,其中 l1Token
和 destinationChainId
与提取的 l1Token
和 SpokePool 链 ID 匹配。如果找到匹配项,则地址匹配并被视为跨链等价物。除了 UMIP-157 中的描述:
为了计算中继者还款,应通过验证以下内容来认为在目标 SpokePool 上目标区块范围内发出的每个 FilledV3Relay
事件都有效:
FilledV3Relay
FillType
字段未设置为 SlowFill
,V3RelayData
准确映射到在相关 originChainId
上发出的相应 V3FundsDeposited
事件。这可以通过比较两个对象的哈希值来比较。如果 V3FundsDeposited
事件指定 outputToken
0x0(即零地址),则应替换目标链上等效的 SpokePool 代币。为了确定 V3RelayData
的等效性,应使用更新/替换的 outputToken
代替 0x0。
为了计算存款人退款,应通过验证以下内容来认为每个 V3FundsDeposited
事件都已过期:
fillDeadline
时间戳在目标 SpokePool 上的目标区块范围内经过(即,fillDeadline
在目标链的包开始和结束区块的 block.timestamp
之间过期),FillStatus
设置为 Unfilled
或 SlowFillRequested
。注意:
depositor
地址。fillDeadline
时间戳应解析为目标链上的区块号,以便确定其是否包含在目标区块范围内。为了计算要发放给接收者的慢速填补,应通过验证以下内容来认为在目标 SpokePool 上目标区块范围内发出的每个 RequestedV3SlowFill
事件都有效:
inputToken
和 outputToken
地址在存款 quoteTimestamp
处等效,destinationChainId
包结束区块号,fillDeadline
尚未经过,V3RelayData
哈希的目标 SpokePool FillStatus
映射为 SlowFillRequested
,RequestedV3SlowFill
V3RelayData
与原始 SpokePool 上的相应 V3FundsDeposited
事件匹配,注意:
V3FundsDeposited
事件发出的相关 V3RelayData
的完整副本来发出慢速填补请求。RequestedV3SlowFill
事件的结果集应作为 SlowFills 包含在后续的根包提案中。每个有效的 FilledV3Relay
事件都需支付 LP 费用。计算 LP 费用的程序如 UMIP-136 添加 IS_RELAY_VALID 作为支持的价格标识符 中定义,但有以下修改:
RateModelStore
合约。HubPool
liquidityUtilizationCurrent()
和 liquidityUtilizationPostRelay()
函数,而不是 BridgePool
变体。inputToken
从 SpokePool 地址映射到 HubPool l1Token
地址。originChainId
和 FilledV3Relay.repaymentChainId
之间计算,其中 relayExecutionInfo.FillType != SlowFill
,否则计算 FilledV3Relay.destinationChainId
。注意:
V3FundsDeposited
inputAmount
的倍数,在本文档的其他地方称为 realizedLpFeePct
。SpokePool 和代币对的目标区块范围的包 LP 费用应通过对每个验证过的事件的适用 LP 费用求和来确定:
FilledV3Relay
对于验证过的 FilledV3Relay
事件,中继者还款金额应按如下方式计算:
(inputAmount * (1 - realizedLpFeePct)) / 1e18
,其中 realizedLpFeePct
在 HubPool 区块号处,在 HubPool l1Token
、originChainId
和 repaymentChainId
集上计算,该区块号对应于相关的 V3FundsDeposited
quoteTimestamp
。l1Token
。对于过期的 V3FundsDeposited
事件,存款人退款金额应计算为 inputAmount
单位的 inputToken
。
为了计算要为 SlowFill 发放给接收者的金额,应通过应用以下程序将中继者费用清零:
updatedOutputAmount = (inputAmount * (1 - realizedLpFeePct)) / 1e18
,其中 realizedLpFeePct
在 originChainId
和 destinationChainId
之间的存款 quoteTimestamp
处计算。约束:
V3FundsDeposited
outputAmount
。注意:
V3FundsDeposited
outputAmount
指定了标准填补的 recipient
要收到的确切金额,因此不包括任何已支付的中继者或 LP 费用。开始运行余额定义为截至先前成功(未争议)根包提案的累计运行余额。
每个唯一的 l1Token
和 repaymentChainId
对的开始运行余额应按如下方式确定:
RootBundleExecuted
chainId
与 repaymentChainId
匹配,并且l1Token
地址出现在 l1Tokens
数组中。l1Tokens
数组中 l1Token
的索引,并在 runningBalances
数组中查找相应的索引。计算 l1Token
和 chainId
对的运行余额的程序应实施以下步骤:
将运行余额初始化为 0。
添加中继者退款:
FilledV3Relay
事件,将运行余额初始化为 0 并添加中继者还款。添加存款退款:
V3FundsDeposited
事件,对原始链上的存款退款总额求和。将该金额添加到该链的现有中继者退款中。添加慢速填补:
RequestedV3SlowFill
事件,将每个慢速中继的 updatedOutputAmount
添加到该组的运行余额。从未执行的慢速填补中减去过量:
FilledV3Relay
事件,其中 FillType
为 ReplacedSlowFill
并且当前包数据中没有具有相同中继数据哈希的有效 RequestedV3SlowFill
事件,从运行余额中减去 SlowFill updatedOutputAmount
,因为该填补金额已转移,所以慢速填补将永远不会执行。FillStatus
为 RequestedSlowFill
且匹配的慢速填补请求不在当前包范围内,从运行余额中减去关联的 SlowFill updatedOutputAmount
,因为超过 fillDeadline
后无法执行慢速填补。添加所选 l1Token
和 chainId
对的开始运行余额。
根据 Computing Net Send Amounts
的结果,为每个 l1Token
和 chainId
对设置净发送金额并更新运行余额,如以下算法所述:
spoke_balance_threshold = 此代币的 `spokeTargetBalances` 中的“threshold”值
spoke_balance_target = 此代币的 `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 代币常量](https://github.com/UMAprotocol/UMIPs/blob/pxrl/umip179/UMIPs/umip-157.md#token-constants) 中指定:
### 构建根包
#### 构建池再平衡根
每个唯一的 `chainId` 和 `l1Token` 对产生一个池再平衡叶,其中相应的 SpokePool 在目标区块范围内发出 `V3FundsDeposited`、`FilledV3Relay` 或 `RequestedV3SlowFill` 事件。
每个池再平衡叶应按如下方式构建:
1. 对于每个唯一的 `chainId` 和 `l1Token` 对:
1. 根据上面概述的程序计算数组 `runningBalances`、`netSendAmounts` 和 `bundleLpFees`。
2. 将 `groupIndex` 设置为 0。
2. 在每个池再平衡叶实例中,数组 `l1Tokens`、`runningBalances`、`netSendAmounts` 和 `bundleLpFees` 应:
1 按 `l1Token` 排序,并且
2 具有相同(非零长度。
如果单个池再平衡叶中包含的 `l1Token` 条目的数量超过 [`MAX_POOL_REBALANCE_LEAF_SIZE`](https://github.com/UMAprotocol/UMIPs/blob/7b1a046098d3e2583abd0372c5e9c6003b46ad92/UMIPs/umip-157.md#global-constants):
1. 应生成额外的池再平衡叶实例以容纳过量。
2. 应在叶的有序数组中维护 `l1Tokens`、`bundleLpFees`、`runningBalance` 和 `neSendAmounts` 的排序。
3. 应为每个后续叶递增 `groupIndex`。
池再平衡叶对象集应按以下顺序排序:
1、`chainId`,然后
2、`l1Tokens`。
应设置池再平衡叶 `leafId` 以指示其在有序数组中的位置,从 0 开始。
每个池再平衡叶的哈希应通过使用 Solidity 的标准流程 `keccak256(abi.encode(poolRebalanceLeaf))` 构建。
应构建池再平衡 Merkle 树,使其可以使用 [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)。
#### 构建中继者退款根
对于 SpokePool 和 `l1Token` 的每个唯一组合,应至少生成一个中继者退款叶满足以下任何条件:
- 有效的 `FilledV3Relay` 事件,或者
- 过期的 `V3Fundsdeposited` 事件,或者
- 负的运行余额净发送金额。
每个中继者退款叶应按如下方式构建:
- `amountToReturn` 应设置为 `max(-netSendAmount, 0)`。
- `l2TokenAddress` 应设置为在池再平衡根生成中考虑的相应 `l1Token` 的 L2 代币地址。
- HubPool 和 SpokePool 代币映射应根据组中任何中继的最高 `quoteTimestamp` 进行。
- 如果不存在中继,则应使用来自先前成功提案的相关代币映射。
- 应根据用于计算中继者还款的已定义程序生成 `refundAddresses` 和 `refundAmounts` 数组的每个元素。
- 每个唯一地址应存在一个条目,其中包含任何未偿还的总和:
- 中继者还款,和/或
- 过期存款。
- `refundAddresses` 和 `refundAmounts` 数组应根据以下条件排序:
1. `refundAmount` 降序,然后
2. `relayerAddress` 升序(如果 `refundAmount` 值重复)。
如果中继者退款叶中包含的退款数量超过 [`MAX_RELAYER_REPAYMENT_LEAF_SIZE`]((https://github.com/UMAprotocol/UMIPs/blob/7b1a046098d3e2583abd0372c5e9c6003b46ad92/UMIPs/umip-157.md#global-constants) 退款:
1. 应生成额外的 `RelayerRefundLeaf` 实例以容纳过量。
2. 应在叶的有序数组中维护 `refundAddresses` 和 `refundAmounts` 的排序。
3. 对于给定的 `l2TokenAddress`,只有第一个叶应包含非零 `amountToReturn`。
中继者退款叶集应根据以下条件排序:
- chainId,然后
- `l2TokenAddress`,然后
- 叶子子索引(在 > [`MAX_RELAYER_REPAYMENT_LEAF_SIZE`](#global-constants) 还款的情况下)。
应根据上面建立的排序规则对中继者退款叶 `leafId` 字段进行编号,从 0 开始。
注意:
- 一旦构建了这些叶,它们就可以用于形成 Merkle 根,如上一节所述。
#### 构建慢速中继根
对于在目标 SpokePool 的目标区块范围内发出的每个有效的 `RequestedV3SlowRelay` 事件,应生成一个慢速中继叶。
每个慢速中继叶应按如下方式构建:
1. 将 `relayData` 设置为经过验证的 `RequestedV3SlowFill` 事件发出的 `V3RelayData` 数据。
2. 将 `chainId` 设置为来自相应验证过的 `RequestedV3SlowFill` 事件的 `destinationChainId`。
3. 将 `updatedOutputAmount` 设置为为 SlowFill 计算的更新数量。
慢速中继叶实例数组应根据以下条件排序:
1、`originChainId`,然后
2、`depositId`。
注意:
- 一旦构建了这些叶,它们就可以用于形成 Merkle 根,如上一节所述。
- 具有不同输出代币的存款(即,其中 outputToken 不是目标链上 inputToken 的等效项的存款)明确不符合慢速填补的条件。对于非等效代币的任何 `RequestedV3SlowFill` 事件实例都应忽略。
## 建议
- 提案人负责检测和减轻不正确或不一致的 RPC 数据。提案人应采取步骤在提案之前验证其 RPC 数据的正确性。
- 提案人应避免依赖存款 `outputAmount`,即使对于 `outputToken` 是已知 HubPool 代币的存款也是如此。在计算费用时,请确保 `realizedLpFee` **始终**从 `inputAmount` 中扣除,而不是尝试根据 `inputAmount` 和 `outputAmount` 之间的差额来推断它们。
- 建议中继者在目标链上进行填补时考虑原始链最终性保证。原始链重组可能会导致存款重新排序,从而使填补无效。
## Across v2 -> V3 过渡
Across v3 是 Across v2 的补充,增加了额外的逻辑支持新型存款、填补和慢速填补请求。在升级到 Across v3 时,将不再可能发出 Across v2 `FundsDeposited` 事件。为了支持服务的连续性并最大限度地减少第三方集成商的中断,可以通过 `FilledRelay` 事件来填充预先存在的 `FundsDeposited` 事件。在此期间,RootBundleProposals 将包含 Across v2 和 v3 的中继者还款和慢速填补叶。
当 ConfigStore 中的 VERSION 字段更新为 3 或更高时,本 UMIP 中定义的 V3 规则将适用。Across 包支持 V2 事件的能力可能会在未来的 VERSION 增量中停止。
## 实施
Across v3 实施可在 Across [contracts-v2](https://github.com/across-protocol/contracts-v2) 存储库中找到。
## 安全注意事项
Across v3 已通过 OpenZeppelin 审核。
注意:
- 如果已知特定的中继者退款无法执行,则提案人可以在提案之前做出充分的公开理由的情况下从包中删除它。这旨在处理不太可能发生的情况,例如中心化代币发行者将某个地址列入黑名单,该地址应获得退款。如果此叶保持不变,此列入黑名单的地址可能会阻止其他地址接收退款。
>- 原文链接: [github.com/UMAprotocol/U...](https://github.com/UMAprotocol/UMIPs/blob/pxrl/umip179/UMIPs/umip-179.md)
>- 登链社区 AI 助手,为大家转译优秀英文文章,如有翻译不通的地方,还请包涵~
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!