Skip to content
Merged
Show file tree
Hide file tree
Changes from 45 commits
Commits
Show all changes
48 commits
Select commit Hold shift + click to select a range
d363bf0
Revert "feat(SpokePool): Introduce opt-in deterministic relay data ha…
mrice32 Oct 9, 2024
c40c197
Reapply "feat(SpokePool): Introduce opt-in deterministic relay data h…
mrice32 Oct 9, 2024
53a4860
add deposit nonces to 7683
mrice32 Oct 9, 2024
e12e716
fix
mrice32 Oct 9, 2024
aae7048
WIP
mrice32 Oct 9, 2024
9e3df5a
Merge branch 'master' into mrice32/deterministic-new
nicholaspai Oct 9, 2024
7641fbf
feat(SpokePool): Introduce opt-in deterministic relay data hashes (#583)
nicholaspai Oct 8, 2024
7423b9e
fix(SpokePool): Apply exclusivity consistently
pxrl Oct 9, 2024
9e8e057
Also check on slow fill requests
pxrl Oct 9, 2024
90b76cd
Update contracts/SpokePool.sol
pxrl Oct 9, 2024
014f879
lint
pxrl Oct 9, 2024
0b65659
Update
pxrl Oct 11, 2024
09b298e
Add pure
pxrl Oct 11, 2024
c24c723
Fix
pxrl Oct 11, 2024
6b5f5f9
Add tests
pxrl Oct 11, 2024
9d9879e
Merge branch 'master' into mrice32/deterministic-new
nicholaspai Oct 17, 2024
33c6d8e
improve(SpokePool): _depositV3 interprets `exclusivityParameter` as 0…
nicholaspai Oct 17, 2024
1f9c101
Merge branch 'pxrl/exclusivity' into mrice32/deterministic-new
nicholaspai Oct 22, 2024
108019e
merge
nicholaspai Oct 22, 2024
2e3d671
Update SpokePool.sol
nicholaspai Oct 22, 2024
cf3e963
Update SpokePool.Relay.ts
nicholaspai Oct 22, 2024
789299c
Update SpokePool.SlowRelay.ts
nicholaspai Oct 23, 2024
56cc357
Update contracts/SpokePool.sol
nicholaspai Oct 29, 2024
7b20c75
Update SpokePool.sol
nicholaspai Oct 30, 2024
543e4c1
Update contracts/SpokePool.sol
pxrl Oct 30, 2024
c1176b1
Merge branch 'master' into mrice32/deterministic-new
nicholaspai Oct 30, 2024
6fe3534
Merge branch 'master' into mrice32/deterministic-new
nicholaspai Oct 30, 2024
97b7d3b
rebase
nicholaspai Oct 30, 2024
3926c0a
Merge branch 'master' into npai/exclusivity-switch
nicholaspai Oct 30, 2024
dddfaee
Update SpokePool.sol
nicholaspai Oct 30, 2024
2432944
Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new
nicholaspai Oct 30, 2024
b44c128
Revert "Merge branch 'npai/exclusivity-switch' into mrice32/determini…
nicholaspai Oct 30, 2024
b8f9c75
Revert "Merge branch 'npai/exclusivity-switch' into mrice32/determini…
nicholaspai Oct 30, 2024
9f2a4f1
Merge branch 'mrice32/deterministic-new' of https://github.com/across…
nicholaspai Oct 30, 2024
43d91db
revert
nicholaspai Oct 30, 2024
eedaf9c
Update SpokePool.sol
nicholaspai Oct 30, 2024
2f1438a
Fix
nicholaspai Oct 30, 2024
cce9436
Merge branch 'master' into npai/exclusivity-switch
nicholaspai Nov 18, 2024
b514866
Merge branch 'npai/exclusivity-switch' into mrice32/deterministic-new
nicholaspai Nov 18, 2024
77251cc
Update SpokePool.sol
nicholaspai Nov 25, 2024
6a21350
Merge remote-tracking branch 'origin/svm-dev' into mrice32/determinis…
nicholaspai Nov 25, 2024
77701ff
WIP
nicholaspai Nov 25, 2024
32cf80b
WIP
nicholaspai Nov 25, 2024
e56fec4
wip
nicholaspai Nov 25, 2024
b62e097
Update SpokePool.Relay.ts
nicholaspai Nov 25, 2024
e71c0f5
Fix
nicholaspai Nov 25, 2024
c411346
Update SpokePool.sol
nicholaspai Nov 25, 2024
d3de7e1
Update SpokePool.sol
nicholaspai Nov 25, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
161 changes: 149 additions & 12 deletions contracts/SpokePool.sol
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@ abstract contract SpokePool is

bytes32 public constant UPDATE_V3_DEPOSIT_DETAILS_HASH =
keccak256(
"UpdateDepositDetails(uint32 depositId,uint256 originChainId,uint256 updatedOutputAmount,bytes32 updatedRecipient,bytes updatedMessage)"
"UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,bytes32 updatedRecipient,bytes updatedMessage)"
);

bytes32 public constant UPDATE_V3_DEPOSIT_ADDRESS_OVERLOAD_DETAILS_HASH =
keccak256(
"UpdateDepositDetails(uint32 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
"UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)"
);

// Default chain Id used to signify that no repayment is requested, for example when executing a slow fill.
Expand Down Expand Up @@ -487,7 +487,7 @@ abstract contract SpokePool is
* the fill will revert on the destination chain. Must be set between [currentTime, currentTime + fillDeadlineBuffer]
* where currentTime is block.timestamp on this chain or this transaction will revert.
* @param exclusivityParameter This value is used to set the exclusivity deadline timestamp in the emitted deposit
* event. Before this destinationchain timestamp, only the exclusiveRelayer (if set to a non-zero address),
* event. Before this destination chain timestamp, only the exclusiveRelayer (if set to a non-zero address),
* can fill this deposit. There are three ways to use this parameter:
* 1. NO EXCLUSIVITY: If this value is set to 0, then a timestamp of 0 will be emitted,
* meaning that there is no exclusivity period.
Expand Down Expand Up @@ -523,7 +523,7 @@ abstract contract SpokePool is
outputAmount: outputAmount,
destinationChainId: destinationChainId,
exclusiveRelayer: exclusiveRelayer,
depositId: numberOfDeposits++, // Increment count of deposits so that deposit ID for this spoke pool is unique.
depositId: numberOfDeposits++,
quoteTimestamp: quoteTimestamp,
fillDeadline: fillDeadline,
exclusivityParameter: exclusivityParameter,
Expand Down Expand Up @@ -563,8 +563,17 @@ abstract contract SpokePool is
* @param fillDeadline The deadline for the relayer to fill the deposit. After this destination chain timestamp, the fill will
* revert on the destination chain. Must be set between [currentTime, currentTime + fillDeadlineBuffer] where currentTime
* is block.timestamp on this chain.
* @param exclusivityPeriod Added to the current time to set the exclusive relayer deadline. After this timestamp,
* anyone can fill the deposit.
* @param exclusivityParameter This value is used to set the exclusivity deadline timestamp in the emitted deposit
* event. Before this destination chain timestamp, only the exclusiveRelayer (if set to a non-zero address),
* can fill this deposit. There are three ways to use this parameter:
* 1. NO EXCLUSIVITY: If this value is set to 0, then a timestamp of 0 will be emitted,
* meaning that there is no exclusivity period.
* 2. OFFSET: If this value is less than MAX_EXCLUSIVITY_PERIOD_SECONDS, then add this value to
* the block.timestamp to derive the exclusive relayer deadline. Note that using the parameter in this way
* will expose the filler of the deposit to the risk that the block.timestamp of this event gets changed
* due to a chain-reorg, which would also change the exclusivity timestamp.
* 3. TIMESTAMP: Otherwise, set this value as the exclusivity deadline timestamp.
* which is the deadline for the exclusiveRelayer to fill the deposit.
* @param message The message to send to the recipient on the destination chain if the recipient is a contract. If the
* message is not empty, the recipient contract must implement `handleV3AcrossMessage()` or the fill will revert.
*/
Expand All @@ -579,9 +588,9 @@ abstract contract SpokePool is
address exclusiveRelayer,
uint32 quoteTimestamp,
uint32 fillDeadline,
uint32 exclusivityPeriod,
uint32 exclusivityParameter,
bytes calldata message
) public payable override {
) public payable override nonReentrant unpausedDeposits {
depositV3(
depositor.toBytes32(),
recipient.toBytes32(),
Expand All @@ -593,11 +602,121 @@ abstract contract SpokePool is
exclusiveRelayer.toBytes32(),
quoteTimestamp,
fillDeadline,
exclusivityPeriod,
exclusivityParameter,
message
);
}

/**
* @notice An overloaded version of `unsafeDepositV3` that accepts `address` types for backward compatibility. *
* @dev This version mirrors the original `unsafeDepositV3` function, but uses `address` types for `depositor`, `recipient`,
* `inputToken`, `outputToken`, and `exclusiveRelayer` for compatibility with contracts using the `address` type.
*
* The key functionality and logic remain identical, ensuring interoperability across both versions.
*/
function unsafeDepositV3(
address depositor,
address recipient,
address inputToken,
address outputToken,
uint256 inputAmount,
uint256 outputAmount,
uint256 destinationChainId,
address exclusiveRelayer,
uint256 depositNonce,
uint32 quoteTimestamp,
uint32 fillDeadline,
uint32 exclusivityParameter,
bytes calldata message
) public payable nonReentrant unpausedDeposits {
unsafeDepositV3(
depositor.toBytes32(),
recipient.toBytes32(),
inputToken.toBytes32(),
outputToken.toBytes32(),
inputAmount,
outputAmount,
destinationChainId,
exclusiveRelayer.toBytes32(),
depositNonce,
quoteTimestamp,
fillDeadline,
exclusivityParameter,
message
);
}

/**
* @notice See depositV3 for details. This function is identical to depositV3 except that it does not use the
* global deposit ID counter as a deposit nonce, instead allowing the caller to pass in a deposit nonce. This
* function is designed to be used by anyone who wants to pre-compute their resultant relay data hash, which
* could be useful for filling a deposit faster and avoiding any risk of a relay hash unexpectedly changing
* due to another deposit front-running this one and incrementing the global deposit ID counter.
* @dev This is labeled "unsafe" because there is no guarantee that the depositId emitted in the resultant
* V3FundsDeposited event is unique which means that the
* corresponding fill might collide with an existing relay hash on the destination chain SpokePool,
* which would make this deposit unfillable. In this case, the depositor would subsequently receive a refund
* of `inputAmount` of `inputToken` on the origin chain after the fill deadline.
* @dev On the destination chain, the hash of the deposit data will be used to uniquely identify this deposit, so
* modifying any params in it will result in a different hash and a different deposit. The hash will comprise
* all parameters to this function along with this chain's chainId(). Relayers are only refunded for filling
* deposits with deposit hashes that map exactly to the one emitted by this contract.
* @param depositNonce The nonce that uniquely identifies this deposit. This function will combine this parameter
* with the msg.sender address to create a unique uint256 depositNonce and ensure that the msg.sender cannot
* use this function to front-run another depositor's unsafe deposit. This function guarantees that the resultant
* deposit nonce will not collide with a safe uint256 deposit nonce whose 24 most significant bytes are always 0.
* @param depositor See identically named parameter in depositV3() comments.
* @param recipient See identically named parameter in depositV3() comments.
* @param inputToken See identically named parameter in depositV3() comments.
* @param outputToken See identically named parameter in depositV3() comments.
* @param inputAmount See identically named parameter in depositV3() comments.
* @param outputAmount See identically named parameter in depositV3() comments.
* @param destinationChainId See identically named parameter in depositV3() comments.
* @param exclusiveRelayer See identically named parameter in depositV3() comments.
* @param quoteTimestamp See identically named parameter in depositV3() comments.
* @param fillDeadline See identically named parameter in depositV3() comments.
* @param exclusivityParameter See identically named parameter in depositV3() comments.
* @param message See identically named parameter in depositV3() comments.
*/
function unsafeDepositV3(
bytes32 depositor,
bytes32 recipient,
bytes32 inputToken,
bytes32 outputToken,
uint256 inputAmount,
uint256 outputAmount,
uint256 destinationChainId,
bytes32 exclusiveRelayer,
uint256 depositNonce,
uint32 quoteTimestamp,
uint32 fillDeadline,
uint32 exclusivityParameter,
bytes calldata message
) public payable nonReentrant unpausedDeposits {
// @dev Create the uint256 deposit ID by concatenating the msg.sender and depositor address with the inputted
// depositNonce parameter. The resultant 32 byte string will be hashed and then casted to an "unsafe"
// uint256 deposit ID. The probability that the resultant ID collides with a "safe" deposit ID is
// equal to the chance that the first 28 bytes of the hash are 0, which is too small for us to consider.

uint256 depositId = getUnsafeDepositId(msg.sender, depositor, depositNonce);
DepositV3Params memory params = DepositV3Params({
depositor: depositor,
recipient: recipient,
inputToken: inputToken,
outputToken: outputToken,
inputAmount: inputAmount,
outputAmount: outputAmount,
destinationChainId: destinationChainId,
exclusiveRelayer: exclusiveRelayer,
depositId: depositId,
quoteTimestamp: quoteTimestamp,
fillDeadline: fillDeadline,
exclusivityParameter: exclusivityParameter,
message: message
});
_depositV3(params);
}

/**
* @notice Submits deposit and sets quoteTimestamp to current Time. Sets fill and exclusivity
* deadlines as offsets added to the current time. This function is designed to be called by users
Expand Down Expand Up @@ -808,7 +927,7 @@ abstract contract SpokePool is
*/
function speedUpV3Deposit(
bytes32 depositor,
uint32 depositId,
uint256 depositId,
uint256 updatedOutputAmount,
bytes32 updatedRecipient,
bytes calldata updatedMessage,
Expand Down Expand Up @@ -862,7 +981,7 @@ abstract contract SpokePool is
*/
function speedUpV3Deposit(
address depositor,
uint32 depositId,
uint256 depositId,
uint256 updatedOutputAmount,
address updatedRecipient,
bytes calldata updatedMessage,
Expand Down Expand Up @@ -1235,6 +1354,24 @@ abstract contract SpokePool is
return block.timestamp; // solhint-disable-line not-rely-on-time
}

/**
* @notice Returns the deposit ID for an unsafe deposit. This function is used to compute the deposit ID
* in unsafeDepositV3 and is provided as a convenience.
* @dev msgSenderand depositor are both used as inputs to allow passthrough depositors to create unique
* deposit hash spaces for unique depositors.
* @param msgSender The caller of the transaction used as input to produce the deposit ID.
* @param depositor The depositor address used as input to produce the deposit ID.
* @param depositNonce The nonce used as input to produce the deposit ID.
* @return The deposit ID for the unsafe deposit.
*/
function getUnsafeDepositId(
address msgSender,
bytes32 depositor,
uint256 depositNonce
) public pure returns (uint256) {
return uint256(keccak256(abi.encodePacked(msgSender, depositor, depositNonce)));
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this is ok to keep msgSender type address since its only used to create unique hashes on evm chains. For solana, I guess you could use bytes32 instead, but we don't really care if a depositID on a solana chain collides with an EVM chain, because the chain ID's would be different. Right? @chrismaree @md0x

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, I think this is Ok.

}

function getRelayerRefund(address l2TokenAddress, address refundAddress) public view returns (uint256) {
return relayerRefund[l2TokenAddress][refundAddress];
}
Expand Down Expand Up @@ -1497,7 +1634,7 @@ abstract contract SpokePool is

function _verifyUpdateV3DepositMessage(
address depositor,
uint32 depositId,
uint256 depositId,
uint256 originChainId,
uint256 updatedOutputAmount,
bytes32 updatedRecipient,
Expand Down
20 changes: 16 additions & 4 deletions contracts/erc7683/ERC7683OrderDepositor.sol
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,7 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
acrossOrderData.outputAmount,
acrossOrderData.destinationChainId,
acrossOriginFillerData.exclusiveRelayer,
acrossOrderData.depositNonce,
// Note: simplifying assumption to avoid quote timestamps that cause orders to expire before the deadline.
SafeCast.toUint32(order.openDeadline - QUOTE_BEFORE_DEADLINE),
order.fillDeadline,
Expand Down Expand Up @@ -103,6 +104,7 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
acrossOrderData.outputAmount,
acrossOrderData.destinationChainId,
acrossOrderData.exclusiveRelayer,
acrossOrderData.depositNonce,
// Note: simplifying assumption to avoid the order type having to bake in the quote timestamp.
SafeCast.toUint32(block.timestamp),
order.fillDeadline,
Expand Down Expand Up @@ -161,6 +163,17 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
return SafeCast.toUint32(block.timestamp); // solhint-disable-line not-rely-on-time
}

/**
* @notice Convenience method to compute the Across depositId for orders sent through 7683.
* @dev if a 0 depositNonce is used, the depositId will not be deterministic (meaning it can change depending on
* when the open txn is mined), but you will be safe from collisions. See the unsafeDepositV3 method on SpokePool
* for more details on how to choose between deterministic and non-deterministic.
* @param depositNonce the depositNonce field in the order.
* @param depositor the sender or signer of the order.
* @return the resulting Across depositId.
*/
function computeDepositId(uint256 depositNonce, address depositor) public view virtual returns (uint256);

function _resolveFor(GaslessCrossChainOrder calldata order, bytes calldata fillerData)
internal
view
Expand Down Expand Up @@ -223,7 +236,7 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
relayData.inputAmount = acrossOrderData.inputAmount;
relayData.outputAmount = acrossOrderData.outputAmount;
relayData.originChainId = block.chainid;
relayData.depositId = _currentDepositId();
relayData.depositId = computeDepositId(acrossOrderData.depositNonce, order.user);
relayData.fillDeadline = order.fillDeadline;
relayData.exclusivityDeadline = acrossOrderData.exclusivityPeriod;
relayData.message = acrossOrderData.message;
Expand Down Expand Up @@ -287,7 +300,7 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
relayData.inputAmount = acrossOrderData.inputAmount;
relayData.outputAmount = acrossOrderData.outputAmount;
relayData.originChainId = block.chainid;
relayData.depositId = _currentDepositId();
relayData.depositId = computeDepositId(acrossOrderData.depositNonce, msg.sender);
relayData.fillDeadline = order.fillDeadline;
relayData.exclusivityDeadline = acrossOrderData.exclusivityPeriod;
relayData.message = acrossOrderData.message;
Expand Down Expand Up @@ -357,13 +370,12 @@ abstract contract ERC7683OrderDepositor is IOriginSettler {
uint256 outputAmount,
uint256 destinationChainId,
address exclusiveRelayer,
uint256 depositNonce,
uint32 quoteTimestamp,
uint32 fillDeadline,
uint32 exclusivityPeriod,
bytes memory message
) internal virtual;

function _currentDepositId() internal view virtual returns (uint32);

function _destinationSettler(uint256 chainId) internal view virtual returns (address);
}
55 changes: 39 additions & 16 deletions contracts/erc7683/ERC7683OrderDepositorExternal.sol
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ import "@uma/core/contracts/common/implementation/MultiCaller.sol";
*/
contract ERC7683OrderDepositorExternal is ERC7683OrderDepositor, Ownable, MultiCaller {
using SafeERC20 for IERC20;
using AddressToBytes32 for address;

event SetDestinationSettler(
uint256 indexed chainId,
Expand Down Expand Up @@ -50,31 +51,53 @@ contract ERC7683OrderDepositorExternal is ERC7683OrderDepositor, Ownable, MultiC
uint256 outputAmount,
uint256 destinationChainId,
address exclusiveRelayer,
uint256 depositNonce,
uint32 quoteTimestamp,
uint32 fillDeadline,
uint32 exclusivityDeadline,
bytes memory message
) internal override {
IERC20(inputToken).forceApprove(address(SPOKE_POOL), inputAmount);

SPOKE_POOL.depositV3(
depositor,
recipient,
inputToken,
outputToken,
inputAmount,
outputAmount,
destinationChainId,
exclusiveRelayer,
quoteTimestamp,
fillDeadline,
exclusivityDeadline,
message
);
if (depositNonce == 0) {
SPOKE_POOL.depositV3(
depositor,
recipient,
inputToken,
outputToken,
inputAmount,
outputAmount,
destinationChainId,
exclusiveRelayer,
quoteTimestamp,
fillDeadline,
exclusivityDeadline,
message
);
} else {
SPOKE_POOL.unsafeDepositV3(
depositor,
recipient,
inputToken,
outputToken,
inputAmount,
outputAmount,
destinationChainId,
exclusiveRelayer,
depositNonce,
quoteTimestamp,
fillDeadline,
exclusivityDeadline,
message
);
}
}

function _currentDepositId() internal view override returns (uint32) {
return SPOKE_POOL.numberOfDeposits();
function computeDepositId(uint256 depositNonce, address depositor) public view override returns (uint256) {
return
depositNonce == 0
? SPOKE_POOL.numberOfDeposits()
: SPOKE_POOL.getUnsafeDepositId(address(this), depositor.toBytes32(), depositNonce);
}

function _destinationSettler(uint256 chainId) internal view override returns (address) {
Expand Down
Loading
Loading