From bd7fa38f5a3d3700d0848f1fb32140dfc5bcced4 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 22 Jan 2025 22:48:43 +0100 Subject: [PATCH 1/5] feat: update function and event naming for backwards compatibility (#805) * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * refined overfloaded function structure Signed-off-by: Chris Maree * Discard changes to test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * update event names Signed-off-by: Matt Rice * fix tests Signed-off-by: Matt Rice * update function Signed-off-by: Matt Rice * update naming Signed-off-by: Matt Rice * drop unintended svm changes Signed-off-by: Matt Rice * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * WIP Signed-off-by: Chris Maree * feat: extend current add-legacy-fill-method-svm-dev (#864) * WIP Signed-off-by: Chris Maree --------- Signed-off-by: Chris Maree Signed-off-by: Chris Maree Signed-off-by: Matt Rice Co-authored-by: Chris Maree Co-authored-by: Matt Rice --- contracts/SpokePool.sol | 165 +++++++------- contracts/SpokePoolVerifier.sol | 4 +- contracts/SwapAndBridge.sol | 2 +- .../erc7683/ERC7683OrderDepositorExternal.sol | 12 +- contracts/interfaces/SpokePoolInterface.sol | 2 +- contracts/interfaces/V3SpokePoolInterface.sol | 145 ++++++++++++- contracts/permit2-order/Permit2Depositor.sol | 2 +- contracts/test/MockSpokePool.sol | 6 +- foundry.toml | 6 +- hardhat.config.ts | 4 +- .../local/SpokePoolDeprecatedMethods.t.sol | 143 ++++++++++++ .../evm/foundry/local/SpokePoolVerifier.t.sol | 4 +- test/evm/hardhat/SpokePool.Deposit.ts | 205 ++++++++++-------- test/evm/hardhat/SpokePool.Relay.ts | 200 ++++++++++------- test/evm/hardhat/SpokePool.SlowRelay.ts | 58 +++-- .../Polygon_SpokePool.ts | 4 +- test/evm/hardhat/constants.ts | 19 -- 17 files changed, 635 insertions(+), 346 deletions(-) create mode 100644 test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index f27cd9d2c..888beb905 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -61,10 +61,10 @@ abstract contract SpokePool is uint32 private DEPRECATED_depositQuoteTimeBuffer; // `numberOfDeposits` acts as a counter to generate unique deposit identifiers for this spoke pool. - // It is a uint32 that increments with each `depositV3` call. In the `V3FundsDeposited` event, it is + // It is a uint32 that increments with each `depositV3` call. In the `FundsDeposited` event, it is // implicitly cast to uint256 by setting its most significant bits to 0, reducing the risk of ID collisions // with unsafe deposits. However, this variable's name could be improved (e.g., `depositNonceCounter`) - // since it does not accurately reflect the total number of deposits, as `unsafeDepositV3` can bypass this increment. + // since it does not accurately reflect the total number of deposits, as `unsafeDeposit` can bypass this increment. uint32 public numberOfDeposits; // Whether deposits and fills are disabled. @@ -137,12 +137,12 @@ abstract contract SpokePool is uint256 public constant MAX_TRANSFER_SIZE = 1e36; - bytes32 public constant UPDATE_V3_DEPOSIT_DETAILS_HASH = + bytes32 public constant UPDATE_BYTES32_DEPOSIT_DETAILS_HASH = keccak256( "UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,bytes32 updatedRecipient,bytes updatedMessage)" ); - bytes32 public constant UPDATE_V3_DEPOSIT_ADDRESS_OVERLOAD_DETAILS_HASH = + bytes32 public constant UPDATE_ADDRESS_DEPOSIT_DETAILS_HASH = keccak256( "UpdateDepositDetails(uint256 depositId,uint256 originChainId,uint256 updatedOutputAmount,address updatedRecipient,bytes updatedMessage)" ); @@ -279,7 +279,7 @@ abstract contract SpokePool is /** * @notice Pauses deposit-related functions. This is intended to be used if this contract is deprecated or when * something goes awry. - * @dev Affects `deposit()` but not `speedUpV3Deposit()`, so that existing deposits can be sped up and still + * @dev Affects `deposit()` but not `speedUpDeposit()`, so that existing deposits can be sped up and still * relayed. * @param pause true if the call is meant to pause the system, false if the call is meant to unpause it. */ @@ -368,6 +368,8 @@ abstract contract SpokePool is **************************************/ /** + * @dev DEPRECATION NOTICE: this function is deprecated and will be removed in the future. + * Please use deposit (under DEPOSITOR FUNCTIONS below) or depositV3 instead. * @notice Called by user to bridge funds from origin to destination chain. Depositor will effectively lock * tokens in this contract and receive a destination token on the destination chain. The origin => destination * token mapping is stored on the L1 HubPool. @@ -375,7 +377,7 @@ abstract contract SpokePool is * @notice The originToken => destinationChainId must be enabled. * @notice This method is payable because the caller is able to deposit native token if the originToken is * wrappedNativeToken and this function will handle wrapping the native token to wrappedNativeToken. - * @dev Produces a V3FundsDeposited event with an infinite expiry, meaning that this deposit can never expire. + * @dev Produces a FundsDeposited event with an infinite expiry, meaning that this deposit can never expire. * Moreover, the event's outputToken is set to 0x0 meaning that this deposit can always be slow filled. * @param recipient Address to receive funds at on destination chain. * @param originToken Token to lock into this contract to initiate deposit. @@ -387,7 +389,7 @@ abstract contract SpokePool is * @param message Arbitrary data that can be used to pass additional information to the recipient along with the tokens. * Note: this is intended to be used to pass along instructions for how a contract should use or allocate the tokens. */ - function deposit( + function depositDeprecated_5947912356( address recipient, address originToken, uint256 amount, @@ -396,7 +398,7 @@ abstract contract SpokePool is uint32 quoteTimestamp, bytes memory message, uint256 // maxCount. Deprecated. - ) public payable override nonReentrant unpausedDeposits { + ) public payable nonReentrant unpausedDeposits { _deposit( msg.sender, recipient, @@ -410,6 +412,8 @@ abstract contract SpokePool is } /** + * @dev DEPRECATION NOTICE: this function is deprecated and will be removed in the future. + * Please use the other deposit or depositV3 instead. * @notice The only difference between depositFor and deposit is that the depositor address stored * in the relay hash can be overridden by the caller. This means that the passed in depositor * can speed up the deposit, which is useful if the deposit is taken from the end user to a middle layer @@ -504,7 +508,7 @@ abstract contract SpokePool is * @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. */ - function depositV3( + function deposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -538,7 +542,7 @@ abstract contract SpokePool is } /** - * @notice An overloaded version of `depositV3` that accepts `address` types for backward compatibility. + * @notice A version of `deposit` that accepts `address` types for backward compatibility. * This function allows bridging of input tokens cross-chain to a destination chain, receiving a specified amount of output tokens. * The relayer is refunded in input tokens on a repayment chain of their choice, minus system fees, after an optimistic challenge * window. The exclusivity period is specified as an offset from the current block timestamp. @@ -596,7 +600,7 @@ abstract contract SpokePool is uint32 exclusivityParameter, bytes calldata message ) public payable override { - depositV3( + deposit( depositor.toBytes32(), recipient.toBytes32(), inputToken.toBytes32(), @@ -613,52 +617,13 @@ abstract contract SpokePool is } /** - * @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 { - 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 + * @notice See deposit for details. This function is identical to deposit 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 + * FundsDeposited 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. @@ -683,7 +648,7 @@ abstract contract SpokePool is * @param exclusivityParameter See identically named parameter in depositV3() comments. * @param message See identically named parameter in depositV3() comments. */ - function unsafeDepositV3( + function unsafeDeposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -754,7 +719,7 @@ abstract contract SpokePool is * @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. */ - function depositV3Now( + function depositNow( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -766,8 +731,8 @@ abstract contract SpokePool is uint32 fillDeadlineOffset, uint32 exclusivityPeriod, bytes calldata message - ) external payable { - depositV3( + ) external payable override { + deposit( depositor, recipient, inputToken, @@ -784,7 +749,7 @@ abstract contract SpokePool is } /** - * @notice An overloaded version of `depositV3Now` that supports addresses as input types for backward compatibility. + * @notice A version of `depositNow` that supports addresses as input types for backward compatibility. * This function submits a deposit and sets `quoteTimestamp` to the current time. The `fill` and `exclusivity` deadlines * are set as offsets added to the current time. It is designed to be called by users, including Multisig contracts, who may * not have certainty when their transaction will be mined. @@ -827,7 +792,7 @@ abstract contract SpokePool is uint32 fillDeadlineOffset, uint32 exclusivityPeriod, bytes calldata message - ) external payable { + ) external payable override { depositV3( depositor, recipient, @@ -847,9 +812,9 @@ abstract contract SpokePool is /** * @notice Depositor can use this function to signal to relayer to use updated output amount, recipient, * and/or message. - * @dev the depositor and depositId must match the params in a V3FundsDeposited event that the depositor + * @dev the depositor and depositId must match the params in a FundsDeposited event that the depositor * wants to speed up. The relayer has the option but not the obligation to use this updated information - * when filling the deposit via fillV3RelayWithUpdatedDeposit(). + * when filling the deposit via fillRelayWithUpdatedDeposit(). * @param depositor Depositor that must sign the depositorSignature and was the original depositor. * @param depositId Deposit ID to speed up. * @param updatedOutputAmount New output amount to use for this deposit. Should be lower than previous value @@ -862,7 +827,7 @@ abstract contract SpokePool is * account. If depositor is a contract, then should implement EIP1271 to sign as a contract. See * _verifyUpdateV3DepositMessage() for more details about how this signature should be constructed. */ - function speedUpV3Deposit( + function speedUpDeposit( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -878,12 +843,12 @@ abstract contract SpokePool is updatedRecipient, updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_DETAILS_HASH + UPDATE_BYTES32_DEPOSIT_DETAILS_HASH ); // Assuming the above checks passed, a relayer can take the signature and the updated deposit information // from the following event to submit a fill with updated relay data. - emit RequestedSpeedUpV3Deposit( + emit RequestedSpeedUpDeposit( updatedOutputAmount, depositId, depositor, @@ -894,14 +859,14 @@ abstract contract SpokePool is } /** - * @notice An overloaded version of `speedUpV3Deposit` using `address` types for backward compatibility. + * @notice A version of `speedUpDeposit` using `address` types for backward compatibility. * This function allows the depositor to signal to the relayer to use updated output amount, recipient, and/or message * when filling a deposit. This can be useful when the deposit needs to be modified after the original transaction has * been mined. * - * @dev The `depositor` and `depositId` must match the parameters in a `V3FundsDeposited` event that the depositor wants to speed up. + * @dev The `depositor` and `depositId` must match the parameters in a `FundsDeposited` event that the depositor wants to speed up. * The relayer is not obligated but has the option to use this updated information when filling the deposit using - * `fillV3RelayWithUpdatedDeposit()`. This version uses `address` types for compatibility with systems relying on + * `fillRelayWithUpdatedDeposit()`. This version uses `address` types for compatibility with systems relying on * `address`-based implementations. * * @param depositor The depositor that must sign the `depositorSignature` and was the original depositor. @@ -932,12 +897,12 @@ abstract contract SpokePool is updatedRecipient.toBytes32(), updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_ADDRESS_OVERLOAD_DETAILS_HASH + UPDATE_ADDRESS_DEPOSIT_DETAILS_HASH ); // Assuming the above checks passed, a relayer can take the signature and the updated deposit information // from the following event to submit a fill with updated relay data. - emit RequestedSpeedUpV3Deposit( + emit RequestedSpeedUpDeposit( updatedOutputAmount, depositId, depositor.toBytes32(), @@ -961,13 +926,13 @@ abstract contract SpokePool is * window in the HubPool, and a system fee charged to relayers. * @dev The hash of the relayData will be used to uniquely identify the deposit to fill, so * modifying any params in it will result in a different hash and a different deposit. The hash will comprise - * all parameters passed to depositV3() on the origin chain along with that chain's chainId(). This chain's - * chainId() must therefore match the destinationChainId passed into depositV3. + * all parameters passed to deposit() on the origin chain along with that chain's chainId(). This chain's + * chainId() must therefore match the destinationChainId passed into deposit. * Relayers are only refunded for filling deposits with deposit hashes that map exactly to the one emitted by the * origin SpokePool therefore the relayer should not modify any params in relayData. * @dev Cannot fill more than once. Partial fills are not supported. * @param relayData struct containing all the data needed to identify the deposit to be filled. Should match - * all the same-named parameters emitted in the origin chain V3FundsDeposited event. + * all the same-named parameters emitted in the origin chain FundsDeposited event. * - depositor: The account credited with the deposit who can request to "speed up" this deposit by modifying * the output amount, recipient, and message. * - recipient The account receiving funds on this chain. Can be an EOA or a contract. If @@ -986,17 +951,17 @@ abstract contract SpokePool is * - fillDeadline The deadline for the caller to fill the deposit. After this timestamp, * the fill will revert on the destination chain. * - exclusivityDeadline: The deadline for the exclusive relayer to fill the deposit. After this - * timestamp, anyone can fill this deposit. Note that if this value was set in depositV3 by adding an offset + * timestamp, anyone can fill this deposit. Note that if this value was set in deposit by adding an offset * to the deposit's block.timestamp, there is re-org risk for the caller of this method because the event's - * block.timestamp can change. Read the comments in `depositV3` about the `exclusivityParameter` for more details. + * block.timestamp can change. Read the comments in `deposit` about the `exclusivityParameter` for more details. * - message The message to send to the recipient if the recipient is a contract that implements a * handleV3AcrossMessage() public function * @param repaymentChainId Chain of SpokePool where relayer wants to be refunded after the challenge window has * passed. Will receive inputAmount of the equivalent token to inputToken on the repayment chain. * @param repaymentAddress Address the relayer wants to be receive their refund at. */ - function fillV3Relay( - V3RelayData calldata relayData, + function fillRelay( + V3RelayData memory relayData, uint256 repaymentChainId, bytes32 repaymentAddress ) public override nonReentrant unpausedFills { @@ -1021,6 +986,28 @@ abstract contract SpokePool is _fillRelayV3(relayExecution, repaymentAddress, false); } + // Exposes the same function as fillRelay but with a legacy V3RelayData struct that takes in address types. Inner + // function fillV3Relay() applies reentrancy & non-paused checks. + function fillV3Relay(V3RelayDataLegacy calldata relayData, uint256 repaymentChainId) public override { + // Convert V3RelayDataLegacy to V3RelayData using the .toBytes32() method. + V3RelayData memory convertedRelayData = V3RelayData({ + depositor: relayData.depositor.toBytes32(), + recipient: relayData.recipient.toBytes32(), + exclusiveRelayer: relayData.exclusiveRelayer.toBytes32(), + inputToken: relayData.inputToken.toBytes32(), + outputToken: relayData.outputToken.toBytes32(), + inputAmount: relayData.inputAmount, + outputAmount: relayData.outputAmount, + originChainId: relayData.originChainId, + depositId: relayData.depositId, + fillDeadline: relayData.fillDeadline, + exclusivityDeadline: relayData.exclusivityDeadline, + message: relayData.message + }); + + fillRelay(convertedRelayData, repaymentChainId, msg.sender.toBytes32()); + } + /** * @notice Identical to fillV3Relay except that the relayer wants to use a depositor's updated output amount, * recipient, and/or message. The relayer should only use this function if they can supply a message signed @@ -1039,7 +1026,7 @@ abstract contract SpokePool is * @param depositorSignature Signed EIP712 hashstruct containing the deposit ID. Should be signed by the depositor * account. */ - function fillV3RelayWithUpdatedDeposit( + function fillRelayWithUpdatedDeposit( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress, @@ -1074,7 +1061,7 @@ abstract contract SpokePool is updatedRecipient, updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_DETAILS_HASH + UPDATE_BYTES32_DEPOSIT_DETAILS_HASH ); _fillRelayV3(relayExecution, repaymentAddress, false); @@ -1088,14 +1075,14 @@ abstract contract SpokePool is * @dev Slow fills are created by inserting slow fill objects into a merkle tree that is included * in the next HubPool "root bundle". Once the optimistic challenge window has passed, the HubPool * will relay the slow root to this chain via relayRootBundle(). Once the slow root is relayed, - * the slow fill can be executed by anyone who calls executeV3SlowRelayLeaf(). + * the slow fill can be executed by anyone who calls executeSlowRelayLeaf(). * @dev Cannot request a slow fill if the fill deadline has passed. * @dev Cannot request a slow fill if the relay has already been filled or a slow fill has already been requested. * @param relayData struct containing all the data needed to identify the deposit that should be * slow filled. If any of the params are missing or different from the origin chain deposit, * then Across will not include a slow fill for the intended deposit. */ - function requestV3SlowFill(V3RelayData calldata relayData) public override nonReentrant unpausedFills { + function requestSlowFill(V3RelayData calldata relayData) public override nonReentrant unpausedFills { uint32 currentTime = uint32(getCurrentTime()); // If a depositor has set an exclusivity deadline, then only the exclusive relayer should be able to // fast fill within this deadline. Moreover, the depositor should expect to get *fast* filled within @@ -1110,7 +1097,7 @@ abstract contract SpokePool is if (fillStatuses[relayHash] != uint256(FillStatus.Unfilled)) revert InvalidSlowFillRequest(); fillStatuses[relayHash] = uint256(FillStatus.RequestedSlowFill); - emit RequestedV3SlowFill( + emit RequestedSlowFill( relayData.inputToken, relayData.outputToken, relayData.inputAmount, @@ -1152,7 +1139,7 @@ abstract contract SpokePool is // Must do a delegatecall because the function requires the inputs to be calldata. (bool success, bytes memory data) = address(this).delegatecall( abi.encodeCall( - V3SpokePoolInterface.fillV3Relay, + V3SpokePoolInterface.fillRelay, (relayData, destinationFillerData.repaymentChainId, msg.sender.toBytes32()) ) ); @@ -1169,7 +1156,7 @@ abstract contract SpokePool is * @notice Executes a slow relay leaf stored as part of a root bundle relayed by the HubPool. * @dev Executing a slow fill leaf is equivalent to filling the relayData so this function cannot be used to * double fill a recipient. The relayData that is filled is included in the slowFillLeaf and is hashed - * like any other fill sent through fillV3Relay(). + * like any other fill sent through a fill method. * @dev There is no relayer credited with filling this relay since funds are sent directly out of this contract. * @param slowFillLeaf Contains all data necessary to uniquely identify a relay for this chain. This struct is * hashed and included in a merkle root that is relayed to all spoke pools. @@ -1182,7 +1169,7 @@ abstract contract SpokePool is * @param rootBundleId Unique ID of root bundle containing slow relay root that this leaf is contained in. * @param proof Inclusion proof for this leaf in slow relay root in root bundle. */ - function executeV3SlowRelayLeaf( + function executeSlowRelayLeaf( V3SlowFill calldata slowFillLeaf, uint32 rootBundleId, bytes32[] calldata proof @@ -1295,7 +1282,7 @@ abstract contract SpokePool is /** * @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. + * in unsafeDeposit and is provided as a convenience. * @dev msgSender and 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. @@ -1387,7 +1374,7 @@ abstract contract SpokePool is ); } - emit V3FundsDeposited( + emit FundsDeposited( params.inputToken, params.outputToken, params.inputAmount, @@ -1445,7 +1432,7 @@ abstract contract SpokePool is IERC20Upgradeable(originToken).safeTransferFrom(msg.sender, address(this), amount); } - emit V3FundsDeposited( + emit FundsDeposited( originToken.toBytes32(), // inputToken bytes32(0), // outputToken. Setting this to 0x0 means that the outputToken should be assumed to be the // canonical token for the destination chain matching the inputToken. Therefore, this deposit @@ -1687,7 +1674,7 @@ abstract contract SpokePool is // If a slow fill for this fill was requested then the relayFills value for this hash will be // FillStatus.RequestedSlowFill. Therefore, if this is the status, then this fast fill // will be replacing the slow fill. If this is a slow fill execution, then the following variable - // is trivially true. We'll emit this value in the FilledV3Relay + // is trivially true. We'll emit this value in the FilledRelay // event to assist the Dataworker in knowing when to return funds back to the HubPool that can no longer // be used for a slow fill execution. FillType fillType = isSlowFill @@ -1701,7 +1688,7 @@ abstract contract SpokePool is // @dev This function doesn't support partial fills. Therefore, we associate the relay hash with // an enum tracking its fill status. All filled relays, whether slow or fast fills, are set to the Filled // status. However, we also use this slot to track whether this fill had a slow fill requested. Therefore - // we can include a bool in the FilledV3Relay event making it easy for the dataworker to compute if this + // we can include a bool in the FilledRelay event making it easy for the dataworker to compute if this // fill was a fast fill that replaced a slow fill and therefore this SpokePool has excess funds that it // needs to send back to the HubPool. if (fillStatuses[relayHash] == uint256(FillStatus.Filled)) revert RelayFilled(); @@ -1709,7 +1696,7 @@ abstract contract SpokePool is // @dev Before returning early, emit events to assist the dataworker in being able to know which fills were // successful. - emit FilledV3Relay( + emit FilledRelay( relayData.inputToken, relayData.outputToken, relayData.inputAmount, diff --git a/contracts/SpokePoolVerifier.sol b/contracts/SpokePoolVerifier.sol index 2aa038498..b15bf41ad 100644 --- a/contracts/SpokePoolVerifier.sol +++ b/contracts/SpokePoolVerifier.sol @@ -21,7 +21,7 @@ contract SpokePoolVerifier { error InvalidSpokePool(); /** - * @notice Passthrough function to `depositV3()` on the SpokePool contract. + * @notice Passthrough function to `deposit()` on the SpokePool contract. * @dev Protects the caller from losing their ETH (or other native token) by reverting if the SpokePool address * they intended to call does not exist on this chain. Because this contract can be deployed at the same address * everywhere callers should be protected even if the transaction is submitted to an unintended network. @@ -58,7 +58,7 @@ contract SpokePoolVerifier { if (msg.value != inputAmount) revert InvalidMsgValue(); if (!address(spokePool).isContract()) revert InvalidSpokePool(); // Set msg.sender as the depositor so that msg.sender can speed up the deposit. - spokePool.depositV3{ value: msg.value }( + spokePool.deposit{ value: msg.value }( msg.sender.toBytes32(), recipient, inputToken, diff --git a/contracts/SwapAndBridge.sol b/contracts/SwapAndBridge.sol index 284d2f28f..2399c28a4 100644 --- a/contracts/SwapAndBridge.sol +++ b/contracts/SwapAndBridge.sol @@ -183,7 +183,7 @@ abstract contract SwapAndBridgeBase is Lockable, MultiCaller { DepositData calldata depositData ) internal { _acrossInputToken.safeIncreaseAllowance(address(spokePool), _acrossInputAmount); - spokePool.depositV3( + spokePool.deposit( depositData.depositor.toBytes32(), depositData.recipient.toBytes32(), address(_acrossInputToken).toBytes32(), // input token diff --git a/contracts/erc7683/ERC7683OrderDepositorExternal.sol b/contracts/erc7683/ERC7683OrderDepositorExternal.sol index 82371f9e1..4ac8fb1e3 100644 --- a/contracts/erc7683/ERC7683OrderDepositorExternal.sol +++ b/contracts/erc7683/ERC7683OrderDepositorExternal.sol @@ -75,15 +75,15 @@ contract ERC7683OrderDepositorExternal is ERC7683OrderDepositor, Ownable, MultiC message ); } else { - SPOKE_POOL.unsafeDepositV3( - depositor, - recipient, - inputToken, - outputToken, + SPOKE_POOL.unsafeDeposit( + depositor.toBytes32(), + recipient.toBytes32(), + inputToken.toBytes32(), + outputToken.toBytes32(), inputAmount, outputAmount, destinationChainId, - exclusiveRelayer, + exclusiveRelayer.toBytes32(), depositNonce, quoteTimestamp, fillDeadline, diff --git a/contracts/interfaces/SpokePoolInterface.sol b/contracts/interfaces/SpokePoolInterface.sol index 08bd4aae8..cfc448d9b 100644 --- a/contracts/interfaces/SpokePoolInterface.sol +++ b/contracts/interfaces/SpokePoolInterface.sol @@ -52,7 +52,7 @@ interface SpokePoolInterface { function emergencyDeleteRootBundle(uint256 rootBundleId) external; - function deposit( + function depositDeprecated_5947912356( address recipient, address originToken, uint256 amount, diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index f7644594c..331fc23b4 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -62,6 +62,23 @@ interface V3SpokePoolInterface { bytes message; } + // Same as V3RelayData but using addresses instead of bytes32 & depositId is uint32. + // Will be deprecated in favor of V3RelayData in the future. + struct V3RelayDataLegacy { + address depositor; + address recipient; + address exclusiveRelayer; + address inputToken; + address outputToken; + uint256 inputAmount; + uint256 outputAmount; + uint256 originChainId; + uint32 depositId; + uint32 fillDeadline; + uint32 exclusivityDeadline; + bytes message; + } + // Contains parameters passed in by someone who wants to execute a slow relay leaf. struct V3SlowFill { V3RelayData relayData; @@ -82,7 +99,7 @@ interface V3SpokePoolInterface { uint256 repaymentChainId; } - // Packs together parameters emitted in FilledV3Relay because there are too many emitted otherwise. + // Packs together parameters emitted in FilledRelay because there are too many emitted otherwise. // Similar to V3RelayExecutionParams, these parameters are not used to uniquely identify the deposit being // filled so they don't have to be unpacked by all clients. struct V3RelayExecutionEventInfo { @@ -113,7 +130,7 @@ interface V3SpokePoolInterface { * EVENTS * **************************************/ - event V3FundsDeposited( + event FundsDeposited( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -129,7 +146,7 @@ interface V3SpokePoolInterface { bytes message ); - event RequestedSpeedUpV3Deposit( + event RequestedSpeedUpDeposit( uint256 updatedOutputAmount, uint256 indexed depositId, bytes32 indexed depositor, @@ -138,7 +155,7 @@ interface V3SpokePoolInterface { bytes depositorSignature ); - event FilledV3Relay( + event FilledRelay( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -156,7 +173,7 @@ interface V3SpokePoolInterface { V3RelayExecutionEventInfo relayExecutionInfo ); - event RequestedV3SlowFill( + event RequestedSlowFill( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -182,7 +199,7 @@ interface V3SpokePoolInterface { * FUNCTIONS * **************************************/ - function depositV3( + function deposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -212,7 +229,7 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - function depositV3Now( + function depositNow( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -226,7 +243,37 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - function speedUpV3Deposit( + function depositV3Now( + address depositor, + address recipient, + address inputToken, + address outputToken, + uint256 inputAmount, + uint256 outputAmount, + uint256 destinationChainId, + address exclusiveRelayer, + uint32 fillDeadlineOffset, + uint32 exclusivityDeadline, + bytes calldata message + ) external payable; + + function unsafeDeposit( + 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 + ) external payable; + + function speedUpDeposit( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -235,13 +282,24 @@ interface V3SpokePoolInterface { bytes calldata depositorSignature ) external; - function fillV3Relay( + function speedUpV3Deposit( + address depositor, + uint256 depositId, + uint256 updatedOutputAmount, + address updatedRecipient, + bytes calldata updatedMessage, + bytes calldata depositorSignature + ) external; + + function fillRelay( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress ) external; - function fillV3RelayWithUpdatedDeposit( + function fillV3Relay(V3RelayDataLegacy calldata relayData, uint256 repaymentChainId) external; + + function fillRelayWithUpdatedDeposit( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress, @@ -251,9 +309,9 @@ interface V3SpokePoolInterface { bytes calldata depositorSignature ) external; - function requestV3SlowFill(V3RelayData calldata relayData) external; + function requestSlowFill(V3RelayData calldata relayData) external; - function executeV3SlowRelayLeaf( + function executeSlowRelayLeaf( V3SlowFill calldata slowFillLeaf, uint32 rootBundleId, bytes32[] calldata proof @@ -284,4 +342,67 @@ interface V3SpokePoolInterface { error LowLevelCallFailed(bytes data); error InsufficientSpokePoolBalanceToExecuteLeaf(); error NoRelayerRefundToClaim(); + + /************************************** + * LEGACY EVENTS * + **************************************/ + + // Note: these events are unused, but included in the ABI for ease of migration. + event V3FundsDeposited( + address inputToken, + address outputToken, + uint256 inputAmount, + uint256 outputAmount, + uint256 indexed destinationChainId, + uint32 indexed depositId, + uint32 quoteTimestamp, + uint32 fillDeadline, + uint32 exclusivityDeadline, + address indexed depositor, + address recipient, + address exclusiveRelayer, + bytes message + ); + + event RequestedSpeedUpV3Deposit( + uint256 updatedOutputAmount, + uint32 indexed depositId, + address indexed depositor, + address updatedRecipient, + bytes updatedMessage, + bytes depositorSignature + ); + + event FilledV3Relay( + address inputToken, + address outputToken, + uint256 inputAmount, + uint256 outputAmount, + uint256 repaymentChainId, + uint256 indexed originChainId, + uint32 indexed depositId, + uint32 fillDeadline, + uint32 exclusivityDeadline, + address exclusiveRelayer, + address indexed relayer, + address depositor, + address recipient, + bytes message, + V3RelayExecutionEventInfo relayExecutionInfo + ); + + event RequestedV3SlowFill( + address inputToken, + address outputToken, + uint256 inputAmount, + uint256 outputAmount, + uint256 indexed originChainId, + uint32 indexed depositId, + uint32 fillDeadline, + uint32 exclusivityDeadline, + address exclusiveRelayer, + address depositor, + address recipient, + bytes message + ); } diff --git a/contracts/permit2-order/Permit2Depositor.sol b/contracts/permit2-order/Permit2Depositor.sol index d265e24e1..6d512baca 100644 --- a/contracts/permit2-order/Permit2Depositor.sol +++ b/contracts/permit2-order/Permit2Depositor.sol @@ -59,7 +59,7 @@ contract Permit2Depositor { uint256 amountToDeposit = order.input.amount + order.fillerCollateral.amount; IERC20(order.input.token).safeIncreaseAllowance(address(SPOKE_POOL), amountToDeposit); - SPOKE_POOL.depositV3( + SPOKE_POOL.deposit( order.info.offerer.toBytes32(), // Note: Permit2OrderLib checks that order only has a single output. order.outputs[0].recipient.toBytes32(), diff --git a/contracts/test/MockSpokePool.sol b/contracts/test/MockSpokePool.sol index 42bae1e24..7d6fb210c 100644 --- a/contracts/test/MockSpokePool.sol +++ b/contracts/test/MockSpokePool.sol @@ -85,7 +85,7 @@ contract MockSpokePool is SpokePool, MockV2SpokePoolInterface, OwnableUpgradeabl _verifyDepositorSignature(depositor, expectedTypedDataV4Hash, depositorSignature); } - function verifyUpdateV3DepositMessage( + function verifyUpdateV3DepositMessageBytes32( bytes32 depositor, uint256 depositId, uint256 originChainId, @@ -103,7 +103,7 @@ contract MockSpokePool is SpokePool, MockV2SpokePoolInterface, OwnableUpgradeabl updatedRecipient, updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_DETAILS_HASH + UPDATE_BYTES32_DEPOSIT_DETAILS_HASH ); } @@ -125,7 +125,7 @@ contract MockSpokePool is SpokePool, MockV2SpokePoolInterface, OwnableUpgradeabl updatedRecipient.toBytes32(), updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_ADDRESS_OVERLOAD_DETAILS_HASH + UPDATE_ADDRESS_DEPOSIT_DETAILS_HASH ); } diff --git a/foundry.toml b/foundry.toml index 1b5feeaba..3b417d773 100644 --- a/foundry.toml +++ b/foundry.toml @@ -23,11 +23,11 @@ remappings = [ "hardhat/=node_modules/hardhat/", ] via_ir = true -optimizer_runs = 1_000_000 -solc_version = "0.8.25" +optimizer_runs = 800 +solc_version = "0.8.23" revert_strings = "strip" -solc = "0.8.25" +solc = "0.8.23" evm_version = "shanghai" [rpc_endpoints] diff --git a/hardhat.config.ts b/hardhat.config.ts index ca502d32c..bb7370f6f 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -47,7 +47,7 @@ const mnemonic = getMnemonic(); const LARGE_CONTRACT_COMPILER_SETTINGS = { version: solcVersion, settings: { - optimizer: { enabled: true, runs: 1000 }, + optimizer: { enabled: true, runs: 800 }, viaIR: true, debug: { revertStrings: isTest ? "debug" : "strip" }, }, @@ -68,7 +68,7 @@ const config: HardhatUserConfig = { overrides: { "contracts/HubPool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Linea_SpokePool.sol": { - ...DEFAULT_CONTRACT_COMPILER_SETTINGS, + ...LARGE_CONTRACT_COMPILER_SETTINGS, // NOTE: Linea only supports 0.8.19. // See https://docs.linea.build/build-on-linea/ethereum-differences#evm-opcodes version: "0.8.19", diff --git a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol new file mode 100644 index 000000000..897fb116d --- /dev/null +++ b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol @@ -0,0 +1,143 @@ +// SPDX-License-Identifier: BUSL-1.1 +pragma solidity ^0.8.0; + +import { Test } from "forge-std/Test.sol"; +import { MockSpokePool } from "../../../../contracts/test/MockSpokePool.sol"; +import { WETH9 } from "../../../../contracts/external/WETH9.sol"; +import { AddressToBytes32 } from "../../../../contracts/libraries/AddressConverters.sol"; +import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy.sol"; + +// Deprecated interface used to show that we can still call deposit() on the spoke, which should route internally to the +// colliding function interface selector on depositDeprecated_5947912356 enabling legacy deposits to still work without +// breaking interface changes. +interface DeprecatedSpokePoolInterface { + function deposit( + address recipient, + address originToken, + uint256 amount, + uint256 destinationChainId, + int64 relayerFeePct, + uint32 quoteTimestamp, + bytes memory message, + uint256 + ) external payable; +} + +contract SpokePoolOverloadedDeprecatedMethodsTest is Test { + using AddressToBytes32 for address; + + MockSpokePool spokePool; + WETH9 mockWETH; + + address depositor; + address owner; + + uint256 destinationChainId = 10; + uint256 depositAmount = 0.5 * (10**18); + + function setUp() public { + mockWETH = new WETH9(); + + depositor = vm.addr(1); + owner = vm.addr(2); + + vm.startPrank(owner); + ERC1967Proxy proxy = new ERC1967Proxy( + address(new MockSpokePool(address(mockWETH))), + abi.encodeCall(MockSpokePool.initialize, (0, owner, address(420))) + ); + spokePool = MockSpokePool(payable(proxy)); + + spokePool.setEnableRoute(address(mockWETH), destinationChainId, true); + + vm.stopPrank(); + + deal(depositor, depositAmount * 2); + + vm.startPrank(depositor); + mockWETH.deposit{ value: depositAmount }(); + mockWETH.approve(address(spokePool), depositAmount); + vm.stopPrank(); + + // Assert that the spokePool balance is zero at the start + assertEq(mockWETH.balanceOf(address(spokePool)), 0, "SpokePool balance should be zero at setup"); + } + + function testDeprecatedDeposit() public { + // Here, we are calling the deprecated deposit method, as defined in the deprecated interface. This should, in + // theory, collide with the function selector depositDeprecated_5947912356, thereby calling the legacy deposit + // method on the spoke pool, while using the old old deposit function signature. + vm.startPrank(depositor); + + DeprecatedSpokePoolInterface(address(spokePool)).deposit( + depositor, // recipient + address(mockWETH), // originToken + depositAmount, // amount + destinationChainId, // destinationChainId + 0, // relayerFeePct + uint32(block.timestamp), // quoteTimestamp + bytes(""), // message + 0 // maxCount + ); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount, "SpokePool balance should increase"); + + // Test depositing native ETH directly + DeprecatedSpokePoolInterface(address(spokePool)).deposit{ value: depositAmount }( + depositor, // recipient + address(mockWETH), // originToken - still WETH address for native deposits + depositAmount, // amount + destinationChainId, // destinationChainId + 0, // relayerFeePct + uint32(block.timestamp), // quoteTimestamp + bytes(""), // message + 0 // maxCount + ); + vm.stopPrank(); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount * 2, "SpokePool balance should increase"); + } + + function testBytes32Deposit() public { + vm.prank(depositor); + + // Show the bytes32 variant of the new deposit method works. + spokePool.deposit( + address(depositor).toBytes32(), // depositor + address(depositor).toBytes32(), // recipient + address(mockWETH).toBytes32(), // inputToken + address(mockWETH).toBytes32(), // outputToken + depositAmount, // inputAmount + 0, // outputAmount + destinationChainId, // destinationChainId + bytes32(0), // exclusiveRelayer + uint32(block.timestamp), // quoteTimestamp + uint32(block.timestamp + 1 hours), // fillDeadline + 0, // exclusivityParameter + bytes("") // message + ); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount, "SpokePool balance should increase"); + } + + function testAddressDeposit() public { + // Show the address variant of the new deposit method works. + vm.prank(depositor); + spokePool.depositV3( + depositor, // depositor + depositor, // recipient + address(mockWETH), // inputToken + address(mockWETH), // outputToken + depositAmount, // inputAmount + 0, // outputAmount + destinationChainId, // destinationChainId + address(0), // exclusiveRelayer + uint32(block.timestamp), // quoteTimestamp + uint32(block.timestamp + 1 hours), // fillDeadline + 0, // exclusivityParameter + bytes("") // message + ); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount, "SpokePool balance should increase"); + } +} diff --git a/test/evm/foundry/local/SpokePoolVerifier.t.sol b/test/evm/foundry/local/SpokePoolVerifier.t.sol index 3b36a006f..403984eee 100644 --- a/test/evm/foundry/local/SpokePoolVerifier.t.sol +++ b/test/evm/foundry/local/SpokePoolVerifier.t.sol @@ -12,7 +12,7 @@ import { ERC1967Proxy } from "@openzeppelin/contracts/proxy/ERC1967/ERC1967Proxy import { AddressToBytes32 } from "../../../../contracts/libraries/AddressConverters.sol"; interface EthereumSpokePoolOnlyAddressInterface { - function depositV3( + function deposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -141,7 +141,7 @@ contract SpokePoolVerifierTest is Test { address(ethereumSpokePool), // callee depositAmount, // value abi.encodeCall( // data - EthereumSpokePoolOnlyAddressInterface.depositV3, + EthereumSpokePoolOnlyAddressInterface.deposit, ( depositor.toBytes32(), depositor.toBytes32(), diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 1160f5d72..64cd36f8c 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -29,7 +29,6 @@ import { originChainId, MAX_EXCLUSIVITY_OFFSET_SECONDS, zeroAddress, - SpokePoolFuncs, } from "./constants"; const { AddressZero: ZERO_ADDRESS } = ethers.constants; @@ -64,7 +63,7 @@ describe("SpokePool Depositor Logic", async function () { // Can't deposit when paused: await spokePool.connect(depositor).pauseDeposits(true); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount: amountToDeposit, @@ -78,7 +77,7 @@ describe("SpokePool Depositor Logic", async function () { await spokePool.connect(depositor).pauseDeposits(false); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ recipient: recipient.address, originToken: erc20.address, @@ -89,7 +88,7 @@ describe("SpokePool Depositor Logic", async function () { }) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(erc20.address), addressToBytes(ZERO_ADDRESS), @@ -129,7 +128,7 @@ describe("SpokePool Depositor Logic", async function () { }) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(erc20.address), addressToBytes(ZERO_ADDRESS), @@ -151,7 +150,7 @@ describe("SpokePool Depositor Logic", async function () { // Fails if msg.value > 0 but doesn't match amount to deposit. await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: weth.address, amount, @@ -164,7 +163,7 @@ describe("SpokePool Depositor Logic", async function () { ).to.be.revertedWith(revertReason); await expect(() => - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ recipient: recipient.address, originToken: weth.address, @@ -184,7 +183,7 @@ describe("SpokePool Depositor Logic", async function () { it("Depositing ETH with msg.value = 0 pulls WETH from depositor", async function () { await expect(() => - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: weth.address, amount, @@ -202,7 +201,7 @@ describe("SpokePool Depositor Logic", async function () { await erc20.connect(depositor).approve(spokePool.address, 0); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -211,11 +210,11 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp, }) ) - ).to.be.revertedWith(insufficientAllowance); + ).to.be.reverted; await erc20.connect(depositor).approve(spokePool.address, amountToDeposit); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -224,7 +223,7 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); }); it("Deposit route is disabled", async function () { @@ -232,7 +231,7 @@ describe("SpokePool Depositor Logic", async function () { // Verify that routes are disabled by default. await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: unwhitelistedErc20.address, amount, @@ -245,7 +244,7 @@ describe("SpokePool Depositor Logic", async function () { // Verify that the route is enabled. await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -254,12 +253,12 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); // Disable the route. await spokePool.connect(depositor).setEnableRoute(erc20.address, destinationChainId, false); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -274,7 +273,7 @@ describe("SpokePool Depositor Logic", async function () { await spokePool.connect(depositor).setEnableRoute(erc20.address, destinationChainId, true); await erc20.connect(depositor).approve(spokePool.address, amountToDeposit); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -283,14 +282,14 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); }); it("Relayer fee is invalid", async function () { const revertReason = "InvalidRelayerFee"; await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -307,7 +306,7 @@ describe("SpokePool Depositor Logic", async function () { const quoteTimeBuffer = await spokePool.depositQuoteTimeBuffer(); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -319,7 +318,7 @@ describe("SpokePool Depositor Logic", async function () { ).to.be.revertedWith("underflowed"); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -334,7 +333,7 @@ describe("SpokePool Depositor Logic", async function () { for (const offset of [0, quoteTimeBuffer]) { await erc20.connect(depositor).approve(spokePool.address, amountToDeposit); await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -343,9 +342,47 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp: quoteTimestamp - offset, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); } }); + it("should call legacy deposit through overloaded interface", async function () { + // Define the deprecated interface + const DeprecatedSpokePoolInterface = new ethers.utils.Interface([ + "function deposit(address recipient, address originToken, uint256 amount, uint256 destinationChainId, int64 relayerFeePct, uint32 quoteTimestamp, bytes memory message, uint256 maxCount) external payable", + ]); + + // Create a new instance of the SpokePool with the deprecated interface + const deprecatedSpokePool = new ethers.Contract(spokePool.address, DeprecatedSpokePoolInterface, depositor); + + // Call the deprecated deposit method + await expect( + await deprecatedSpokePool.deposit( + depositor.address, // recipient + erc20.address, // originToken + amountToDeposit, // amount + destinationChainId, // destinationChainId + 0, // relayerFeePct + quoteTimestamp, // quoteTimestamp + "0x", // message + 0 // maxCount + ) + ).to.emit(spokePool, "FundsDeposited"); + + // Test depositing native ETH directly + await expect( + deprecatedSpokePool.deposit( + depositor.address, // recipient + weth.address, // originToken - still WETH address for native deposits + amountToDeposit, // amount + destinationChainId, // destinationChainId + 0, // relayerFeePct + quoteTimestamp, // quoteTimestamp + "0x", // message + 0, // maxCount + { value: amountToDeposit } // Send ETH + ) + ).to.emit(spokePool, "FundsDeposited"); + }); describe("deposit V3", function () { let relayData: V3RelayData, depositArgs: any[]; @@ -412,44 +449,40 @@ describe("SpokePool Depositor Logic", async function () { depositArgs = getDepositArgsFromRelayData(relayData); }); it("placeholder: gas test", async function () { - await spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs); + await spokePool.connect(depositor).deposit(...depositArgs); }); it("should allow depositv3 with address overload", async function () { await spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Address]( - ...getDepositArgsFromRelayData(relayData, destinationChainId, quoteTimestamp, true) - ); + .depositV3(...getDepositArgsFromRelayData(relayData, destinationChainId, quoteTimestamp, true)); }); it("route disabled", async function () { // Verify that routes are disabled by default for a new route const _depositArgs = getDepositArgsFromRelayData(relayData, 999); - await expect(spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](..._depositArgs)).to.be.revertedWith( - "DisabledRoute" - ); + await expect(spokePool.connect(depositor).deposit(..._depositArgs)).to.be.revertedWith("DisabledRoute"); // Enable the route: await spokePool.connect(depositor).setEnableRoute(erc20.address, 999, true); - await expect(spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](..._depositArgs)).to.not.be.reverted; + await expect(spokePool.connect(depositor).deposit(..._depositArgs)).to.not.be.reverted; }); it("invalid quoteTimestamp", async function () { const quoteTimeBuffer = await spokePool.depositQuoteTimeBuffer(); const currentTime = await spokePool.getCurrentTime(); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // quoteTimestamp too far into past (i.e. beyond the buffer) ...getDepositArgsFromRelayData(relayData, destinationChainId, currentTime.sub(quoteTimeBuffer).sub(1)) ) ).to.be.revertedWith("InvalidQuoteTimestamp"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // quoteTimestamp in the future should also revert with InvalidQuoteTimestamp ...getDepositArgsFromRelayData(relayData, destinationChainId, currentTime.add(500)) ) ).to.be.revertedWith("InvalidQuoteTimestamp"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // quoteTimestamp right at the buffer is OK ...getDepositArgsFromRelayData(relayData, destinationChainId, currentTime.sub(quoteTimeBuffer)) ) @@ -460,19 +493,19 @@ describe("SpokePool Depositor Logic", async function () { const currentTime = await spokePool.getCurrentTime(); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // fillDeadline too far into future (i.e. beyond the buffer) ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.add(fillDeadlineBuffer).add(1) }) ) ).to.be.revertedWith("InvalidFillDeadline"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // fillDeadline in past ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.sub(1) }) ) ).to.be.revertedWith("InvalidFillDeadline"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( // fillDeadline right at the buffer is OK ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.add(fillDeadlineBuffer) }) ) @@ -483,7 +516,7 @@ describe("SpokePool Depositor Logic", async function () { // If exclusive deadline is not zero, then exclusive relayer must be set. await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -492,7 +525,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -501,7 +534,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -510,7 +543,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -519,7 +552,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -528,7 +561,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -542,7 +575,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const exclusivityDeadlineOffset = MAX_EXCLUSIVITY_OFFSET_SECONDS; await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -554,7 +587,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -577,7 +610,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const exclusivityDeadlineTimestamp = MAX_EXCLUSIVITY_OFFSET_SECONDS + 1; await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -589,7 +622,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -612,7 +645,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const zeroExclusivity = 0; await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -624,7 +657,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -646,7 +679,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .deposit(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { value: 1, }) ).to.be.revertedWith("MsgValueDoesNotMatchInputAmount"); @@ -655,7 +688,7 @@ describe("SpokePool Depositor Logic", async function () { await expect(() => spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .deposit(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { value: amountToDeposit, }) ).to.changeEtherBalances([depositor, weth], [amountToDeposit.mul(toBN("-1")), amountToDeposit]); // ETH should transfer from depositor to WETH contract. @@ -665,24 +698,24 @@ describe("SpokePool Depositor Logic", async function () { }); it("if input token is not WETH then msg.value must be 0", async function () { await expect( - spokePool - .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData(relayData), { value: 1 }) + spokePool.connect(depositor).deposit(...getDepositArgsFromRelayData(relayData), { value: 1 }) ).to.be.revertedWith("MsgValueDoesNotMatchInputAmount"); }); it("if input token is WETH and msg.value = 0, pulls ERC20 from depositor", async function () { await expect(() => spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .deposit(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { value: 0, }) ).to.changeTokenBalances(weth, [depositor, spokePool], [amountToDeposit.mul(toBN("-1")), amountToDeposit]); }); it("pulls input token from caller", async function () { - await expect(() => - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs) - ).to.changeTokenBalances(erc20, [depositor, spokePool], [amountToDeposit.mul(toBN("-1")), amountToDeposit]); + await expect(() => spokePool.connect(depositor).deposit(...depositArgs)).to.changeTokenBalances( + erc20, + [depositor, spokePool], + [amountToDeposit.mul(toBN("-1")), amountToDeposit] + ); }); it("depositV3Now uses current time as quote time", async function () { const currentTime = (await spokePool.getCurrentTime()).toNumber(); @@ -692,7 +725,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3NowBytes]( + .depositNow( addressToBytes(relayData.depositor), addressToBytes(relayData.recipient), addressToBytes(relayData.inputToken), @@ -706,7 +739,7 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -731,7 +764,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3NowAddress]( + .depositV3Now( bytes32ToAddress(relayData.depositor), bytes32ToAddress(relayData.recipient), bytes32ToAddress(relayData.inputToken), @@ -745,7 +778,7 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -763,9 +796,9 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ); }); - it("emits V3FundsDeposited event with correct deposit ID", async function () { - await expect(spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs)) - .to.emit(spokePool, "V3FundsDeposited") + it("emits FundsDeposited event with correct deposit ID", async function () { + await expect(spokePool.connect(depositor).deposit(...depositArgs)) + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -784,18 +817,16 @@ describe("SpokePool Depositor Logic", async function () { ); }); it("deposit ID state variable incremented", async function () { - await spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs); + await spokePool.connect(depositor).deposit(...depositArgs); expect(await spokePool.numberOfDeposits()).to.equal(1); }); it("tokens are always pulled from caller, even if different from specified depositor", async function () { const balanceBefore = await erc20.balanceOf(depositor.address); const newDepositor = randomAddress(); await expect( - spokePool - .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) + spokePool.connect(depositor).deposit(...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -816,15 +847,11 @@ describe("SpokePool Depositor Logic", async function () { }); it("deposits are not paused", async function () { await spokePool.pauseDeposits(true); - await expect(spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs)).to.be.revertedWith( - "DepositsArePaused" - ); + await expect(spokePool.connect(depositor).deposit(...depositArgs)).to.be.revertedWith("DepositsArePaused"); }); it("reentrancy protected", async function () { - const functionCalldata = spokePool.interface.encodeFunctionData(SpokePoolFuncs.depositV3Bytes, [...depositArgs]); - await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.revertedWith( - "ReentrancyGuard: reentrant call" - ); + const functionCalldata = spokePool.interface.encodeFunctionData("deposit", [...depositArgs]); + await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.reverted; }); it("unsafe deposit ID", async function () { // new deposit ID should be the uint256 equivalent of the keccak256 hash of packed {msg.sender, depositor, forcedDepositId}. @@ -843,11 +870,11 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.unsafeDepositV3Bytes]( + .unsafeDeposit( ...getUnsafeDepositArgsFromRelayData({ ...relayData, depositor: recipient.address }, forcedDepositId) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -879,7 +906,7 @@ describe("SpokePool Depositor Logic", async function () { addressToBytes(updatedRecipient), updatedMessage ); - await spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + await spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, originChainId, @@ -891,7 +918,7 @@ describe("SpokePool Depositor Logic", async function () { // Reverts if passed in depositor is the signer or if signature is incorrect await expect( - spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(updatedRecipient), depositId, originChainId, @@ -912,7 +939,7 @@ describe("SpokePool Depositor Logic", async function () { updatedMessage ); await expect( - spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, originChainId, @@ -937,7 +964,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositBytes]( + .speedUpDeposit( addressToBytes(depositor.address), depositId, updatedOutputAmount, @@ -946,7 +973,7 @@ describe("SpokePool Depositor Logic", async function () { expectedSignature ) ) - .to.emit(spokePool, "RequestedSpeedUpV3Deposit") + .to.emit(spokePool, "RequestedSpeedUpDeposit") .withArgs( updatedOutputAmount, depositId, @@ -967,7 +994,7 @@ describe("SpokePool Depositor Logic", async function () { updatedMessage ); await expect( - spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, otherChainId, @@ -980,7 +1007,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositBytes]( + .speedUpDeposit( addressToBytes(depositor.address), depositId, updatedOutputAmount, @@ -1007,7 +1034,7 @@ describe("SpokePool Depositor Logic", async function () { true ); - await spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageAddress]( + await spokePool.verifyUpdateV3DepositMessage( depositor.address, depositId, spokePoolChainId, @@ -1020,7 +1047,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositAddress]( + .speedUpV3Deposit( depositor.address, depositId, updatedOutputAmount, @@ -1029,7 +1056,7 @@ describe("SpokePool Depositor Logic", async function () { signature ) ) - .to.emit(spokePool, "RequestedSpeedUpV3Deposit") + .to.emit(spokePool, "RequestedSpeedUpDeposit") .withArgs( updatedOutputAmount, depositId, diff --git a/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index 51c946807..2d159e8e9 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -22,7 +22,14 @@ import { getUpdatedV3DepositSignature, getV3RelayHash, } from "./fixtures/SpokePool.Fixture"; -import * as consts from "./constants"; +import { + repaymentChainId, + amountToSeedWallets, + amountToDeposit, + originChainId, + firstDepositId, + destinationChainId, +} from "./constants"; let spokePool: Contract, weth: Contract, erc20: Contract, destErc20: Contract, erc1271: Contract; let depositor: SignerWithAddress, recipient: SignerWithAddress, relayer: SignerWithAddress; @@ -39,7 +46,7 @@ async function getRelayExecutionParams( updatedOutputAmount: _relayData.outputAmount, updatedRecipient: _relayData.recipient, updatedMessage: _relayData.message, - repaymentChainId: consts.repaymentChainId, + repaymentChainId: repaymentChainId, }; } @@ -49,14 +56,14 @@ describe("SpokePool Relayer Logic", async function () { ({ weth, erc20, spokePool, destErc20, erc1271 } = await spokePoolFixture()); // mint some fresh tokens and deposit ETH for weth for depositor and relayer. - await seedWallet(depositor, [erc20], weth, consts.amountToSeedWallets); - await seedWallet(relayer, [destErc20], weth, consts.amountToSeedWallets); + await seedWallet(depositor, [erc20], weth, amountToSeedWallets); + await seedWallet(relayer, [destErc20], weth, amountToSeedWallets); // Approve spokepool to spend tokens - await erc20.connect(depositor).approve(spokePool.address, consts.amountToDeposit); - await weth.connect(depositor).approve(spokePool.address, consts.amountToDeposit); - await destErc20.connect(relayer).approve(spokePool.address, consts.amountToDeposit); - await weth.connect(relayer).approve(spokePool.address, consts.amountToDeposit); + await erc20.connect(depositor).approve(spokePool.address, amountToDeposit); + await weth.connect(depositor).approve(spokePool.address, amountToDeposit); + await destErc20.connect(relayer).approve(spokePool.address, amountToDeposit); + await weth.connect(relayer).approve(spokePool.address, amountToDeposit); }); describe("fill V3", function () { let relayData: V3RelayData; @@ -68,10 +75,10 @@ describe("SpokePool Relayer Logic", async function () { exclusiveRelayer: addressToBytes(relayer.address), inputToken: addressToBytes(erc20.address), outputToken: addressToBytes(destErc20.address), - inputAmount: consts.amountToDeposit, - outputAmount: consts.amountToDeposit, - originChainId: consts.originChainId, - depositId: consts.firstDepositId, + inputAmount: amountToDeposit, + outputAmount: amountToDeposit, + originChainId: originChainId, + depositId: firstDepositId, fillDeadline: fillDeadline, exclusivityDeadline: fillDeadline - 500, message: "0x", @@ -79,7 +86,7 @@ describe("SpokePool Relayer Logic", async function () { }); describe("_fillRelay internal logic", function () { it("default status is unfilled", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); expect(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(FillStatus.Unfilled); }); it("expired fill deadline reverts", async function () { @@ -87,7 +94,7 @@ describe("SpokePool Relayer Logic", async function () { ...relayData, fillDeadline: 0, // Will always be less than SpokePool.currentTime so should expire. }; - const relayExecution = await getRelayExecutionParams(_relay, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(_relay, destinationChainId); await expect( spokePool.connect(relayer).fillRelayV3Internal( relayExecution, @@ -97,7 +104,7 @@ describe("SpokePool Relayer Logic", async function () { ).to.be.revertedWith("ExpiredFillDeadline"); }); it("relay hash already marked filled", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); await spokePool.setFillStatus(relayExecution.relayHash, FillStatus.Filled); await expect( spokePool.connect(relayer).fillRelayV3Internal( @@ -108,7 +115,7 @@ describe("SpokePool Relayer Logic", async function () { ).to.be.revertedWith("RelayFilled"); }); it("fast fill replacing speed up request emits correct FillType", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); await spokePool.setFillStatus(relayExecution.relayHash, FillStatus.RequestedSlowFill); await expect( spokePool.connect(relayer).fillRelayV3Internal( @@ -117,7 +124,7 @@ describe("SpokePool Relayer Logic", async function () { false // isSlowFill ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -144,7 +151,7 @@ describe("SpokePool Relayer Logic", async function () { expect(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(FillStatus.Filled); }); it("slow fill emits correct FillType", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); await destErc20.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount); await expect( spokePool.connect(relayer).fillRelayV3Internal( @@ -153,7 +160,7 @@ describe("SpokePool Relayer Logic", async function () { true // isSlowFill ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -180,7 +187,7 @@ describe("SpokePool Relayer Logic", async function () { expect(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(FillStatus.Filled); }); it("fast fill emits correct FillType", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); await expect( spokePool.connect(relayer).fillRelayV3Internal( relayExecution, @@ -188,7 +195,7 @@ describe("SpokePool Relayer Logic", async function () { false // isSlowFill ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -219,7 +226,7 @@ describe("SpokePool Relayer Logic", async function () { // Set recipient == relayer recipient: addressToBytes(relayer.address), }; - const relayExecution = await getRelayExecutionParams(_relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(_relayData, destinationChainId); await expect( spokePool.connect(relayer).fillRelayV3Internal( relayExecution, @@ -229,11 +236,11 @@ describe("SpokePool Relayer Logic", async function () { ).to.not.emit(destErc20, "Transfer"); }); it("sends updatedOutputAmount to updatedRecipient", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); const _relayExecution = { ...relayExecution, // Overwrite amount to send to be double the original amount - updatedOutputAmount: consts.amountToDeposit.mul(2), + updatedOutputAmount: amountToDeposit.mul(2), // Overwrite recipient to depositor which is not the same as the original recipient updatedRecipient: addressToBytes(depositor.address), }; @@ -246,14 +253,14 @@ describe("SpokePool Relayer Logic", async function () { addressToBytes(relayer.address), false // isSlowFill ) - ).to.changeTokenBalance(destErc20, depositor, consts.amountToDeposit.mul(2)); + ).to.changeTokenBalance(destErc20, depositor, amountToDeposit.mul(2)); }); it("unwraps native token if sending to EOA", async function () { const _relayData = { ...relayData, outputToken: addressToBytes(weth.address), }; - const relayExecution = await getRelayExecutionParams(_relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(_relayData, destinationChainId); await expect(() => spokePool.connect(relayer).fillRelayV3Internal( relayExecution, @@ -267,7 +274,7 @@ describe("SpokePool Relayer Logic", async function () { ...relayData, outputToken: addressToBytes(weth.address), }; - const relayExecution = await getRelayExecutionParams(_relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(_relayData, destinationChainId); await weth.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount); const initialSpokeBalance = await weth.balanceOf(spokePool.address); await expect(() => @@ -281,7 +288,7 @@ describe("SpokePool Relayer Logic", async function () { expect(spokeBalance).to.equal(initialSpokeBalance.sub(relayExecution.updatedOutputAmount)); }); it("slow fills send non-native token out of spoke pool balance", async function () { - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); await destErc20.connect(relayer).transfer(spokePool.address, relayExecution.updatedOutputAmount); await expect(() => spokePool.connect(relayer).fillRelayV3Internal( @@ -299,7 +306,7 @@ describe("SpokePool Relayer Logic", async function () { recipient: addressToBytes(acrossMessageHandler.address), message: "0x1234", }; - const relayExecution = await getRelayExecutionParams(_relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(_relayData, destinationChainId); // Handler is called with expected params. await spokePool.connect(relayer).fillRelayV3Internal( @@ -320,13 +327,13 @@ describe("SpokePool Relayer Logic", async function () { it("fills are not paused", async function () { await spokePool.pauseFills(true); await expect( - spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("FillsArePaused"); }); it("reentrancy protected", async function () { - const functionCalldata = spokePool.interface.encodeFunctionData("fillV3Relay", [ + const functionCalldata = spokePool.interface.encodeFunctionData("fillRelay", [ relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), ]); await expect(spokePool.connect(relayer).callback(functionCalldata)).to.be.revertedWith( @@ -341,31 +348,60 @@ describe("SpokePool Relayer Logic", async function () { exclusivityDeadline: relayData.fillDeadline, }; await expect( - spokePool.connect(relayer).fillV3Relay(_relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillRelay(_relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("NotExclusiveRelayer"); // Can send it after exclusivity deadline await expect( spokePool .connect(relayer) - .fillV3Relay( - { ..._relayData, exclusivityDeadline: 0 }, - consts.repaymentChainId, - addressToBytes(relayer.address) - ) + .fillRelay({ ..._relayData, exclusivityDeadline: 0 }, repaymentChainId, addressToBytes(relayer.address)) ).to.not.be.reverted; }); - it("calls _fillRelayV3 with expected params", async function () { - await expect( - spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)) - ) - .to.emit(spokePool, "FilledV3Relay") + it("calls _fillRelayV3 with expected params", async function () { + await expect(spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address))) + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, - consts.repaymentChainId, // Should be passed-in repayment chain ID + repaymentChainId, // Should be passed-in repayment chain ID + relayData.originChainId, + relayData.depositId, + relayData.fillDeadline, + relayData.exclusivityDeadline, + addressToBytes(relayData.exclusiveRelayer), + addressToBytes(relayer.address), // Should be equal to msg.sender of fillRelayV3 + addressToBytes(relayData.depositor), + addressToBytes(relayData.recipient), + hashNonEmptyMessage(relayData.message), + [ + addressToBytes(relayData.recipient), // updatedRecipient should be equal to recipient + hashNonEmptyMessage(relayData.message), // updatedMessageHash should be equal to message hash + relayData.outputAmount, // updatedOutputAmount should be equal to outputAmount + // Should be FastFill + FillType.FastFill, + ] + ); + }); + it("calls legacy (address) fillRelayV3 with expected params", async function () { + const legacyRelayData = { + ...relayData, + depositor: bytes32ToAddress(relayData.depositor), + recipient: bytes32ToAddress(relayData.recipient), + exclusiveRelayer: bytes32ToAddress(relayData.exclusiveRelayer), + inputToken: bytes32ToAddress(relayData.inputToken), + outputToken: bytes32ToAddress(relayData.outputToken), + }; + await expect(spokePool.connect(relayer).fillV3Relay(legacyRelayData, repaymentChainId)) + .to.emit(spokePool, "FilledRelay") + .withArgs( + addressToBytes(relayData.inputToken), + addressToBytes(relayData.outputToken), + relayData.inputAmount, + relayData.outputAmount, + repaymentChainId, // Should be passed-in repayment chain ID relayData.originChainId, relayData.depositId, relayData.fillDeadline, @@ -385,7 +421,7 @@ describe("SpokePool Relayer Logic", async function () { ); }); }); - describe("fillV3RelayWithUpdatedDeposit", function () { + describe("fillRelayWithUpdatedDeposit", function () { let updatedOutputAmount: BigNumber, updatedRecipient: string, updatedMessage: string, signature: string; beforeEach(async function () { updatedOutputAmount = relayData.outputAmount.add(1); @@ -407,16 +443,16 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, exclusivityDeadline: 0 }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), updatedMessage, signature ) - ).to.emit(spokePool, "FilledV3Relay"); + ).to.emit(spokePool, "FilledRelay"); }); it("must be exclusive relayer before exclusivity deadline", async function () { const _relayData = { @@ -428,9 +464,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( _relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -441,12 +477,12 @@ describe("SpokePool Relayer Logic", async function () { // Even if not exclusive relayer, can send it after exclusivity deadline await expect( - spokePool.connect(relayer).fillV3RelayWithUpdatedDeposit( + spokePool.connect(relayer).fillRelayWithUpdatedDeposit( { ..._relayData, exclusivityDeadline: 0, }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -464,9 +500,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -474,13 +510,13 @@ describe("SpokePool Relayer Logic", async function () { signature ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), relayData.inputAmount, relayData.outputAmount, - consts.repaymentChainId, // Should be passed-in repayment chain ID + repaymentChainId, // Should be passed-in repayment chain ID relayData.originChainId, relayData.depositId, relayData.fillDeadline, @@ -501,7 +537,7 @@ describe("SpokePool Relayer Logic", async function () { ); // Check fill status mapping is updated - const relayExecution = await getRelayExecutionParams(relayData, consts.destinationChainId); + const relayExecution = await getRelayExecutionParams(relayData, destinationChainId); expect(await spokePool.fillStatuses(relayExecution.relayHash)).to.equal(FillStatus.Filled); }); it("validates depositor signature", async function () { @@ -509,9 +545,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(relayer.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -532,9 +568,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -547,9 +583,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, originChainId: relayData.originChainId + 1 }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -562,9 +598,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositId: relayData.depositId.add(toBN(1)) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -577,9 +613,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount.sub(1), addressToBytes(updatedRecipient), @@ -592,9 +628,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(randomAddress()), @@ -607,9 +643,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -632,9 +668,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -645,9 +681,9 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -657,15 +693,13 @@ describe("SpokePool Relayer Logic", async function () { ).to.not.be.reverted; }); it("cannot send updated fill after original fill", async function () { - await spokePool - .connect(relayer) - .fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + await spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -677,9 +711,9 @@ describe("SpokePool Relayer Logic", async function () { it("cannot send updated fill after slow fill", async function () { await spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -687,7 +721,7 @@ describe("SpokePool Relayer Logic", async function () { signature ); await expect( - spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("RelayFilled"); }); }); diff --git a/test/evm/hardhat/SpokePool.SlowRelay.ts b/test/evm/hardhat/SpokePool.SlowRelay.ts index 1088b3c34..11363769b 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -36,7 +36,7 @@ describe("SpokePool Slow Relay Logic", async function () { await destErc20.connect(relayer).approve(spokePool.address, consts.maxUint256); }); - describe("requestV3SlowFill", function () { + describe("requestSlowFill", function () { let relayData: V3RelayData; beforeEach(async function () { const fillDeadline = (await spokePool.getCurrentTime()).toNumber() + 1000; @@ -59,19 +59,19 @@ describe("SpokePool Slow Relay Logic", async function () { }); it("fill deadline is expired", async function () { relayData.fillDeadline = (await spokePool.getCurrentTime()).sub(1); - await expect(spokePool.connect(relayer).requestV3SlowFill(relayData)).to.be.revertedWith("ExpiredFillDeadline"); + await expect(spokePool.connect(relayer).requestSlowFill(relayData)).to.be.revertedWith("ExpiredFillDeadline"); }); it("in absence of exclusivity", async function () { // Clock drift between spokes can mean exclusivityDeadline is in future even when no exclusivity was applied. await spokePool.setCurrentTime(relayData.exclusivityDeadline - 1); - await expect(spokePool.connect(relayer).requestV3SlowFill({ ...relayData, exclusivityDeadline: 0 })).to.emit( + await expect(spokePool.connect(relayer).requestSlowFill({ ...relayData, exclusivityDeadline: 0 })).to.emit( spokePool, - "RequestedV3SlowFill" + "RequestedSlowFill" ); }); it("during exclusivity deadline", async function () { await spokePool.setCurrentTime(relayData.exclusivityDeadline); - await expect(spokePool.connect(relayer).requestV3SlowFill(relayData)).to.be.revertedWith( + await expect(spokePool.connect(relayer).requestSlowFill(relayData)).to.be.revertedWith( "NoSlowFillsInExclusivityWindow" ); }); @@ -80,41 +80,37 @@ describe("SpokePool Slow Relay Logic", async function () { // FillStatus must be Unfilled: expect(await spokePool.fillStatuses(relayHash)).to.equal(FillStatus.Unfilled); - expect(await spokePool.connect(relayer).requestV3SlowFill(relayData)).to.emit(spokePool, "RequestedV3SlowFill"); + expect(await spokePool.connect(relayer).requestSlowFill(relayData)).to.emit(spokePool, "RequestedSlowFill"); // FillStatus gets reset to RequestedSlowFill: expect(await spokePool.fillStatuses(relayHash)).to.equal(FillStatus.RequestedSlowFill); // Can't request slow fill again: - await expect(spokePool.connect(relayer).requestV3SlowFill(relayData)).to.be.revertedWith( - "InvalidSlowFillRequest" - ); + await expect(spokePool.connect(relayer).requestSlowFill(relayData)).to.be.revertedWith("InvalidSlowFillRequest"); // Can fast fill after: - await spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + await spokePool.connect(relayer).fillRelay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); }); it("cannot request if FillStatus is Filled", async function () { const relayHash = getV3RelayHash(relayData, consts.destinationChainId); await spokePool.setFillStatus(relayHash, FillStatus.Filled); expect(await spokePool.fillStatuses(relayHash)).to.equal(FillStatus.Filled); - await expect(spokePool.connect(relayer).requestV3SlowFill(relayData)).to.be.revertedWith( - "InvalidSlowFillRequest" - ); + await expect(spokePool.connect(relayer).requestSlowFill(relayData)).to.be.revertedWith("InvalidSlowFillRequest"); }); it("fills are not paused", async function () { await spokePool.pauseFills(true); - await expect(spokePool.connect(relayer).requestV3SlowFill(relayData)).to.be.revertedWith("FillsArePaused"); + await expect(spokePool.connect(relayer).requestSlowFill(relayData)).to.be.revertedWith("FillsArePaused"); }); it("reentrancy protected", async function () { // In this test we create a reentrancy attempt by sending a fill with a recipient contract that calls back into // the spoke pool via the tested function. - const functionCalldata = spokePool.interface.encodeFunctionData("requestV3SlowFill", [relayData]); + const functionCalldata = spokePool.interface.encodeFunctionData("requestSlowFill", [relayData]); await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.revertedWith( "ReentrancyGuard: reentrant call" ); }); }); - describe("executeV3SlowRelayLeaf", function () { + describe("executeSlowRelayLeaf", function () { let relayData: V3RelayData, slowRelayLeaf: V3SlowFill; beforeEach(async function () { const fillDeadline = (await spokePool.getCurrentTime()).toNumber() + 1000; @@ -144,7 +140,7 @@ describe("SpokePool Slow Relay Logic", async function () { const tree = await buildV3SlowRelayTree([slowRelayLeaf]); await spokePool.connect(depositor).relayRootBundle(consts.mockTreeRoot, tree.getHexRoot()); await expect(() => - spokePool.connect(recipient).executeV3SlowRelayLeaf( + spokePool.connect(recipient).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) @@ -158,13 +154,13 @@ describe("SpokePool Slow Relay Logic", async function () { it("cannot double execute leaf", async function () { const tree = await buildV3SlowRelayTree([slowRelayLeaf]); await spokePool.connect(depositor).relayRootBundle(consts.mockTreeRoot, tree.getHexRoot()); - await spokePool.connect(relayer).executeV3SlowRelayLeaf( + await spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) ); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) @@ -175,7 +171,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3Relay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + .fillRelay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("RelayFilled"); }); it("cannot be used to double send a fill", async function () { @@ -185,9 +181,9 @@ describe("SpokePool Slow Relay Logic", async function () { // Fill before executing slow fill await spokePool .connect(relayer) - .fillV3Relay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + .fillRelay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) @@ -196,7 +192,7 @@ describe("SpokePool Slow Relay Logic", async function () { }); it("cannot re-enter", async function () { const tree = await buildV3SlowRelayTree([slowRelayLeaf]); - const functionCalldata = spokePool.interface.encodeFunctionData("executeV3SlowRelayLeaf", [ + const functionCalldata = spokePool.interface.encodeFunctionData("executeSlowRelayLeaf", [ slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf), @@ -210,7 +206,7 @@ describe("SpokePool Slow Relay Logic", async function () { const tree = await buildV3SlowRelayTree([slowRelayLeaf]); await spokePool.connect(depositor).relayRootBundle(consts.mockTreeRoot, tree.getHexRoot()); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) @@ -221,7 +217,7 @@ describe("SpokePool Slow Relay Logic", async function () { const tree = await buildV3SlowRelayTree([slowRelayLeaf]); await spokePool.connect(depositor).relayRootBundle(consts.mockTreeRoot, tree.getHexRoot()); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) @@ -242,7 +238,7 @@ describe("SpokePool Slow Relay Logic", async function () { .connect(depositor) .relayRootBundle(consts.mockTreeRoot, treeWithWrongDestinationChain.getHexRoot()); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeafWithWrongDestinationChain, 0, // rootBundleId treeWithWrongDestinationChain.getHexProof(slowRelayLeafWithWrongDestinationChain) @@ -261,7 +257,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Incorrect root bundle ID await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 1, // rootBundleId should be 0 tree.getHexProof(slowRelayLeaf) @@ -270,7 +266,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Invalid proof await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, tree.getHexProof(leafWithDifferentUpdatedOutputAmount) // Invalid proof @@ -281,7 +277,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .executeV3SlowRelayLeaf(leafWithDifferentUpdatedOutputAmount, 0, tree.getHexProof(slowRelayLeaf)) + .executeSlowRelayLeaf(leafWithDifferentUpdatedOutputAmount, 0, tree.getHexProof(slowRelayLeaf)) ).to.revertedWith("InvalidMerkleProof"); }); it("calls _fillRelay with expected params", async function () { @@ -289,13 +285,13 @@ describe("SpokePool Slow Relay Logic", async function () { await spokePool.connect(depositor).relayRootBundle(consts.mockTreeRoot, tree.getHexRoot()); await expect( - spokePool.connect(relayer).executeV3SlowRelayLeaf( + spokePool.connect(relayer).executeSlowRelayLeaf( slowRelayLeaf, 0, // rootBundleId tree.getHexProof(slowRelayLeaf) ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index 186b3df07..0628a7f21 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -336,12 +336,12 @@ describe("Polygon Spoke Pool", function () { message: "0x1234", }; const fillData = [ - polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ + polygonSpokePool.interface.encodeFunctionData("fillRelay", [ relayData, repaymentChainId, addressToBytes(relayer.address), ]), - polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ + polygonSpokePool.interface.encodeFunctionData("fillRelay", [ { ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address), diff --git a/test/evm/hardhat/constants.ts b/test/evm/hardhat/constants.ts index a8ed598d6..6cb8b2ee5 100644 --- a/test/evm/hardhat/constants.ts +++ b/test/evm/hardhat/constants.ts @@ -109,22 +109,3 @@ export const sampleRateModel = { R1: toWei(0.07).toString(), R2: toWei(0.75).toString(), }; - -export const SpokePoolFuncs = { - unsafeDepositV3Bytes: - "unsafeDepositV3(bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,bytes32,uint256,uint32,uint32,uint32,bytes)", - depositV3Bytes: - "depositV3(bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,bytes32,uint32,uint32,uint32,bytes)", - depositV3Address: - "depositV3(address,address,address,address,uint256,uint256,uint256,address,uint32,uint32,uint32,bytes)", - depositV3NowBytes: - "depositV3Now(bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,bytes32,uint32,uint32,bytes)", - depositV3NowAddress: - "depositV3Now(address,address,address,address,uint256,uint256,uint256,address,uint32,uint32,bytes)", - speedUpV3DepositBytes: "speedUpV3Deposit(bytes32,uint256,uint256,bytes32,bytes,bytes)", - speedUpV3DepositAddress: "speedUpV3Deposit(address,uint256,uint256,address,bytes,bytes)", - verifyUpdateV3DepositMessageBytes: - "verifyUpdateV3DepositMessage(bytes32,uint256,uint256,uint256,bytes32,bytes,bytes)", - verifyUpdateV3DepositMessageAddress: - "verifyUpdateV3DepositMessage(address,uint256,uint256,uint256,address,bytes,bytes)", -}; From dd88c3c4af6396c83759e0f3f9063ead8d3fd699 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Wed, 22 Jan 2025 22:43:28 -0500 Subject: [PATCH 2/5] fix: update legacy FilledV3Relay event to match old event signature (#873) * fix: update legacy event to match old event signature Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice * WIP Signed-off-by: Matt Rice --------- Signed-off-by: Matt Rice --- contracts/interfaces/V3SpokePoolInterface.sol | 10 +++++++++- 1 file changed, 9 insertions(+), 1 deletion(-) diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index 331fc23b4..8e5773114 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -373,6 +373,14 @@ interface V3SpokePoolInterface { bytes depositorSignature ); + // Legacy struct only used to preserve the FilledV3Relay event definition. + struct LegacyV3RelayExecutionEventInfo { + address updatedRecipient; + bytes updatedMessage; + uint256 updatedOutputAmount; + FillType fillType; + } + event FilledV3Relay( address inputToken, address outputToken, @@ -388,7 +396,7 @@ interface V3SpokePoolInterface { address depositor, address recipient, bytes message, - V3RelayExecutionEventInfo relayExecutionInfo + LegacyV3RelayExecutionEventInfo relayExecutionInfo ); event RequestedV3SlowFill( From c47ae110e8a00bd9aa1248ee01aad6cf0d356da8 Mon Sep 17 00:00:00 2001 From: bmzig <57361391+bmzig@users.noreply.github.com> Date: Tue, 4 Feb 2025 16:37:07 -0600 Subject: [PATCH 3/5] fix: use entire message when calculating relay hash for evm chains (#867) * fix: hash entire message when calculating relay hash for evm chains Signed-off-by: bennett * make getV3RelayHash public Signed-off-by: bennett * update fixture with relay hash change Signed-off-by: bennett --------- Signed-off-by: bennett --- contracts/SpokePool.sol | 33 +++++-------------- .../evm/hardhat/fixtures/SpokePool.Fixture.ts | 32 ++---------------- 2 files changed, 11 insertions(+), 54 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 888beb905..0a9c6c43a 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -976,7 +976,7 @@ abstract contract SpokePool is V3RelayExecutionParams memory relayExecution = V3RelayExecutionParams({ relay: relayData, - relayHash: _getV3RelayHash(relayData), + relayHash: getV3RelayHash(relayData), updatedOutputAmount: relayData.outputAmount, updatedRecipient: relayData.recipient, updatedMessage: relayData.message, @@ -1046,7 +1046,7 @@ abstract contract SpokePool is V3RelayExecutionParams memory relayExecution = V3RelayExecutionParams({ relay: relayData, - relayHash: _getV3RelayHash(relayData), + relayHash: getV3RelayHash(relayData), updatedOutputAmount: updatedOutputAmount, updatedRecipient: updatedRecipient, updatedMessage: updatedMessage, @@ -1093,7 +1093,7 @@ abstract contract SpokePool is } if (relayData.fillDeadline < currentTime) revert ExpiredFillDeadline(); - bytes32 relayHash = _getV3RelayHash(relayData); + bytes32 relayHash = getV3RelayHash(relayData); if (fillStatuses[relayHash] != uint256(FillStatus.Unfilled)) revert InvalidSlowFillRequest(); fillStatuses[relayHash] = uint256(FillStatus.RequestedSlowFill); @@ -1182,7 +1182,7 @@ abstract contract SpokePool is // deposit params like outputAmount, message and recipient. V3RelayExecutionParams memory relayExecution = V3RelayExecutionParams({ relay: relayData, - relayHash: _getV3RelayHash(relayData), + relayHash: getV3RelayHash(relayData), updatedOutputAmount: slowFillLeaf.updatedOutputAmount, updatedRecipient: relayData.recipient, updatedMessage: relayData.message, @@ -1302,6 +1302,10 @@ abstract contract SpokePool is return relayerRefund[l2TokenAddress][refundAddress]; } + function getV3RelayHash(V3RelayData memory relayData) public view returns (bytes32) { + return keccak256(abi.encode(relayData, chainId())); + } + /************************************** * INTERNAL FUNCTIONS * **************************************/ @@ -1627,27 +1631,6 @@ abstract contract SpokePool is return (amount * uint256(int256(1e18) - feesPct)) / 1e18; } - function _getV3RelayHash(V3RelayData memory relayData) private view returns (bytes32) { - return - keccak256( - abi.encode( - relayData.depositor, - relayData.recipient, - relayData.exclusiveRelayer, - relayData.inputToken, - relayData.outputToken, - relayData.inputAmount, - relayData.outputAmount, - relayData.originChainId, - relayData.depositId, - relayData.fillDeadline, - relayData.exclusivityDeadline, - _hashNonEmptyMessage(relayData.message), - chainId() - ) - ); - } - // Unwraps ETH and does a transfer to a recipient address. If the recipient is a smart contract then sends wrappedNativeToken. function _unwrapwrappedNativeTokenTo(address payable to, uint256 amount) internal { if (address(to).isContract()) { diff --git a/test/evm/hardhat/fixtures/SpokePool.Fixture.ts b/test/evm/hardhat/fixtures/SpokePool.Fixture.ts index a2be57f58..6293c87d9 100644 --- a/test/evm/hardhat/fixtures/SpokePool.Fixture.ts +++ b/test/evm/hardhat/fixtures/SpokePool.Fixture.ts @@ -180,39 +180,13 @@ export function getRelayHash( } export function getV3RelayHash(relayData: V3RelayData, destinationChainId: number): string { - const messageHash = relayData.message == "0x" ? ethers.constants.HashZero : ethers.utils.keccak256(relayData.message); return ethers.utils.keccak256( defaultAbiCoder.encode( [ - "bytes32", // depositor - "bytes32", // recipient - "bytes32", // exclusiveRelayer - "bytes32", // inputToken - "bytes32", // outputToken - "uint256", // inputAmount - "uint256", // outputAmount - "uint256", // originChainId - "uint256", // depositId - "uint32", // fillDeadline - "uint32", // exclusivityDeadline - "bytes32", // messageHash - "uint256", // destinationChainId + "tuple(bytes32 depositor, bytes32 recipient, bytes32 exclusiveRelayer, bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, uint256 outputAmount, uint256 originChainId, uint256 depositId, uint32 fillDeadline, uint32 exclusivityDeadline, bytes message)", + "uint256 destinationChainId", ], - [ - relayData.depositor, - relayData.recipient, - relayData.exclusiveRelayer, - relayData.inputToken, - relayData.outputToken, - relayData.inputAmount, - relayData.outputAmount, - relayData.originChainId, - relayData.depositId, - relayData.fillDeadline, - relayData.exclusivityDeadline, - messageHash, - destinationChainId, - ] + [relayData, destinationChainId] ) ); } From 6aeb4806e6514fb2d9d06a9afcb6c01b0602ea64 Mon Sep 17 00:00:00 2001 From: Paul <108695806+pxrl@users.noreply.github.com> Date: Tue, 4 Feb 2025 23:37:33 +0100 Subject: [PATCH 4/5] feat(SpokePool): Permit historical fillDeadline on deposit (#870) * feat(SpokePool): Permit historical fillDeadline on deposit This removes a sharp edge for pre-fill deposits, where the deposit comes after the fill. Permitting a historical fillDeadline gives more flexibility to the relayer around when they submit the deposit on the origin chain. * fix test * restore test * Bump approvals --- contracts/SpokePool.sol | 15 +++++---------- test/evm/hardhat/SpokePool.Deposit.ts | 6 +++--- 2 files changed, 8 insertions(+), 13 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 0a9c6c43a..962105388 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -492,8 +492,8 @@ abstract contract SpokePool is * This must be set to some time between [currentTime - depositQuoteTimeBuffer, currentTime] * where currentTime is block.timestamp on this chain or this transaction will revert. * @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 or this transaction will revert. + * the fill will revert on the destination chain. Must be set before 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 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: @@ -570,8 +570,8 @@ abstract contract SpokePool is * @param quoteTimestamp The HubPool timestamp that determines the system fee paid by the depositor. This must be set * between [currentTime - depositQuoteTimeBuffer, currentTime] where currentTime is block.timestamp on this chain. * @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. + * revert on the destination chain. Must be set before currentTime + fillDeadlineBuffer, where currentTime is block.timestamp + * on this chain. * @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: @@ -1330,12 +1330,7 @@ abstract contract SpokePool is // fillDeadline is relative to the destination chain. // Don’t allow fillDeadline to be more than several bundles into the future. // This limits the maximum required lookback for dataworker and relayer instances. - // Also, don't allow fillDeadline to be in the past. This poses a potential UX issue if the destination - // chain time keeping and this chain's time keeping are out of sync but is not really a practical hurdle - // unless they are significantly out of sync or the depositor is setting very short fill deadlines. This latter - // situation won't be a problem for honest users. - if (params.fillDeadline < currentTime || params.fillDeadline > currentTime + fillDeadlineBuffer) - revert InvalidFillDeadline(); + if (params.fillDeadline > currentTime + fillDeadlineBuffer) revert InvalidFillDeadline(); // There are three cases for setting the exclusivity deadline using the exclusivity parameter: // 1. If this parameter is 0, then there is no exclusivity period and emit 0 for the deadline. This diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 64cd36f8c..096e81c47 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -48,8 +48,8 @@ describe("SpokePool Depositor Logic", async function () { await seedWallet(depositor, [erc20], weth, amountToSeedWallets); // Approve spokepool to spend tokens - await erc20.connect(depositor).approve(spokePool.address, amountToDeposit); - await weth.connect(depositor).approve(spokePool.address, amountToDeposit); + await erc20.connect(depositor).approve(spokePool.address, amountToDeposit.mul(10)); + await weth.connect(depositor).approve(spokePool.address, amountToDeposit.mul(10)); // Whitelist origin token => destination chain ID routes: await enableRoutes(spokePool, [{ originToken: erc20.address }, { originToken: weth.address }]); @@ -503,7 +503,7 @@ describe("SpokePool Depositor Logic", async function () { // fillDeadline in past ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.sub(1) }) ) - ).to.be.revertedWith("InvalidFillDeadline"); + ).to.not.be.reverted; await expect( spokePool.connect(depositor).deposit( // fillDeadline right at the buffer is OK From cf0b6b8ff0d1e4f2d1bc516f39d33da1216b312a Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Tue, 4 Feb 2025 17:37:44 -0500 Subject: [PATCH 5/5] fix: add check to ensure depositor is a valid EVM address (#874) Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 3 +++ contracts/libraries/AddressConverters.sol | 10 +++++++--- test/evm/hardhat/SpokePool.Deposit.ts | 9 +++++++++ 3 files changed, 19 insertions(+), 3 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 962105388..ab1d57783 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -1311,6 +1311,9 @@ abstract contract SpokePool is **************************************/ function _depositV3(DepositV3Params memory params) internal { + // Verify depositor is a valid EVM address. + params.depositor.checkAddress(); + // Check that deposit route is enabled for the input token. There are no checks required for the output token // which is pulled from the relayer at fill time and passed through this contract atomically to the recipient. if (!enabledDepositRoutes[params.inputToken.toAddress()][params.destinationChainId]) revert DisabledRoute(); diff --git a/contracts/libraries/AddressConverters.sol b/contracts/libraries/AddressConverters.sol index 929bd789c..6b52ff479 100644 --- a/contracts/libraries/AddressConverters.sol +++ b/contracts/libraries/AddressConverters.sol @@ -8,15 +8,19 @@ library Bytes32ToAddress { error InvalidBytes32(); function toAddress(bytes32 _bytes32) internal pure returns (address) { - if (uint256(_bytes32) >> 160 != 0) { - revert InvalidBytes32(); - } + checkAddress(_bytes32); return address(uint160(uint256(_bytes32))); } function toAddressUnchecked(bytes32 _bytes32) internal pure returns (address) { return address(uint160(uint256(_bytes32))); } + + function checkAddress(bytes32 _bytes32) internal pure { + if (uint256(_bytes32) >> 160 != 0) { + revert InvalidBytes32(); + } + } } library AddressToBytes32 { diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 096e81c47..2e69e17c9 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -853,6 +853,15 @@ describe("SpokePool Depositor Logic", async function () { const functionCalldata = spokePool.interface.encodeFunctionData("deposit", [...depositArgs]); await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.reverted; }); + it("depositor must be valid evm address", async function () { + const functionCalldata = spokePool.interface.encodeFunctionData("deposit", [ + ...getDepositArgsFromRelayData({ + ...relayData, + depositor: "0x044852b2a670ade5407e78fb2863c51de9fcb96542a07186fe3aeda6bb8a116d", + }), + ]); + await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.reverted; + }); it("unsafe deposit ID", async function () { // new deposit ID should be the uint256 equivalent of the keccak256 hash of packed {msg.sender, depositor, forcedDepositId}. const forcedDepositId = "99";