From 84c618f3f04400654997b5d132991a6feb74842c Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 18 Dec 2024 12:00:01 +0100 Subject: [PATCH 01/24] WIP Signed-off-by: Chris Maree --- contracts/SpokePool.sol | 41 ++++- contracts/interfaces/V3SpokePoolInterface.sol | 18 ++ test/evm/hardhat/SpokePool.Relay.ts | 161 ++++++++++++------ test/evm/hardhat/constants.ts | 4 + 4 files changed, 166 insertions(+), 58 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index c8cc1c05a..178a0b192 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -1020,6 +1020,39 @@ abstract contract SpokePool is _fillRelayV3(relayExecution, repaymentAddress, false); } + // Exposes the same function as fillV3Relay 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 + }); + + // Call the existing fillV3Relay function + (bool success, bytes memory data) = address(this).delegatecall( + abi.encodeWithSignature( + "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + convertedRelayData, + repaymentChainId, + msg.sender.toBytes32() + ) + ); + if (!success) { + revert LowLevelCallFailed(data); + } + } + /** * @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 @@ -1149,9 +1182,11 @@ 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, - (relayData, destinationFillerData.repaymentChainId, msg.sender.toBytes32()) + abi.encode( + "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + relayData, + destinationFillerData.repaymentChainId, + msg.sender.toBytes32() ) ); if (!success) { diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index 34262013c..ad24f10c1 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -62,6 +62,22 @@ interface V3SpokePoolInterface { bytes message; } + // Same as V3RelayData but using addresses instead of bytes32. 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; + uint256 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; @@ -241,6 +257,8 @@ interface V3SpokePoolInterface { bytes32 repaymentAddress ) external; + function fillV3Relay(V3RelayDataLegacy calldata relayData, uint256 repaymentChainId) external; + function fillV3RelayWithUpdatedDeposit( V3RelayData calldata relayData, uint256 repaymentChainId, diff --git a/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index 51c946807..d75b6cf09 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -22,7 +22,15 @@ import { getUpdatedV3DepositSignature, getV3RelayHash, } from "./fixtures/SpokePool.Fixture"; -import * as consts from "./constants"; +import { + SpokePoolFuncs, + 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,24 +47,24 @@ async function getRelayExecutionParams( updatedOutputAmount: _relayData.outputAmount, updatedRecipient: _relayData.recipient, updatedMessage: _relayData.message, - repaymentChainId: consts.repaymentChainId, + repaymentChainId: repaymentChainId, }; } -describe("SpokePool Relayer Logic", async function () { +describe.only("SpokePool Relayer Logic", async function () { beforeEach(async function () { [depositor, recipient, relayer] = await ethers.getSigners(); ({ 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 +76,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 +87,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 +95,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 +105,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 +116,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( @@ -144,7 +152,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( @@ -180,7 +188,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, @@ -219,7 +227,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 +237,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 +254,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 +275,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 +289,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 +307,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 +328,15 @@ 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) + [SpokePoolFuncs.fillV3RelayBytes](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(SpokePoolFuncs.fillV3RelayBytes, [ relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), ]); await expect(spokePool.connect(relayer).callback(functionCalldata)).to.be.revertedWith( @@ -341,23 +351,27 @@ describe("SpokePool Relayer Logic", async function () { exclusivityDeadline: relayData.fillDeadline, }; await expect( - spokePool.connect(relayer).fillV3Relay(_relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool + .connect(relayer) + [SpokePoolFuncs.fillV3RelayBytes](_relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("NotExclusiveRelayer"); // Can send it after exclusivity deadline await expect( spokePool .connect(relayer) - .fillV3Relay( + [SpokePoolFuncs.fillV3RelayBytes]( { ..._relayData, exclusivityDeadline: 0 }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address) ) ).to.not.be.reverted; }); - it("calls _fillRelayV3 with expected params", async function () { + it("calls _fillRelayV3 with expected params", async function () { await expect( - spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool + .connect(relayer) + [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)) ) .to.emit(spokePool, "FilledV3Relay") .withArgs( @@ -365,7 +379,42 @@ describe("SpokePool Relayer Logic", async function () { 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)[SpokePoolFuncs.fillV3RelayAddress](legacyRelayData, repaymentChainId)) + .to.emit(spokePool, "FilledV3Relay") + .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, @@ -409,7 +458,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, exclusivityDeadline: 0 }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -430,7 +479,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( _relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -446,7 +495,7 @@ describe("SpokePool Relayer Logic", async function () { ..._relayData, exclusivityDeadline: 0, }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -466,7 +515,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -480,7 +529,7 @@ describe("SpokePool Relayer Logic", async function () { 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 +550,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 () { @@ -511,7 +560,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(relayer.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -534,7 +583,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -549,7 +598,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, originChainId: relayData.originChainId + 1 }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -564,7 +613,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, depositId: relayData.depositId.add(toBN(1)) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -579,7 +628,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount.sub(1), addressToBytes(updatedRecipient), @@ -594,7 +643,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(randomAddress()), @@ -609,7 +658,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -634,7 +683,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -647,7 +696,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -659,13 +708,13 @@ describe("SpokePool Relayer Logic", async function () { it("cannot send updated fill after original fill", async function () { await spokePool .connect(relayer) - .fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -679,7 +728,7 @@ describe("SpokePool Relayer Logic", async function () { .connect(relayer) .fillV3RelayWithUpdatedDeposit( relayData, - consts.repaymentChainId, + repaymentChainId, addressToBytes(relayer.address), updatedOutputAmount, addressToBytes(updatedRecipient), @@ -687,7 +736,9 @@ describe("SpokePool Relayer Logic", async function () { signature ); await expect( - spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + spokePool + .connect(relayer) + [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("RelayFilled"); }); }); diff --git a/test/evm/hardhat/constants.ts b/test/evm/hardhat/constants.ts index a8ed598d6..486fced5d 100644 --- a/test/evm/hardhat/constants.ts +++ b/test/evm/hardhat/constants.ts @@ -127,4 +127,8 @@ export const SpokePoolFuncs = { "verifyUpdateV3DepositMessage(bytes32,uint256,uint256,uint256,bytes32,bytes,bytes)", verifyUpdateV3DepositMessageAddress: "verifyUpdateV3DepositMessage(address,uint256,uint256,uint256,address,bytes,bytes)", + fillV3RelayBytes: + "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + fillV3RelayAddress: + "fillV3Relay((address,address,address,address,address,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256)", }; From 775128cd5a125e320fbe5b85764ded698a4b14a9 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 18 Dec 2024 12:06:13 +0100 Subject: [PATCH 02/24] WIP Signed-off-by: Chris Maree --- test/evm/hardhat/SpokePool.Relay.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index d75b6cf09..c8e927333 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -51,7 +51,7 @@ async function getRelayExecutionParams( }; } -describe.only("SpokePool Relayer Logic", async function () { +describe("SpokePool Relayer Logic", async function () { beforeEach(async function () { [depositor, recipient, relayer] = await ethers.getSigners(); ({ weth, erc20, spokePool, destErc20, erc1271 } = await spokePoolFixture()); From 326600c2220f72c71026a3f872d225479aa4c742 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 18 Dec 2024 14:31:21 +0100 Subject: [PATCH 03/24] WIP Signed-off-by: Chris Maree --- test/evm/hardhat/SpokePool.SlowRelay.ts | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/test/evm/hardhat/SpokePool.SlowRelay.ts b/test/evm/hardhat/SpokePool.SlowRelay.ts index 1088b3c34..8e6800107 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -12,7 +12,7 @@ import { import { spokePoolFixture, V3RelayData, getV3RelayHash, V3SlowFill, FillType } from "./fixtures/SpokePool.Fixture"; import { buildV3SlowRelayTree } from "./MerkleLib.utils"; import * as consts from "./constants"; -import { FillStatus } from "../../../utils/constants"; +import { FillStatus, fillV3RelayBytes } from "../../../utils/constants"; let spokePool: Contract, erc20: Contract, destErc20: Contract; let depositor: SignerWithAddress, recipient: SignerWithAddress, relayer: SignerWithAddress; @@ -91,7 +91,9 @@ describe("SpokePool Slow Relay Logic", async function () { ); // Can fast fill after: - await spokePool.connect(relayer).fillV3Relay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + await spokePool + .connect(relayer) + [fillV3RelayBytes](relayData, consts.repaymentChainId, addressToBytes(relayer.address)); }); it("cannot request if FillStatus is Filled", async function () { const relayHash = getV3RelayHash(relayData, consts.destinationChainId); @@ -175,7 +177,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3Relay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + [fillV3RelayBytes](slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("RelayFilled"); }); it("cannot be used to double send a fill", async function () { @@ -185,7 +187,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Fill before executing slow fill await spokePool .connect(relayer) - .fillV3Relay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + [fillV3RelayBytes](slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool.connect(relayer).executeV3SlowRelayLeaf( slowRelayLeaf, From 863f485075706383782d3bfbf8d2a85936259df1 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 18 Dec 2024 15:35:36 +0100 Subject: [PATCH 04/24] WIP Signed-off-by: Chris Maree --- test/evm/hardhat/SpokePool.SlowRelay.ts | 17 +++++++++++++---- .../Polygon_SpokePool.ts | 18 ++++++++---------- 2 files changed, 21 insertions(+), 14 deletions(-) diff --git a/test/evm/hardhat/SpokePool.SlowRelay.ts b/test/evm/hardhat/SpokePool.SlowRelay.ts index 8e6800107..4ad004ed8 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -12,7 +12,8 @@ import { import { spokePoolFixture, V3RelayData, getV3RelayHash, V3SlowFill, FillType } from "./fixtures/SpokePool.Fixture"; import { buildV3SlowRelayTree } from "./MerkleLib.utils"; import * as consts from "./constants"; -import { FillStatus, fillV3RelayBytes } from "../../../utils/constants"; +import { FillStatus } from "../../../utils/constants"; +import { SpokePoolFuncs } from "./constants"; let spokePool: Contract, erc20: Contract, destErc20: Contract; let depositor: SignerWithAddress, recipient: SignerWithAddress, relayer: SignerWithAddress; @@ -93,7 +94,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Can fast fill after: await spokePool .connect(relayer) - [fillV3RelayBytes](relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + [SpokePoolFuncs.fillV3RelayBytes](relayData, consts.repaymentChainId, addressToBytes(relayer.address)); }); it("cannot request if FillStatus is Filled", async function () { const relayHash = getV3RelayHash(relayData, consts.destinationChainId); @@ -177,7 +178,11 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - [fillV3RelayBytes](slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) + [SpokePoolFuncs.fillV3RelayBytes]( + slowRelayLeaf.relayData, + consts.repaymentChainId, + addressToBytes(relayer.address) + ) ).to.be.revertedWith("RelayFilled"); }); it("cannot be used to double send a fill", async function () { @@ -187,7 +192,11 @@ describe("SpokePool Slow Relay Logic", async function () { // Fill before executing slow fill await spokePool .connect(relayer) - [fillV3RelayBytes](slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + [SpokePoolFuncs.fillV3RelayBytes]( + slowRelayLeaf.relayData, + consts.repaymentChainId, + addressToBytes(relayer.address) + ); await expect( spokePool.connect(relayer).executeV3SlowRelayLeaf( slowRelayLeaf, diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index 186b3df07..64b5805eb 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -336,16 +336,14 @@ describe("Polygon Spoke Pool", function () { message: "0x1234", }; const fillData = [ - polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ - relayData, - repaymentChainId, - addressToBytes(relayer.address), - ]), - polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ - { ...relayData, depositId: 1 }, - repaymentChainId, - addressToBytes(relayer.address), - ]), + polygonSpokePool.interface.encodeFunctionData( + "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + [relayData, repaymentChainId, addressToBytes(relayer.address)] + ), + polygonSpokePool.interface.encodeFunctionData( + "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + [{ ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address)] + ), ]; const otherData = [polygonSpokePool.interface.encodeFunctionData("wrap", [])]; From c4d1b5fc6b500ee3b2a3ae5e2dd77a234dacc987 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Tue, 7 Jan 2025 18:25:58 +0100 Subject: [PATCH 05/24] refined overfloaded function structure Signed-off-by: Chris Maree --- contracts/SpokePool.sol | 24 ++--- contracts/SpokePoolVerifier.sol | 2 +- contracts/SwapAndBridge.sol | 2 +- contracts/interfaces/V3SpokePoolInterface.sol | 68 ++++++++++++-- contracts/permit2-order/Permit2Depositor.sol | 2 +- contracts/test/MockSpokePool.sol | 2 +- test/evm/hardhat/SpokePool.Deposit.ts | 93 +++++++++---------- test/evm/hardhat/SpokePool.Relay.ts | 27 ++---- test/evm/hardhat/SpokePool.SlowRelay.ts | 15 +-- .../Polygon_SpokePool.ts | 4 +- test/evm/hardhat/constants.ts | 23 ----- 11 files changed, 136 insertions(+), 126 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 178a0b192..832359fa8 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -504,7 +504,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 depositV3Bytes32( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -596,7 +596,7 @@ abstract contract SpokePool is uint32 exclusivityParameter, bytes calldata message ) public payable override { - depositV3( + depositV3Bytes32( depositor.toBytes32(), recipient.toBytes32(), inputToken.toBytes32(), @@ -633,8 +633,8 @@ abstract contract SpokePool is uint32 fillDeadline, uint32 exclusivityParameter, bytes calldata message - ) public payable { - unsafeDepositV3( + ) public payable override { + unsafeDepositV3Bytes32( depositor.toBytes32(), recipient.toBytes32(), inputToken.toBytes32(), @@ -683,7 +683,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 unsafeDepositV3Bytes32( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -754,7 +754,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 depositV3NowBytes32( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -766,8 +766,8 @@ abstract contract SpokePool is uint32 fillDeadlineOffset, uint32 exclusivityPeriod, bytes calldata message - ) external payable { - depositV3( + ) external payable override { + depositV3Bytes32( depositor, recipient, inputToken, @@ -827,7 +827,7 @@ abstract contract SpokePool is uint32 fillDeadlineOffset, uint32 exclusivityPeriod, bytes calldata message - ) external payable { + ) external payable override { depositV3( depositor, recipient, @@ -862,7 +862,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 speedUpV3DepositBytes32( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -994,7 +994,7 @@ abstract contract SpokePool is * @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. */ - function fillV3Relay( + function fillV3RelayBytes32( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress @@ -1042,7 +1042,7 @@ abstract contract SpokePool is // Call the existing fillV3Relay function (bool success, bytes memory data) = address(this).delegatecall( abi.encodeWithSignature( - "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", convertedRelayData, repaymentChainId, msg.sender.toBytes32() diff --git a/contracts/SpokePoolVerifier.sol b/contracts/SpokePoolVerifier.sol index 2aa038498..9d005c982 100644 --- a/contracts/SpokePoolVerifier.sol +++ b/contracts/SpokePoolVerifier.sol @@ -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.depositV3Bytes32{ value: msg.value }( msg.sender.toBytes32(), recipient, inputToken, diff --git a/contracts/SwapAndBridge.sol b/contracts/SwapAndBridge.sol index 284d2f28f..21a36eef9 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.depositV3Bytes32( depositData.depositor.toBytes32(), depositData.recipient.toBytes32(), address(_acrossInputToken).toBytes32(), // input token diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index ad24f10c1..635fd341f 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -62,7 +62,8 @@ interface V3SpokePoolInterface { bytes message; } - // Same as V3RelayData but using addresses instead of bytes32. Will be deprecated in favor of V3RelayData in the future. + // 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; @@ -72,7 +73,7 @@ interface V3SpokePoolInterface { uint256 inputAmount; uint256 outputAmount; uint256 originChainId; - uint256 depositId; + uint32 depositId; uint32 fillDeadline; uint32 exclusivityDeadline; bytes message; @@ -198,7 +199,7 @@ interface V3SpokePoolInterface { * FUNCTIONS * **************************************/ - function depositV3( + function depositV3Bytes32( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -228,7 +229,7 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - function depositV3Now( + function depositV3NowBytes32( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -242,7 +243,53 @@ 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 unsafeDepositV3Bytes32( + 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 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 + ) external payable; + + function speedUpV3DepositBytes32( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -251,7 +298,16 @@ 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 fillV3RelayBytes32( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress diff --git a/contracts/permit2-order/Permit2Depositor.sol b/contracts/permit2-order/Permit2Depositor.sol index d265e24e1..3c729c954 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.depositV3Bytes32( 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..fb23cddb1 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, diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 1160f5d72..c1a9851bc 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; @@ -412,44 +411,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).depositV3Bytes32(...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).depositV3Bytes32(..._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).depositV3Bytes32(..._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).depositV3Bytes32( // 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).depositV3Bytes32( // 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).depositV3Bytes32( // quoteTimestamp right at the buffer is OK ...getDepositArgsFromRelayData(relayData, destinationChainId, currentTime.sub(quoteTimeBuffer)) ) @@ -460,19 +455,19 @@ describe("SpokePool Depositor Logic", async function () { const currentTime = await spokePool.getCurrentTime(); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( // 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).depositV3Bytes32( // fillDeadline in past ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.sub(1) }) ) ).to.be.revertedWith("InvalidFillDeadline"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( // fillDeadline right at the buffer is OK ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.add(fillDeadlineBuffer) }) ) @@ -483,7 +478,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).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -492,7 +487,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -501,7 +496,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -510,7 +505,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -519,7 +514,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -528,7 +523,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -542,7 +537,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).depositV3Bytes32( ...getDepositArgsFromRelayData( { ...relayData, @@ -577,7 +572,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).depositV3Bytes32( ...getDepositArgsFromRelayData( { ...relayData, @@ -612,7 +607,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const zeroExclusivity = 0; await expect( - spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes]( + spokePool.connect(depositor).depositV3Bytes32( ...getDepositArgsFromRelayData( { ...relayData, @@ -646,7 +641,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .depositV3Bytes32(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { value: 1, }) ).to.be.revertedWith("MsgValueDoesNotMatchInputAmount"); @@ -655,7 +650,7 @@ describe("SpokePool Depositor Logic", async function () { await expect(() => spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .depositV3Bytes32(...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 +660,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).depositV3Bytes32(...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 }), { + .depositV3Bytes32(...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).depositV3Bytes32(...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 +687,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3NowBytes]( + .depositV3NowBytes32( addressToBytes(relayData.depositor), addressToBytes(relayData.recipient), addressToBytes(relayData.inputToken), @@ -731,7 +726,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3NowAddress]( + .depositV3Now( bytes32ToAddress(relayData.depositor), bytes32ToAddress(relayData.recipient), bytes32ToAddress(relayData.inputToken), @@ -764,7 +759,7 @@ describe("SpokePool Depositor Logic", async function () { ); }); it("emits V3FundsDeposited event with correct deposit ID", async function () { - await expect(spokePool.connect(depositor)[SpokePoolFuncs.depositV3Bytes](...depositArgs)) + await expect(spokePool.connect(depositor).depositV3Bytes32(...depositArgs)) .to.emit(spokePool, "V3FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), @@ -784,7 +779,7 @@ 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).depositV3Bytes32(...depositArgs); expect(await spokePool.numberOfDeposits()).to.equal(1); }); it("tokens are always pulled from caller, even if different from specified depositor", async function () { @@ -793,7 +788,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.depositV3Bytes](...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) + .depositV3Bytes32(...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) ) .to.emit(spokePool, "V3FundsDeposited") .withArgs( @@ -816,12 +811,12 @@ 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( + await expect(spokePool.connect(depositor).depositV3Bytes32(...depositArgs)).to.be.revertedWith( "DepositsArePaused" ); }); it("reentrancy protected", async function () { - const functionCalldata = spokePool.interface.encodeFunctionData(SpokePoolFuncs.depositV3Bytes, [...depositArgs]); + const functionCalldata = spokePool.interface.encodeFunctionData("depositV3Bytes32", [...depositArgs]); await expect(spokePool.connect(depositor).callback(functionCalldata)).to.be.revertedWith( "ReentrancyGuard: reentrant call" ); @@ -843,7 +838,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.unsafeDepositV3Bytes]( + .unsafeDepositV3Bytes32( ...getUnsafeDepositArgsFromRelayData({ ...relayData, depositor: recipient.address }, forcedDepositId) ) ) @@ -879,7 +874,7 @@ describe("SpokePool Depositor Logic", async function () { addressToBytes(updatedRecipient), updatedMessage ); - await spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + await spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, originChainId, @@ -891,7 +886,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 +907,7 @@ describe("SpokePool Depositor Logic", async function () { updatedMessage ); await expect( - spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, originChainId, @@ -937,7 +932,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositBytes]( + .speedUpV3DepositBytes32( addressToBytes(depositor.address), depositId, updatedOutputAmount, @@ -967,7 +962,7 @@ describe("SpokePool Depositor Logic", async function () { updatedMessage ); await expect( - spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageBytes]( + spokePool.verifyUpdateV3DepositMessageBytes32( addressToBytes(depositor.address), depositId, otherChainId, @@ -980,7 +975,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositBytes]( + .speedUpV3DepositBytes32( addressToBytes(depositor.address), depositId, updatedOutputAmount, @@ -1007,7 +1002,7 @@ describe("SpokePool Depositor Logic", async function () { true ); - await spokePool[SpokePoolFuncs.verifyUpdateV3DepositMessageAddress]( + await spokePool.verifyUpdateV3DepositMessage( depositor.address, depositId, spokePoolChainId, @@ -1020,7 +1015,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - [SpokePoolFuncs.speedUpV3DepositAddress]( + .speedUpV3Deposit( depositor.address, depositId, updatedOutputAmount, diff --git a/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index c8e927333..0d001e215 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -23,7 +23,6 @@ import { getV3RelayHash, } from "./fixtures/SpokePool.Fixture"; import { - SpokePoolFuncs, repaymentChainId, amountToSeedWallets, amountToDeposit, @@ -328,13 +327,11 @@ describe("SpokePool Relayer Logic", async function () { it("fills are not paused", async function () { await spokePool.pauseFills(true); await expect( - spokePool - .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillV3RelayBytes32(relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("FillsArePaused"); }); it("reentrancy protected", async function () { - const functionCalldata = spokePool.interface.encodeFunctionData(SpokePoolFuncs.fillV3RelayBytes, [ + const functionCalldata = spokePool.interface.encodeFunctionData("fillV3RelayBytes32", [ relayData, repaymentChainId, addressToBytes(relayer.address), @@ -351,16 +348,14 @@ describe("SpokePool Relayer Logic", async function () { exclusivityDeadline: relayData.fillDeadline, }; await expect( - spokePool - .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](_relayData, repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillV3RelayBytes32(_relayData, repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("NotExclusiveRelayer"); // Can send it after exclusivity deadline await expect( spokePool .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes]( + .fillV3RelayBytes32( { ..._relayData, exclusivityDeadline: 0 }, repaymentChainId, addressToBytes(relayer.address) @@ -369,9 +364,7 @@ describe("SpokePool Relayer Logic", async function () { }); it("calls _fillRelayV3 with expected params", async function () { await expect( - spokePool - .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillV3RelayBytes32(relayData, repaymentChainId, addressToBytes(relayer.address)) ) .to.emit(spokePool, "FilledV3Relay") .withArgs( @@ -398,7 +391,7 @@ describe("SpokePool Relayer Logic", async function () { ] ); }); - it("calls legacy (address) _fillRelayV3 with expected params", async function () { + it("calls legacy (address) fillRelayV3 with expected params", async function () { const legacyRelayData = { ...relayData, depositor: bytes32ToAddress(relayData.depositor), @@ -407,7 +400,7 @@ describe("SpokePool Relayer Logic", async function () { inputToken: bytes32ToAddress(relayData.inputToken), outputToken: bytes32ToAddress(relayData.outputToken), }; - await expect(spokePool.connect(relayer)[SpokePoolFuncs.fillV3RelayAddress](legacyRelayData, repaymentChainId)) + await expect(spokePool.connect(relayer).fillV3Relay(legacyRelayData, repaymentChainId)) .to.emit(spokePool, "FilledV3Relay") .withArgs( addressToBytes(relayData.inputToken), @@ -708,7 +701,7 @@ describe("SpokePool Relayer Logic", async function () { it("cannot send updated fill after original fill", async function () { await spokePool .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)); + .fillV3RelayBytes32(relayData, repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool .connect(relayer) @@ -736,9 +729,7 @@ describe("SpokePool Relayer Logic", async function () { signature ); await expect( - spokePool - .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](relayData, repaymentChainId, addressToBytes(relayer.address)) + spokePool.connect(relayer).fillV3RelayBytes32(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 4ad004ed8..ee6ea31f6 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -13,7 +13,6 @@ import { spokePoolFixture, V3RelayData, getV3RelayHash, V3SlowFill, FillType } f import { buildV3SlowRelayTree } from "./MerkleLib.utils"; import * as consts from "./constants"; import { FillStatus } from "../../../utils/constants"; -import { SpokePoolFuncs } from "./constants"; let spokePool: Contract, erc20: Contract, destErc20: Contract; let depositor: SignerWithAddress, recipient: SignerWithAddress, relayer: SignerWithAddress; @@ -94,7 +93,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Can fast fill after: await spokePool .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes](relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + .fillV3RelayBytes32(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); }); it("cannot request if FillStatus is Filled", async function () { const relayHash = getV3RelayHash(relayData, consts.destinationChainId); @@ -178,11 +177,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes]( - slowRelayLeaf.relayData, - consts.repaymentChainId, - addressToBytes(relayer.address) - ) + .fillV3RelayBytes32(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)) ).to.be.revertedWith("RelayFilled"); }); it("cannot be used to double send a fill", async function () { @@ -192,11 +187,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Fill before executing slow fill await spokePool .connect(relayer) - [SpokePoolFuncs.fillV3RelayBytes]( - slowRelayLeaf.relayData, - consts.repaymentChainId, - addressToBytes(relayer.address) - ); + .fillV3RelayBytes32(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool.connect(relayer).executeV3SlowRelayLeaf( slowRelayLeaf, diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index 64b5805eb..028f60085 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -337,11 +337,11 @@ describe("Polygon Spoke Pool", function () { }; const fillData = [ polygonSpokePool.interface.encodeFunctionData( - "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", [relayData, repaymentChainId, addressToBytes(relayer.address)] ), polygonSpokePool.interface.encodeFunctionData( - "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", [{ ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address)] ), ]; diff --git a/test/evm/hardhat/constants.ts b/test/evm/hardhat/constants.ts index 486fced5d..6cb8b2ee5 100644 --- a/test/evm/hardhat/constants.ts +++ b/test/evm/hardhat/constants.ts @@ -109,26 +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)", - fillV3RelayBytes: - "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", - fillV3RelayAddress: - "fillV3Relay((address,address,address,address,address,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256)", -}; From 7972b058505b8c5530d468f89f90fd916a3dc186 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Tue, 7 Jan 2025 18:29:56 +0100 Subject: [PATCH 06/24] Discard changes to test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts --- .../Polygon_SpokePool.ts | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index 028f60085..186b3df07 100644 --- a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts +++ b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts @@ -336,14 +336,16 @@ describe("Polygon Spoke Pool", function () { message: "0x1234", }; const fillData = [ - polygonSpokePool.interface.encodeFunctionData( - "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", - [relayData, repaymentChainId, addressToBytes(relayer.address)] - ), - polygonSpokePool.interface.encodeFunctionData( - "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", - [{ ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address)] - ), + polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ + relayData, + repaymentChainId, + addressToBytes(relayer.address), + ]), + polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ + { ...relayData, depositId: 1 }, + repaymentChainId, + addressToBytes(relayer.address), + ]), ]; const otherData = [polygonSpokePool.interface.encodeFunctionData("wrap", [])]; From 5862c654479aa962cde7e87aea05410d1a2c7aa8 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Tue, 7 Jan 2025 18:30:14 +0100 Subject: [PATCH 07/24] WIP Signed-off-by: Chris Maree --- contracts/SpokePool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 832359fa8..59d0b7f62 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -1020,7 +1020,7 @@ abstract contract SpokePool is _fillRelayV3(relayExecution, repaymentAddress, false); } - // Exposes the same function as fillV3Relay but with a legacy V3RelayData struct that takes in address types. Inner + // Exposes the same function as fillV3RelayBytes32 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 From 4573e42856ad4fd7a36a23e20f3b4874531c694a Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Tue, 7 Jan 2025 18:32:53 +0100 Subject: [PATCH 08/24] WIP Signed-off-by: Chris Maree --- .../hardhat/chain-specific-spokepools/Polygon_SpokePool.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index 186b3df07..f76f72fbe 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("fillV3RelayBytes32", [ relayData, repaymentChainId, addressToBytes(relayer.address), ]), - polygonSpokePool.interface.encodeFunctionData("fillV3Relay", [ + polygonSpokePool.interface.encodeFunctionData("fillV3RelayBytes32", [ { ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address), From 8c223e85fe0c476f310991600eb3087092766c2f Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 8 Jan 2025 12:17:22 +0100 Subject: [PATCH 09/24] WIP Signed-off-by: Chris Maree --- test/evm/foundry/local/SpokePoolVerifier.t.sol | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) diff --git a/test/evm/foundry/local/SpokePoolVerifier.t.sol b/test/evm/foundry/local/SpokePoolVerifier.t.sol index 3b36a006f..dc5f530ca 100644 --- a/test/evm/foundry/local/SpokePoolVerifier.t.sol +++ b/test/evm/foundry/local/SpokePoolVerifier.t.sol @@ -78,7 +78,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if inputToken is WETH and msg.value is not equal to inputAmount vm.expectRevert(SpokePoolVerifier.InvalidMsgValue.selector); - spokePoolVerifier.deposit{ value: 0 }( + spokePoolVerifier.depositBytes32{ value: 0 }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken @@ -94,7 +94,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if msg.value matches inputAmount but inputToken is not WETH vm.expectRevert(V3SpokePoolInterface.MsgValueDoesNotMatchInputAmount.selector); - spokePoolVerifier.deposit{ value: depositAmount }( + spokePoolVerifier.depositBytes32{ value: depositAmount }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockERC20).toBytes32(), // inputToken @@ -116,7 +116,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if spokePool is not a contract vm.expectRevert(SpokePoolVerifier.InvalidSpokePool.selector); - spokePoolVerifier.deposit{ value: depositAmount }( + spokePoolVerifier.depositBytes32{ value: depositAmount }( V3SpokePoolInterface(address(0)), // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken @@ -141,7 +141,7 @@ contract SpokePoolVerifierTest is Test { address(ethereumSpokePool), // callee depositAmount, // value abi.encodeCall( // data - EthereumSpokePoolOnlyAddressInterface.depositV3, + EthereumSpokePoolOnlyAddressInterface.depositV3Bytes32, ( depositor.toBytes32(), depositor.toBytes32(), @@ -158,7 +158,7 @@ contract SpokePoolVerifierTest is Test { ) ) ); - spokePoolVerifier.deposit{ value: depositAmount }( + spokePoolVerifier.depositBytes32{ value: depositAmount }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken From fba59700f4612198ef32dd04ba2dfa092e2023bc Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Wed, 8 Jan 2025 12:33:12 +0100 Subject: [PATCH 10/24] WIP Signed-off-by: Chris Maree --- contracts/SpokePoolVerifier.sol | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/contracts/SpokePoolVerifier.sol b/contracts/SpokePoolVerifier.sol index 9d005c982..3f69e6580 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 `depositV3Bytes32()` 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. @@ -42,7 +42,7 @@ contract SpokePoolVerifier { * to 0 if exclusiveRelayer is set to 0x0, and vice versa. * @param fillDeadline Timestamp after which this deposit can no longer be filled. */ - function deposit( + function depositV3Bytes32( V3SpokePoolInterface spokePool, bytes32 recipient, bytes32 inputToken, From 53a0ab0fdab2db410fe8df30fd39aebee33ea7fd Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 12:26:27 -0500 Subject: [PATCH 11/24] WIP Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 73 +++++-------------- contracts/SpokePoolVerifier.sol | 6 +- contracts/SwapAndBridge.sol | 2 +- .../erc7683/ERC7683OrderDepositorExternal.sol | 12 +-- contracts/interfaces/SpokePoolInterface.sol | 2 +- contracts/interfaces/V3SpokePoolInterface.sol | 26 ++----- contracts/permit2-order/Permit2Depositor.sol | 2 +- foundry.toml | 4 +- hardhat.config.ts | 4 +- .../evm/foundry/local/SpokePoolVerifier.t.sol | 12 +-- test/evm/hardhat/SpokePool.Deposit.ts | 66 ++++++++--------- test/evm/hardhat/SpokePool.Relay.ts | 22 ++---- test/evm/hardhat/SpokePool.SlowRelay.ts | 8 +- .../Polygon_SpokePool.ts | 4 +- 14 files changed, 89 insertions(+), 154 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 59d0b7f62..ca6c024e1 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -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. @@ -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 deposit_5962092001( 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 depositV3Bytes32( + 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,45 +600,7 @@ abstract contract SpokePool is uint32 exclusivityParameter, bytes calldata message ) public payable override { - depositV3Bytes32( - depositor.toBytes32(), - recipient.toBytes32(), - inputToken.toBytes32(), - outputToken.toBytes32(), - inputAmount, - outputAmount, - destinationChainId, - exclusiveRelayer.toBytes32(), - quoteTimestamp, - fillDeadline, - exclusivityParameter, - message - ); - } - - /** - * @notice An overloaded version of `unsafeDepositV3` that accepts `address` types for backward compatibility. * - * @dev This version mirrors the original `unsafeDepositV3` function, but uses `address` types for `depositor`, `recipient`, - * `inputToken`, `outputToken`, and `exclusiveRelayer` for compatibility with contracts using the `address` type. - * - * The key functionality and logic remain identical, ensuring interoperability across both versions. - */ - function unsafeDepositV3( - address depositor, - address recipient, - address inputToken, - address outputToken, - uint256 inputAmount, - uint256 outputAmount, - uint256 destinationChainId, - address exclusiveRelayer, - uint256 depositNonce, - uint32 quoteTimestamp, - uint32 fillDeadline, - uint32 exclusivityParameter, - bytes calldata message - ) public payable override { - unsafeDepositV3Bytes32( + deposit( depositor.toBytes32(), recipient.toBytes32(), inputToken.toBytes32(), @@ -643,7 +609,6 @@ abstract contract SpokePool is outputAmount, destinationChainId, exclusiveRelayer.toBytes32(), - depositNonce, quoteTimestamp, fillDeadline, exclusivityParameter, @@ -652,7 +617,7 @@ abstract contract SpokePool is } /** - * @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 @@ -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 unsafeDepositV3Bytes32( + 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 depositV3NowBytes32( + function depositNow( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -767,7 +732,7 @@ abstract contract SpokePool is uint32 exclusivityPeriod, bytes calldata message ) external payable override { - depositV3Bytes32( + 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. @@ -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 speedUpV3DepositBytes32( + function speedUpDeposit( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -894,7 +859,7 @@ 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. @@ -994,7 +959,7 @@ abstract contract SpokePool is * @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. */ - function fillV3RelayBytes32( + function fillRelay( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress @@ -1020,7 +985,7 @@ abstract contract SpokePool is _fillRelayV3(relayExecution, repaymentAddress, false); } - // Exposes the same function as fillV3RelayBytes32 but with a legacy V3RelayData struct that takes in address types. Inner + // 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 @@ -1042,7 +1007,7 @@ abstract contract SpokePool is // Call the existing fillV3Relay function (bool success, bytes memory data) = address(this).delegatecall( abi.encodeWithSignature( - "fillV3RelayBytes32((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + "fillRelay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", convertedRelayData, repaymentChainId, msg.sender.toBytes32() diff --git a/contracts/SpokePoolVerifier.sol b/contracts/SpokePoolVerifier.sol index 3f69e6580..b15bf41ad 100644 --- a/contracts/SpokePoolVerifier.sol +++ b/contracts/SpokePoolVerifier.sol @@ -21,7 +21,7 @@ contract SpokePoolVerifier { error InvalidSpokePool(); /** - * @notice Passthrough function to `depositV3Bytes32()` 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. @@ -42,7 +42,7 @@ contract SpokePoolVerifier { * to 0 if exclusiveRelayer is set to 0x0, and vice versa. * @param fillDeadline Timestamp after which this deposit can no longer be filled. */ - function depositV3Bytes32( + function deposit( V3SpokePoolInterface spokePool, bytes32 recipient, bytes32 inputToken, @@ -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.depositV3Bytes32{ value: msg.value }( + spokePool.deposit{ value: msg.value }( msg.sender.toBytes32(), recipient, inputToken, diff --git a/contracts/SwapAndBridge.sol b/contracts/SwapAndBridge.sol index 21a36eef9..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.depositV3Bytes32( + 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..fc26624d4 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 deposit_5962092001( address recipient, address originToken, uint256 amount, diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index 635fd341f..d7daf76c3 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -199,7 +199,7 @@ interface V3SpokePoolInterface { * FUNCTIONS * **************************************/ - function depositV3Bytes32( + function deposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -229,7 +229,7 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - function depositV3NowBytes32( + function depositNow( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -257,7 +257,7 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - function unsafeDepositV3Bytes32( + function unsafeDeposit( bytes32 depositor, bytes32 recipient, bytes32 inputToken, @@ -273,23 +273,7 @@ interface V3SpokePoolInterface { bytes calldata message ) external payable; - 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 - ) external payable; - - function speedUpV3DepositBytes32( + function speedUpDeposit( bytes32 depositor, uint256 depositId, uint256 updatedOutputAmount, @@ -307,7 +291,7 @@ interface V3SpokePoolInterface { bytes calldata depositorSignature ) external; - function fillV3RelayBytes32( + function fillRelay( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress diff --git a/contracts/permit2-order/Permit2Depositor.sol b/contracts/permit2-order/Permit2Depositor.sol index 3c729c954..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.depositV3Bytes32( + 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/foundry.toml b/foundry.toml index 1b5feeaba..3f6b9437b 100644 --- a/foundry.toml +++ b/foundry.toml @@ -24,10 +24,10 @@ remappings = [ ] via_ir = true optimizer_runs = 1_000_000 -solc_version = "0.8.25" +solc_version = "0.8.28" revert_strings = "strip" -solc = "0.8.25" +solc = "0.8.28" evm_version = "shanghai" [rpc_endpoints] diff --git a/hardhat.config.ts b/hardhat.config.ts index 594ae3c1b..1554cb1c0 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/SpokePoolVerifier.t.sol b/test/evm/foundry/local/SpokePoolVerifier.t.sol index dc5f530ca..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, @@ -78,7 +78,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if inputToken is WETH and msg.value is not equal to inputAmount vm.expectRevert(SpokePoolVerifier.InvalidMsgValue.selector); - spokePoolVerifier.depositBytes32{ value: 0 }( + spokePoolVerifier.deposit{ value: 0 }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken @@ -94,7 +94,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if msg.value matches inputAmount but inputToken is not WETH vm.expectRevert(V3SpokePoolInterface.MsgValueDoesNotMatchInputAmount.selector); - spokePoolVerifier.depositBytes32{ value: depositAmount }( + spokePoolVerifier.deposit{ value: depositAmount }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockERC20).toBytes32(), // inputToken @@ -116,7 +116,7 @@ contract SpokePoolVerifierTest is Test { // Reverts if spokePool is not a contract vm.expectRevert(SpokePoolVerifier.InvalidSpokePool.selector); - spokePoolVerifier.depositBytes32{ value: depositAmount }( + spokePoolVerifier.deposit{ value: depositAmount }( V3SpokePoolInterface(address(0)), // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken @@ -141,7 +141,7 @@ contract SpokePoolVerifierTest is Test { address(ethereumSpokePool), // callee depositAmount, // value abi.encodeCall( // data - EthereumSpokePoolOnlyAddressInterface.depositV3Bytes32, + EthereumSpokePoolOnlyAddressInterface.deposit, ( depositor.toBytes32(), depositor.toBytes32(), @@ -158,7 +158,7 @@ contract SpokePoolVerifierTest is Test { ) ) ); - spokePoolVerifier.depositBytes32{ value: depositAmount }( + spokePoolVerifier.deposit{ value: depositAmount }( ethereumSpokePool, // spokePool depositor.toBytes32(), // recipient address(mockWETH).toBytes32(), // inputToken diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index c1a9851bc..8d5a0fd1c 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -411,7 +411,7 @@ describe("SpokePool Depositor Logic", async function () { depositArgs = getDepositArgsFromRelayData(relayData); }); it("placeholder: gas test", async function () { - await spokePool.connect(depositor).depositV3Bytes32(...depositArgs); + await spokePool.connect(depositor).deposit(...depositArgs); }); it("should allow depositv3 with address overload", async function () { await spokePool @@ -421,30 +421,30 @@ describe("SpokePool Depositor Logic", async function () { 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).depositV3Bytes32(..._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).depositV3Bytes32(..._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).depositV3Bytes32( + 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).depositV3Bytes32( + 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).depositV3Bytes32( + spokePool.connect(depositor).deposit( // quoteTimestamp right at the buffer is OK ...getDepositArgsFromRelayData(relayData, destinationChainId, currentTime.sub(quoteTimeBuffer)) ) @@ -455,19 +455,19 @@ describe("SpokePool Depositor Logic", async function () { const currentTime = await spokePool.getCurrentTime(); await expect( - spokePool.connect(depositor).depositV3Bytes32( + 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).depositV3Bytes32( + spokePool.connect(depositor).deposit( // fillDeadline in past ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.sub(1) }) ) ).to.be.revertedWith("InvalidFillDeadline"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( // fillDeadline right at the buffer is OK ...getDepositArgsFromRelayData({ ...relayData, fillDeadline: currentTime.add(fillDeadlineBuffer) }) ) @@ -478,7 +478,7 @@ describe("SpokePool Depositor Logic", async function () { // If exclusive deadline is not zero, then exclusive relayer must be set. await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -487,7 +487,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -496,7 +496,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -505,7 +505,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -514,7 +514,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -523,7 +523,7 @@ describe("SpokePool Depositor Logic", async function () { ) ).to.be.revertedWith("InvalidExclusiveRelayer"); await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData({ ...relayData, exclusiveRelayer: zeroAddress, @@ -537,7 +537,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const exclusivityDeadlineOffset = MAX_EXCLUSIVITY_OFFSET_SECONDS; await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -572,7 +572,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const exclusivityDeadlineTimestamp = MAX_EXCLUSIVITY_OFFSET_SECONDS + 1; await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -607,7 +607,7 @@ describe("SpokePool Depositor Logic", async function () { const fillDeadlineOffset = 1000; const zeroExclusivity = 0; await expect( - spokePool.connect(depositor).depositV3Bytes32( + spokePool.connect(depositor).deposit( ...getDepositArgsFromRelayData( { ...relayData, @@ -641,7 +641,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - .depositV3Bytes32(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { + .deposit(...getDepositArgsFromRelayData({ ...relayData, inputToken: weth.address }), { value: 1, }) ).to.be.revertedWith("MsgValueDoesNotMatchInputAmount"); @@ -650,7 +650,7 @@ describe("SpokePool Depositor Logic", async function () { await expect(() => spokePool .connect(depositor) - .depositV3Bytes32(...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. @@ -660,20 +660,20 @@ 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).depositV3Bytes32(...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) - .depositV3Bytes32(...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).depositV3Bytes32(...depositArgs)).to.changeTokenBalances( + await expect(() => spokePool.connect(depositor).deposit(...depositArgs)).to.changeTokenBalances( erc20, [depositor, spokePool], [amountToDeposit.mul(toBN("-1")), amountToDeposit] @@ -687,7 +687,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - .depositV3NowBytes32( + .depositNow( addressToBytes(relayData.depositor), addressToBytes(relayData.recipient), addressToBytes(relayData.inputToken), @@ -759,7 +759,7 @@ describe("SpokePool Depositor Logic", async function () { ); }); it("emits V3FundsDeposited event with correct deposit ID", async function () { - await expect(spokePool.connect(depositor).depositV3Bytes32(...depositArgs)) + await expect(spokePool.connect(depositor).deposit(...depositArgs)) .to.emit(spokePool, "V3FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), @@ -779,16 +779,14 @@ describe("SpokePool Depositor Logic", async function () { ); }); it("deposit ID state variable incremented", async function () { - await spokePool.connect(depositor).depositV3Bytes32(...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) - .depositV3Bytes32(...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) + spokePool.connect(depositor).deposit(...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) ) .to.emit(spokePool, "V3FundsDeposited") .withArgs( @@ -811,9 +809,7 @@ describe("SpokePool Depositor Logic", async function () { }); it("deposits are not paused", async function () { await spokePool.pauseDeposits(true); - await expect(spokePool.connect(depositor).depositV3Bytes32(...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("depositV3Bytes32", [...depositArgs]); @@ -838,7 +834,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - .unsafeDepositV3Bytes32( + .unsafeDeposit( ...getUnsafeDepositArgsFromRelayData({ ...relayData, depositor: recipient.address }, forcedDepositId) ) ) @@ -932,7 +928,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - .speedUpV3DepositBytes32( + .speedUpDeposit( addressToBytes(depositor.address), depositId, updatedOutputAmount, @@ -975,7 +971,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool .connect(depositor) - .speedUpV3DepositBytes32( + .speedUpDeposit( addressToBytes(depositor.address), depositId, updatedOutputAmount, diff --git a/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index 0d001e215..156e64b54 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -327,11 +327,11 @@ describe("SpokePool Relayer Logic", async function () { it("fills are not paused", async function () { await spokePool.pauseFills(true); await expect( - spokePool.connect(relayer).fillV3RelayBytes32(relayData, 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("fillV3RelayBytes32", [ + const functionCalldata = spokePool.interface.encodeFunctionData("fillRelay", [ relayData, repaymentChainId, addressToBytes(relayer.address), @@ -348,24 +348,18 @@ describe("SpokePool Relayer Logic", async function () { exclusivityDeadline: relayData.fillDeadline, }; await expect( - spokePool.connect(relayer).fillV3RelayBytes32(_relayData, 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) - .fillV3RelayBytes32( - { ..._relayData, exclusivityDeadline: 0 }, - 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).fillV3RelayBytes32(relayData, repaymentChainId, addressToBytes(relayer.address)) - ) + await expect(spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address))) .to.emit(spokePool, "FilledV3Relay") .withArgs( addressToBytes(relayData.inputToken), @@ -699,9 +693,7 @@ describe("SpokePool Relayer Logic", async function () { ).to.not.be.reverted; }); it("cannot send updated fill after original fill", async function () { - await spokePool - .connect(relayer) - .fillV3RelayBytes32(relayData, repaymentChainId, addressToBytes(relayer.address)); + await spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool .connect(relayer) @@ -729,7 +721,7 @@ describe("SpokePool Relayer Logic", async function () { signature ); await expect( - spokePool.connect(relayer).fillV3RelayBytes32(relayData, 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 ee6ea31f6..f7a98607e 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -91,9 +91,7 @@ describe("SpokePool Slow Relay Logic", async function () { ); // Can fast fill after: - await spokePool - .connect(relayer) - .fillV3RelayBytes32(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); @@ -177,7 +175,7 @@ describe("SpokePool Slow Relay Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayBytes32(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 () { @@ -187,7 +185,7 @@ describe("SpokePool Slow Relay Logic", async function () { // Fill before executing slow fill await spokePool .connect(relayer) - .fillV3RelayBytes32(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); + .fillRelay(slowRelayLeaf.relayData, consts.repaymentChainId, addressToBytes(relayer.address)); await expect( spokePool.connect(relayer).executeV3SlowRelayLeaf( slowRelayLeaf, diff --git a/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts b/test/evm/hardhat/chain-specific-spokepools/Polygon_SpokePool.ts index f76f72fbe..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("fillV3RelayBytes32", [ + polygonSpokePool.interface.encodeFunctionData("fillRelay", [ relayData, repaymentChainId, addressToBytes(relayer.address), ]), - polygonSpokePool.interface.encodeFunctionData("fillV3RelayBytes32", [ + polygonSpokePool.interface.encodeFunctionData("fillRelay", [ { ...relayData, depositId: 1 }, repaymentChainId, addressToBytes(relayer.address), From 460abc9ef3f6315948fc631bfd858bcb1f70f506 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 12:28:14 -0500 Subject: [PATCH 12/24] WIP Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index ca6c024e1..ff2f2be29 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -389,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_5962092001( + function depositDeprecated_5947912356( address recipient, address originToken, uint256 amount, From 54a9289e8850ca5fee65d362ebff18a723f5aacc Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 12:30:29 -0500 Subject: [PATCH 13/24] WIP Signed-off-by: Matt Rice --- contracts/interfaces/SpokePoolInterface.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/interfaces/SpokePoolInterface.sol b/contracts/interfaces/SpokePoolInterface.sol index fc26624d4..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_5962092001( + function depositDeprecated_5947912356( address recipient, address originToken, uint256 amount, From db8f29841bfa59a4eaf683d2445b58eb5477def7 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 15:01:52 -0500 Subject: [PATCH 14/24] WIP Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 8 +-- contracts/interfaces/V3SpokePoolInterface.sol | 71 +++++++++++++++++-- 2 files changed, 71 insertions(+), 8 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index ff2f2be29..acd9c1a88 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -848,7 +848,7 @@ abstract contract SpokePool is // 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, @@ -902,7 +902,7 @@ abstract contract SpokePool is // 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(), @@ -1106,7 +1106,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, @@ -1385,7 +1385,7 @@ abstract contract SpokePool is ); } - emit V3FundsDeposited( + emit FundsDeposited( params.inputToken, params.outputToken, params.inputAmount, diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index d7daf76c3..643055169 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -130,7 +130,7 @@ interface V3SpokePoolInterface { * EVENTS * **************************************/ - event V3FundsDeposited( + event FundsDeposited( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -146,7 +146,7 @@ interface V3SpokePoolInterface { bytes message ); - event RequestedSpeedUpV3Deposit( + event RequestedSpeedUpDeposit( uint256 updatedOutputAmount, uint256 indexed depositId, bytes32 indexed depositor, @@ -155,7 +155,7 @@ interface V3SpokePoolInterface { bytes depositorSignature ); - event FilledV3Relay( + event FilledRelay( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -173,7 +173,7 @@ interface V3SpokePoolInterface { V3RelayExecutionEventInfo relayExecutionInfo ); - event RequestedV3SlowFill( + event RequestedSlowFill( bytes32 inputToken, bytes32 outputToken, uint256 inputAmount, @@ -340,4 +340,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 + ); } From d134e818f6a6028fb2cf2e19b291f5d4a6dc7919 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 15:45:24 -0500 Subject: [PATCH 15/24] update event names Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 20 +++++------ contracts/interfaces/V3SpokePoolInterface.sol | 2 +- programs/svm-spoke/src/event.rs | 6 ++-- .../svm-spoke/src/instructions/deposit.rs | 4 +-- programs/svm-spoke/src/instructions/fill.rs | 4 +-- .../svm-spoke/src/instructions/slow_fill.rs | 8 ++--- programs/svm-spoke/src/lib.rs | 2 +- .../svm-spoke/src/utils/merkle_proof_utils.rs | 2 +- test/evm/hardhat/SpokePool.Deposit.ts | 34 +++++++++---------- test/evm/hardhat/SpokePool.Relay.ts | 14 ++++---- test/evm/hardhat/SpokePool.SlowRelay.ts | 6 ++-- test/svm/SvmSpoke.Deposit.ts | 2 +- test/svm/SvmSpoke.Fill.ts | 10 +++--- test/svm/SvmSpoke.SlowFill.AcrossPlus.ts | 2 +- test/svm/SvmSpoke.SlowFill.ts | 14 ++++---- 15 files changed, 65 insertions(+), 65 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index acd9c1a88..2ed4e969d 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -61,7 +61,7 @@ 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. @@ -377,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. @@ -623,7 +623,7 @@ abstract contract SpokePool is * 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. @@ -812,7 +812,7 @@ 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(). * @param depositor Depositor that must sign the depositorSignature and was the original depositor. @@ -864,7 +864,7 @@ abstract contract SpokePool is * 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 * `address`-based implementations. @@ -932,7 +932,7 @@ abstract contract SpokePool is * 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 @@ -1443,7 +1443,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 @@ -1685,7 +1685,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 @@ -1699,7 +1699,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(); @@ -1707,7 +1707,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/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index 643055169..e7934ff03 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -99,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 { diff --git a/programs/svm-spoke/src/event.rs b/programs/svm-spoke/src/event.rs index b7bab22c7..866e606d1 100644 --- a/programs/svm-spoke/src/event.rs +++ b/programs/svm-spoke/src/event.rs @@ -37,7 +37,7 @@ pub struct EmergencyDeletedRootBundle { // Deposit events #[event] -pub struct V3FundsDeposited { +pub struct FundsDeposited { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, @@ -70,7 +70,7 @@ pub struct V3RelayExecutionEventInfo { } #[event] -pub struct FilledV3Relay { +pub struct FilledRelay { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, @@ -90,7 +90,7 @@ pub struct FilledV3Relay { // Slow fill events #[event] -pub struct RequestedV3SlowFill { +pub struct RequestedSlowFill { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, diff --git a/programs/svm-spoke/src/instructions/deposit.rs b/programs/svm-spoke/src/instructions/deposit.rs index da118d2b1..8ffd0d000 100644 --- a/programs/svm-spoke/src/instructions/deposit.rs +++ b/programs/svm-spoke/src/instructions/deposit.rs @@ -4,7 +4,7 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use crate::{ constants::{MAX_EXCLUSIVITY_PERIOD_SECONDS, ZERO_DEPOSIT_ID}, error::{CommonError, SvmError}, - event::V3FundsDeposited, + event::FundsDeposited, state::{Route, State}, utils::{get_current_time, get_unsafe_deposit_id, transfer_from}, }; @@ -120,7 +120,7 @@ pub fn _deposit_v3( applied_deposit_id[..4].copy_from_slice(&state.number_of_deposits.to_le_bytes()); } - emit_cpi!(V3FundsDeposited { + emit_cpi!(FundsDeposited { input_token, output_token, input_amount, diff --git a/programs/svm-spoke/src/instructions/fill.rs b/programs/svm-spoke/src/instructions/fill.rs index 720c2ccf3..6d0f66613 100644 --- a/programs/svm-spoke/src/instructions/fill.rs +++ b/programs/svm-spoke/src/instructions/fill.rs @@ -9,7 +9,7 @@ use crate::{ constants::DISCRIMINATOR_SIZE, constraints::is_relay_hash_valid, error::{CommonError, SvmError}, - event::{FillType, FilledV3Relay, V3RelayExecutionEventInfo}, + event::{FillType, FilledRelay, V3RelayExecutionEventInfo}, state::{FillStatus, FillStatusAccount, FillV3RelayParams, State}, utils::{get_current_time, hash_non_empty_message, invoke_handler, transfer_from}, }; @@ -145,7 +145,7 @@ pub fn fill_v3_relay<'info>( // Empty message is not hashed and emits zeroed bytes32 for easier human observability. let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(FilledV3Relay { + emit_cpi!(FilledRelay { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, diff --git a/programs/svm-spoke/src/instructions/slow_fill.rs b/programs/svm-spoke/src/instructions/slow_fill.rs index a1f970c69..7fbed9083 100644 --- a/programs/svm-spoke/src/instructions/slow_fill.rs +++ b/programs/svm-spoke/src/instructions/slow_fill.rs @@ -1,7 +1,7 @@ use anchor_lang::{prelude::*, solana_program::keccak}; use anchor_spl::token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked}; -use crate::event::{FillType, FilledV3Relay, RequestedV3SlowFill, V3RelayExecutionEventInfo}; +use crate::event::{FillType, FilledRelay, RequestedSlowFill, V3RelayExecutionEventInfo}; use crate::{ common::V3RelayData, constants::DISCRIMINATOR_SIZE, @@ -70,10 +70,10 @@ pub fn request_v3_slow_fill(ctx: Context, relay_data: Option< fill_status_account.relayer = ctx.accounts.signer.key(); fill_status_account.fill_deadline = relay_data.fill_deadline; - // Emit the RequestedV3SlowFill event. Empty message is not hashed and emits zeroed bytes32 for easier observability + // Emit the RequestedSlowFill event. Empty message is not hashed and emits zeroed bytes32 for easier observability let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(RequestedV3SlowFill { + emit_cpi!(RequestedSlowFill { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, @@ -265,7 +265,7 @@ pub fn execute_v3_slow_relay_leaf<'info>( // Empty message is not hashed and emits zeroed bytes32 for easier human observability. let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(FilledV3Relay { + emit_cpi!(FilledRelay { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, diff --git a/programs/svm-spoke/src/lib.rs b/programs/svm-spoke/src/lib.rs index eea54150d..de5f4a3f0 100644 --- a/programs/svm-spoke/src/lib.rs +++ b/programs/svm-spoke/src/lib.rs @@ -398,7 +398,7 @@ pub mod svm_spoke { /// - _relay_hash: The hash identifying the deposit to be filled. Caller must pass this in. Computed as hash of /// the flattened relay_data & destination_chain_id. /// - relay_data: 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. /// - recipient: The account receiving funds on this chain. /// - input_token: The token pulled from the caller's account to initiate the deposit. The equivalent of this diff --git a/programs/svm-spoke/src/utils/merkle_proof_utils.rs b/programs/svm-spoke/src/utils/merkle_proof_utils.rs index 0077e9e3c..bbb4bb9d8 100644 --- a/programs/svm-spoke/src/utils/merkle_proof_utils.rs +++ b/programs/svm-spoke/src/utils/merkle_proof_utils.rs @@ -6,7 +6,7 @@ pub fn get_v3_relay_hash(relay_data: &V3RelayData, chain_id: u64) -> [u8; 32] { let mut input = relay_data.try_to_vec().unwrap(); // We have serialized the original V3RelayData struct above, but we need to replace the message field with its hash, - // so that relay hash can be reconstructed only from FilledV3Relay event that does not contain the original message + // so that relay hash can be reconstructed only from FilledRelay event that does not contain the original message // but its hash. Trimming off the serialized message (4 bytes length + message bytes) and appending its hash turns // out to be less compute intensive than serializing individual struct fields. input.truncate(input.len() - 4 - relay_data.message.len()); diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 8d5a0fd1c..795665010 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -88,7 +88,7 @@ describe("SpokePool Depositor Logic", async function () { }) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(erc20.address), addressToBytes(ZERO_ADDRESS), @@ -128,7 +128,7 @@ describe("SpokePool Depositor Logic", async function () { }) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(erc20.address), addressToBytes(ZERO_ADDRESS), @@ -223,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 () { @@ -253,7 +253,7 @@ 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); @@ -282,7 +282,7 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); }); it("Relayer fee is invalid", async function () { @@ -342,7 +342,7 @@ describe("SpokePool Depositor Logic", async function () { quoteTimestamp: quoteTimestamp - offset, }) ) - ).to.emit(spokePool, "V3FundsDeposited"); + ).to.emit(spokePool, "FundsDeposited"); } }); @@ -549,7 +549,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -584,7 +584,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -619,7 +619,7 @@ describe("SpokePool Depositor Logic", async function () { ) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -701,7 +701,7 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -740,7 +740,7 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -758,9 +758,9 @@ describe("SpokePool Depositor Logic", async function () { relayData.message ); }); - it("emits V3FundsDeposited event with correct deposit ID", async function () { + it("emits FundsDeposited event with correct deposit ID", async function () { await expect(spokePool.connect(depositor).deposit(...depositArgs)) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -788,7 +788,7 @@ describe("SpokePool Depositor Logic", async function () { await expect( spokePool.connect(depositor).deposit(...getDepositArgsFromRelayData({ ...relayData, depositor: newDepositor })) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -838,7 +838,7 @@ describe("SpokePool Depositor Logic", async function () { ...getUnsafeDepositArgsFromRelayData({ ...relayData, depositor: recipient.address }, forcedDepositId) ) ) - .to.emit(spokePool, "V3FundsDeposited") + .to.emit(spokePool, "FundsDeposited") .withArgs( relayData.inputToken, relayData.outputToken, @@ -937,7 +937,7 @@ describe("SpokePool Depositor Logic", async function () { expectedSignature ) ) - .to.emit(spokePool, "RequestedSpeedUpV3Deposit") + .to.emit(spokePool, "RequestedSpeedUpDeposit") .withArgs( updatedOutputAmount, depositId, @@ -1020,7 +1020,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 156e64b54..94d7abe10 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -124,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), @@ -160,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), @@ -195,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), @@ -360,7 +360,7 @@ describe("SpokePool Relayer Logic", async function () { }); it("calls _fillRelayV3 with expected params", async function () { await expect(spokePool.connect(relayer).fillRelay(relayData, repaymentChainId, addressToBytes(relayer.address))) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -395,7 +395,7 @@ describe("SpokePool Relayer Logic", async function () { outputToken: bytes32ToAddress(relayData.outputToken), }; await expect(spokePool.connect(relayer).fillV3Relay(legacyRelayData, repaymentChainId)) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), @@ -452,7 +452,7 @@ describe("SpokePool Relayer Logic", async function () { updatedMessage, signature ) - ).to.emit(spokePool, "FilledV3Relay"); + ).to.emit(spokePool, "FilledRelay"); }); it("must be exclusive relayer before exclusivity deadline", async function () { const _relayData = { @@ -510,7 +510,7 @@ describe("SpokePool Relayer Logic", async function () { signature ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), diff --git a/test/evm/hardhat/SpokePool.SlowRelay.ts b/test/evm/hardhat/SpokePool.SlowRelay.ts index f7a98607e..ff848d484 100644 --- a/test/evm/hardhat/SpokePool.SlowRelay.ts +++ b/test/evm/hardhat/SpokePool.SlowRelay.ts @@ -66,7 +66,7 @@ describe("SpokePool Slow Relay Logic", async function () { await spokePool.setCurrentTime(relayData.exclusivityDeadline - 1); await expect(spokePool.connect(relayer).requestV3SlowFill({ ...relayData, exclusivityDeadline: 0 })).to.emit( spokePool, - "RequestedV3SlowFill" + "RequestedSlowFill" ); }); it("during exclusivity deadline", async function () { @@ -80,7 +80,7 @@ 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).requestV3SlowFill(relayData)).to.emit(spokePool, "RequestedSlowFill"); // FillStatus gets reset to RequestedSlowFill: expect(await spokePool.fillStatuses(relayHash)).to.equal(FillStatus.RequestedSlowFill); @@ -295,7 +295,7 @@ describe("SpokePool Slow Relay Logic", async function () { tree.getHexProof(slowRelayLeaf) ) ) - .to.emit(spokePool, "FilledV3Relay") + .to.emit(spokePool, "FilledRelay") .withArgs( addressToBytes(relayData.inputToken), addressToBytes(relayData.outputToken), diff --git a/test/svm/SvmSpoke.Deposit.ts b/test/svm/SvmSpoke.Deposit.ts index 009a98106..70a97e478 100644 --- a/test/svm/SvmSpoke.Deposit.ts +++ b/test/svm/SvmSpoke.Deposit.ts @@ -180,7 +180,7 @@ describe("svm_spoke.deposit", () => { ); }); - it("Verifies V3FundsDeposited after deposits", async () => { + it("Verifies FundsDeposited after deposits", async () => { depositData.inputAmount = depositData.inputAmount.add(new BN(69)); // Execute the first deposit_v3 call diff --git a/test/svm/SvmSpoke.Fill.ts b/test/svm/SvmSpoke.Fill.ts index a556796bb..9461171a8 100644 --- a/test/svm/SvmSpoke.Fill.ts +++ b/test/svm/SvmSpoke.Fill.ts @@ -177,14 +177,14 @@ describe("svm_spoke.fill", () => { assertSE(recipientAccount.amount, relayAmount, "Recipient's balance should be increased by the relay amount"); }); - it("Verifies FilledV3Relay event after filling a relay", async () => { + it("Verifies FilledRelay event after filling a relay", async () => { const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId)); const tx = await approvedFillV3Relay([relayHash, relayData, new BN(420), otherRelayer.publicKey]); - // Fetch and verify the FilledV3Relay event + // Fetch and verify the FilledRelay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledV3Relay event should be emitted"); + assert.isNotNull(event, "FilledRelay event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -658,10 +658,10 @@ describe("svm_spoke.fill", () => { const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId)); const tx = await approvedFillV3Relay([relayHash, relayData, new BN(420), otherRelayer.publicKey]); - // Fetch and verify the FilledV3Relay event + // Fetch and verify the FilledRelay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledV3Relay event should be emitted"); + assert.isNotNull(event, "FilledRelay event should be emitted"); // Verify that the event data has zeroed message hash. assertSE(event.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); diff --git a/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts b/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts index ebcc860dc..67a68c14b 100644 --- a/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts +++ b/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts @@ -459,7 +459,7 @@ describe("svm_spoke.slow_fill.across_plus", () => { // being able to close the fill status PDA using only event data. const events = await readEventsUntilFound(connection, txSignature, [program]); const eventData = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(eventData, "FilledV3Relay event should be emitted"); + assert.isNotNull(eventData, "FilledRelay event should be emitted"); // Recover relay hash and derived fill status from event data. const relayHashUint8Array = calculateRelayEventHashUint8Array(eventData, chainId); diff --git a/test/svm/SvmSpoke.SlowFill.ts b/test/svm/SvmSpoke.SlowFill.ts index 7f8b90a72..14701018f 100644 --- a/test/svm/SvmSpoke.SlowFill.ts +++ b/test/svm/SvmSpoke.SlowFill.ts @@ -203,10 +203,10 @@ describe("svm_spoke.slow_fill", () => { .signers([relayer]) .rpc(); - // Fetch and verify the RequestedV3SlowFill event + // Fetch and verify the RequestedSlowFill event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "requestedV3SlowFill")?.data; - assert.isNotNull(event, "RequestedV3SlowFill event should be emitted"); + assert.isNotNull(event, "RequestedSlowFill event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -375,10 +375,10 @@ describe("svm_spoke.slow_fill", () => { "Recipient balance should be increased by relay amount" ); - // Fetch and verify the FilledV3Relay event + // Fetch and verify the FilledRelay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledV3Relay event should be emitted"); + assert.isNotNull(event, "FilledRelay event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -661,15 +661,15 @@ describe("svm_spoke.slow_fill", () => { .remainingAccounts(fillRemainingAccounts) .rpc(); - // Fetch and verify message hash in the RequestedV3SlowFill and FilledV3Relay events + // Fetch and verify message hash in the RequestedSlowFill and FilledRelay events const requestEvents = await readEventsUntilFound(connection, tx1, [program]); const requestEvent = requestEvents.find((event) => event.name === "requestedV3SlowFill")?.data; - assert.isNotNull(requestEvent, "RequestedV3SlowFill event should be emitted"); + assert.isNotNull(requestEvent, "RequestedSlowFill event should be emitted"); assertSE(requestEvent.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); const fillEvents = await readEventsUntilFound(connection, tx2, [program]); const fillEvent = fillEvents.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(fillEvent, "FilledV3Relay event should be emitted"); + assert.isNotNull(fillEvent, "FilledRelay event should be emitted"); assertSE(fillEvent.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); assertSE( fillEvent.relayExecutionInfo.updatedMessageHash, From 5b232453353c09bb7e37399fed8299dffaa91f91 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 17:47:23 -0500 Subject: [PATCH 16/24] fix tests Signed-off-by: Matt Rice --- test/evm/hardhat/SpokePool.Deposit.ts | 38 +++++++++++++-------------- 1 file changed, 18 insertions(+), 20 deletions(-) diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 795665010..007ff2056 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -63,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, @@ -77,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, @@ -150,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, @@ -163,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, @@ -183,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, @@ -201,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, @@ -210,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, @@ -231,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, @@ -244,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, @@ -258,7 +258,7 @@ describe("SpokePool Depositor Logic", async function () { // 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, @@ -273,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, @@ -289,7 +289,7 @@ describe("SpokePool Depositor Logic", async function () { const revertReason = "InvalidRelayerFee"; await expect( - spokePool.connect(depositor).deposit( + spokePool.connect(depositor).depositDeprecated_5947912356( ...getDepositParams({ originToken: erc20.address, amount, @@ -306,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, @@ -318,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, @@ -333,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, @@ -812,10 +812,8 @@ describe("SpokePool Depositor Logic", async function () { await expect(spokePool.connect(depositor).deposit(...depositArgs)).to.be.revertedWith("DepositsArePaused"); }); it("reentrancy protected", async function () { - const functionCalldata = spokePool.interface.encodeFunctionData("depositV3Bytes32", [...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}. From fe738af801b802a3688c12392c54d6aee8139277 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 18:00:18 -0500 Subject: [PATCH 17/24] update function Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 15 ++------------- foundry.toml | 6 +++--- 2 files changed, 5 insertions(+), 16 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 2ed4e969d..a045e2d8a 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -960,7 +960,7 @@ abstract contract SpokePool is * passed. Will receive inputAmount of the equivalent token to inputToken on the repayment chain. */ function fillRelay( - V3RelayData calldata relayData, + V3RelayData memory relayData, uint256 repaymentChainId, bytes32 repaymentAddress ) public override nonReentrant unpausedFills { @@ -1004,18 +1004,7 @@ abstract contract SpokePool is message: relayData.message }); - // Call the existing fillV3Relay function - (bool success, bytes memory data) = address(this).delegatecall( - abi.encodeWithSignature( - "fillRelay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", - convertedRelayData, - repaymentChainId, - msg.sender.toBytes32() - ) - ); - if (!success) { - revert LowLevelCallFailed(data); - } + fillRelay(convertedRelayData, repaymentChainId, msg.sender.toBytes32()); } /** diff --git a/foundry.toml b/foundry.toml index 3f6b9437b..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.28" +optimizer_runs = 800 +solc_version = "0.8.23" revert_strings = "strip" -solc = "0.8.28" +solc = "0.8.23" evm_version = "shanghai" [rpc_endpoints] From 788682d025e834fef5ed609c75820e857fa9cb24 Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 18:10:28 -0500 Subject: [PATCH 18/24] update naming Signed-off-by: Matt Rice --- contracts/SpokePool.sol | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index a045e2d8a..668bdf8be 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -1137,7 +1137,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.encode( - "fillV3Relay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", + "fillRelay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", relayData, destinationFillerData.repaymentChainId, msg.sender.toBytes32() From 29e28d5be44516f41f0132364be24250afd1148a Mon Sep 17 00:00:00 2001 From: Matt Rice Date: Thu, 9 Jan 2025 18:46:13 -0500 Subject: [PATCH 19/24] drop unintended svm changes Signed-off-by: Matt Rice --- programs/svm-spoke/src/event.rs | 6 +++--- programs/svm-spoke/src/instructions/deposit.rs | 4 ++-- programs/svm-spoke/src/instructions/fill.rs | 4 ++-- programs/svm-spoke/src/instructions/slow_fill.rs | 8 ++++---- programs/svm-spoke/src/lib.rs | 2 +- programs/svm-spoke/src/utils/merkle_proof_utils.rs | 2 +- test/svm/SvmSpoke.Deposit.ts | 2 +- test/svm/SvmSpoke.Fill.ts | 10 +++++----- test/svm/SvmSpoke.SlowFill.AcrossPlus.ts | 2 +- test/svm/SvmSpoke.SlowFill.ts | 14 +++++++------- 10 files changed, 27 insertions(+), 27 deletions(-) diff --git a/programs/svm-spoke/src/event.rs b/programs/svm-spoke/src/event.rs index 866e606d1..b7bab22c7 100644 --- a/programs/svm-spoke/src/event.rs +++ b/programs/svm-spoke/src/event.rs @@ -37,7 +37,7 @@ pub struct EmergencyDeletedRootBundle { // Deposit events #[event] -pub struct FundsDeposited { +pub struct V3FundsDeposited { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, @@ -70,7 +70,7 @@ pub struct V3RelayExecutionEventInfo { } #[event] -pub struct FilledRelay { +pub struct FilledV3Relay { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, @@ -90,7 +90,7 @@ pub struct FilledRelay { // Slow fill events #[event] -pub struct RequestedSlowFill { +pub struct RequestedV3SlowFill { pub input_token: Pubkey, pub output_token: Pubkey, pub input_amount: u64, diff --git a/programs/svm-spoke/src/instructions/deposit.rs b/programs/svm-spoke/src/instructions/deposit.rs index 8ffd0d000..da118d2b1 100644 --- a/programs/svm-spoke/src/instructions/deposit.rs +++ b/programs/svm-spoke/src/instructions/deposit.rs @@ -4,7 +4,7 @@ use anchor_spl::token_interface::{Mint, TokenAccount, TokenInterface}; use crate::{ constants::{MAX_EXCLUSIVITY_PERIOD_SECONDS, ZERO_DEPOSIT_ID}, error::{CommonError, SvmError}, - event::FundsDeposited, + event::V3FundsDeposited, state::{Route, State}, utils::{get_current_time, get_unsafe_deposit_id, transfer_from}, }; @@ -120,7 +120,7 @@ pub fn _deposit_v3( applied_deposit_id[..4].copy_from_slice(&state.number_of_deposits.to_le_bytes()); } - emit_cpi!(FundsDeposited { + emit_cpi!(V3FundsDeposited { input_token, output_token, input_amount, diff --git a/programs/svm-spoke/src/instructions/fill.rs b/programs/svm-spoke/src/instructions/fill.rs index 6d0f66613..720c2ccf3 100644 --- a/programs/svm-spoke/src/instructions/fill.rs +++ b/programs/svm-spoke/src/instructions/fill.rs @@ -9,7 +9,7 @@ use crate::{ constants::DISCRIMINATOR_SIZE, constraints::is_relay_hash_valid, error::{CommonError, SvmError}, - event::{FillType, FilledRelay, V3RelayExecutionEventInfo}, + event::{FillType, FilledV3Relay, V3RelayExecutionEventInfo}, state::{FillStatus, FillStatusAccount, FillV3RelayParams, State}, utils::{get_current_time, hash_non_empty_message, invoke_handler, transfer_from}, }; @@ -145,7 +145,7 @@ pub fn fill_v3_relay<'info>( // Empty message is not hashed and emits zeroed bytes32 for easier human observability. let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(FilledRelay { + emit_cpi!(FilledV3Relay { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, diff --git a/programs/svm-spoke/src/instructions/slow_fill.rs b/programs/svm-spoke/src/instructions/slow_fill.rs index 7fbed9083..a1f970c69 100644 --- a/programs/svm-spoke/src/instructions/slow_fill.rs +++ b/programs/svm-spoke/src/instructions/slow_fill.rs @@ -1,7 +1,7 @@ use anchor_lang::{prelude::*, solana_program::keccak}; use anchor_spl::token_interface::{transfer_checked, Mint, TokenAccount, TokenInterface, TransferChecked}; -use crate::event::{FillType, FilledRelay, RequestedSlowFill, V3RelayExecutionEventInfo}; +use crate::event::{FillType, FilledV3Relay, RequestedV3SlowFill, V3RelayExecutionEventInfo}; use crate::{ common::V3RelayData, constants::DISCRIMINATOR_SIZE, @@ -70,10 +70,10 @@ pub fn request_v3_slow_fill(ctx: Context, relay_data: Option< fill_status_account.relayer = ctx.accounts.signer.key(); fill_status_account.fill_deadline = relay_data.fill_deadline; - // Emit the RequestedSlowFill event. Empty message is not hashed and emits zeroed bytes32 for easier observability + // Emit the RequestedV3SlowFill event. Empty message is not hashed and emits zeroed bytes32 for easier observability let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(RequestedSlowFill { + emit_cpi!(RequestedV3SlowFill { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, @@ -265,7 +265,7 @@ pub fn execute_v3_slow_relay_leaf<'info>( // Empty message is not hashed and emits zeroed bytes32 for easier human observability. let message_hash = hash_non_empty_message(&relay_data.message); - emit_cpi!(FilledRelay { + emit_cpi!(FilledV3Relay { input_token: relay_data.input_token, output_token: relay_data.output_token, input_amount: relay_data.input_amount, diff --git a/programs/svm-spoke/src/lib.rs b/programs/svm-spoke/src/lib.rs index de5f4a3f0..eea54150d 100644 --- a/programs/svm-spoke/src/lib.rs +++ b/programs/svm-spoke/src/lib.rs @@ -398,7 +398,7 @@ pub mod svm_spoke { /// - _relay_hash: The hash identifying the deposit to be filled. Caller must pass this in. Computed as hash of /// the flattened relay_data & destination_chain_id. /// - relay_data: 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 FundsDeposited event. + /// all the same-named parameters emitted in the origin chain V3FundsDeposited event. /// - depositor: The account credited with the deposit. /// - recipient: The account receiving funds on this chain. /// - input_token: The token pulled from the caller's account to initiate the deposit. The equivalent of this diff --git a/programs/svm-spoke/src/utils/merkle_proof_utils.rs b/programs/svm-spoke/src/utils/merkle_proof_utils.rs index bbb4bb9d8..0077e9e3c 100644 --- a/programs/svm-spoke/src/utils/merkle_proof_utils.rs +++ b/programs/svm-spoke/src/utils/merkle_proof_utils.rs @@ -6,7 +6,7 @@ pub fn get_v3_relay_hash(relay_data: &V3RelayData, chain_id: u64) -> [u8; 32] { let mut input = relay_data.try_to_vec().unwrap(); // We have serialized the original V3RelayData struct above, but we need to replace the message field with its hash, - // so that relay hash can be reconstructed only from FilledRelay event that does not contain the original message + // so that relay hash can be reconstructed only from FilledV3Relay event that does not contain the original message // but its hash. Trimming off the serialized message (4 bytes length + message bytes) and appending its hash turns // out to be less compute intensive than serializing individual struct fields. input.truncate(input.len() - 4 - relay_data.message.len()); diff --git a/test/svm/SvmSpoke.Deposit.ts b/test/svm/SvmSpoke.Deposit.ts index 70a97e478..009a98106 100644 --- a/test/svm/SvmSpoke.Deposit.ts +++ b/test/svm/SvmSpoke.Deposit.ts @@ -180,7 +180,7 @@ describe("svm_spoke.deposit", () => { ); }); - it("Verifies FundsDeposited after deposits", async () => { + it("Verifies V3FundsDeposited after deposits", async () => { depositData.inputAmount = depositData.inputAmount.add(new BN(69)); // Execute the first deposit_v3 call diff --git a/test/svm/SvmSpoke.Fill.ts b/test/svm/SvmSpoke.Fill.ts index 9461171a8..a556796bb 100644 --- a/test/svm/SvmSpoke.Fill.ts +++ b/test/svm/SvmSpoke.Fill.ts @@ -177,14 +177,14 @@ describe("svm_spoke.fill", () => { assertSE(recipientAccount.amount, relayAmount, "Recipient's balance should be increased by the relay amount"); }); - it("Verifies FilledRelay event after filling a relay", async () => { + it("Verifies FilledV3Relay event after filling a relay", async () => { const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId)); const tx = await approvedFillV3Relay([relayHash, relayData, new BN(420), otherRelayer.publicKey]); - // Fetch and verify the FilledRelay event + // Fetch and verify the FilledV3Relay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledRelay event should be emitted"); + assert.isNotNull(event, "FilledV3Relay event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -658,10 +658,10 @@ describe("svm_spoke.fill", () => { const relayHash = Array.from(calculateRelayHashUint8Array(relayData, chainId)); const tx = await approvedFillV3Relay([relayHash, relayData, new BN(420), otherRelayer.publicKey]); - // Fetch and verify the FilledRelay event + // Fetch and verify the FilledV3Relay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledRelay event should be emitted"); + assert.isNotNull(event, "FilledV3Relay event should be emitted"); // Verify that the event data has zeroed message hash. assertSE(event.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); diff --git a/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts b/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts index 67a68c14b..ebcc860dc 100644 --- a/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts +++ b/test/svm/SvmSpoke.SlowFill.AcrossPlus.ts @@ -459,7 +459,7 @@ describe("svm_spoke.slow_fill.across_plus", () => { // being able to close the fill status PDA using only event data. const events = await readEventsUntilFound(connection, txSignature, [program]); const eventData = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(eventData, "FilledRelay event should be emitted"); + assert.isNotNull(eventData, "FilledV3Relay event should be emitted"); // Recover relay hash and derived fill status from event data. const relayHashUint8Array = calculateRelayEventHashUint8Array(eventData, chainId); diff --git a/test/svm/SvmSpoke.SlowFill.ts b/test/svm/SvmSpoke.SlowFill.ts index 14701018f..7f8b90a72 100644 --- a/test/svm/SvmSpoke.SlowFill.ts +++ b/test/svm/SvmSpoke.SlowFill.ts @@ -203,10 +203,10 @@ describe("svm_spoke.slow_fill", () => { .signers([relayer]) .rpc(); - // Fetch and verify the RequestedSlowFill event + // Fetch and verify the RequestedV3SlowFill event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "requestedV3SlowFill")?.data; - assert.isNotNull(event, "RequestedSlowFill event should be emitted"); + assert.isNotNull(event, "RequestedV3SlowFill event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -375,10 +375,10 @@ describe("svm_spoke.slow_fill", () => { "Recipient balance should be increased by relay amount" ); - // Fetch and verify the FilledRelay event + // Fetch and verify the FilledV3Relay event const events = await readEventsUntilFound(connection, tx, [program]); const event = events.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(event, "FilledRelay event should be emitted"); + assert.isNotNull(event, "FilledV3Relay event should be emitted"); // Verify that the event data matches the relay data. Object.entries(relayData).forEach(([key, value]) => { @@ -661,15 +661,15 @@ describe("svm_spoke.slow_fill", () => { .remainingAccounts(fillRemainingAccounts) .rpc(); - // Fetch and verify message hash in the RequestedSlowFill and FilledRelay events + // Fetch and verify message hash in the RequestedV3SlowFill and FilledV3Relay events const requestEvents = await readEventsUntilFound(connection, tx1, [program]); const requestEvent = requestEvents.find((event) => event.name === "requestedV3SlowFill")?.data; - assert.isNotNull(requestEvent, "RequestedSlowFill event should be emitted"); + assert.isNotNull(requestEvent, "RequestedV3SlowFill event should be emitted"); assertSE(requestEvent.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); const fillEvents = await readEventsUntilFound(connection, tx2, [program]); const fillEvent = fillEvents.find((event) => event.name === "filledV3Relay")?.data; - assert.isNotNull(fillEvent, "FilledRelay event should be emitted"); + assert.isNotNull(fillEvent, "FilledV3Relay event should be emitted"); assertSE(fillEvent.messageHash, new Uint8Array(32), `MessageHash should be zeroed`); assertSE( fillEvent.relayExecutionInfo.updatedMessageHash, From 32731e7308dee50b4b5163c6a17cd13017bdb5f6 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Fri, 10 Jan 2025 15:49:26 +0100 Subject: [PATCH 20/24] WIP Signed-off-by: Chris Maree --- .../local/SpokePoolDeprecatedMethods.t.sol | 130 ++++++++++++++++++ 1 file changed, 130 insertions(+) create mode 100644 test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol diff --git a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol new file mode 100644 index 000000000..b664529a1 --- /dev/null +++ b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol @@ -0,0 +1,130 @@ +// 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(); + } + + 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 + ); + + // 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(); + } + + 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 + ); + } + + 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 + ); + } +} From a934570f7d11820879a02c7d25256bb0708498e0 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Fri, 10 Jan 2025 16:11:28 +0100 Subject: [PATCH 21/24] WIP Signed-off-by: Chris Maree --- .../local/SpokePoolDeprecatedMethods.t.sol | 13 +++++++ test/evm/hardhat/SpokePool.Deposit.ts | 38 +++++++++++++++++++ 2 files changed, 51 insertions(+) diff --git a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol index b664529a1..897fb116d 100644 --- a/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol +++ b/test/evm/foundry/local/SpokePoolDeprecatedMethods.t.sol @@ -58,6 +58,9 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { 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 { @@ -65,6 +68,7 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { // 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 @@ -76,6 +80,8 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { 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 @@ -88,10 +94,13 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { 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 @@ -107,6 +116,8 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { 0, // exclusivityParameter bytes("") // message ); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount, "SpokePool balance should increase"); } function testAddressDeposit() public { @@ -126,5 +137,7 @@ contract SpokePoolOverloadedDeprecatedMethodsTest is Test { 0, // exclusivityParameter bytes("") // message ); + + assertEq(mockWETH.balanceOf(address(spokePool)), depositAmount, "SpokePool balance should increase"); } } diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 007ff2056..73bf58f1d 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -345,6 +345,44 @@ describe("SpokePool Depositor Logic", async function () { ).to.emit(spokePool, "FundsDeposited"); } }); + it.only("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[]; From de61c0a2f06f4981a9352b0ed968acb7ae6337b9 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Fri, 10 Jan 2025 16:43:29 +0100 Subject: [PATCH 22/24] WIP Signed-off-by: Chris Maree --- test/evm/hardhat/SpokePool.Deposit.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/evm/hardhat/SpokePool.Deposit.ts b/test/evm/hardhat/SpokePool.Deposit.ts index 73bf58f1d..64cd36f8c 100644 --- a/test/evm/hardhat/SpokePool.Deposit.ts +++ b/test/evm/hardhat/SpokePool.Deposit.ts @@ -345,7 +345,7 @@ describe("SpokePool Depositor Logic", async function () { ).to.emit(spokePool, "FundsDeposited"); } }); - it.only("should call legacy deposit through overloaded interface", async function () { + 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", From ff5fcbceb1ba637f43002700e63b02167a96a399 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Thu, 16 Jan 2025 12:41:53 +0100 Subject: [PATCH 23/24] feat: extend current add-legacy-fill-method-svm-dev (#864) --- contracts/SpokePool.sol | 36 +++++++------- contracts/interfaces/V3SpokePoolInterface.sol | 6 +-- contracts/test/MockSpokePool.sol | 4 +- test/evm/hardhat/SpokePool.Relay.ts | 32 ++++++------- test/evm/hardhat/SpokePool.SlowRelay.ts | 48 +++++++++---------- 5 files changed, 61 insertions(+), 65 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 85a4df1e2..007a57cc5 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -64,7 +64,7 @@ abstract contract SpokePool 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)" ); @@ -814,7 +814,7 @@ abstract contract SpokePool is * and/or message. * @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 @@ -843,7 +843,7 @@ 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 @@ -866,7 +866,7 @@ abstract contract SpokePool is * * @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. @@ -897,7 +897,7 @@ 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 @@ -926,8 +926,8 @@ 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. @@ -951,9 +951,9 @@ 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 @@ -1026,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, @@ -1061,7 +1061,7 @@ abstract contract SpokePool is updatedRecipient, updatedMessage, depositorSignature, - UPDATE_V3_DEPOSIT_DETAILS_HASH + UPDATE_BYTES32_DEPOSIT_DETAILS_HASH ); _fillRelayV3(relayExecution, repaymentAddress, false); @@ -1075,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 @@ -1158,7 +1158,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. @@ -1171,7 +1171,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 @@ -1284,7 +1284,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. diff --git a/contracts/interfaces/V3SpokePoolInterface.sol b/contracts/interfaces/V3SpokePoolInterface.sol index 053c4e712..331fc23b4 100644 --- a/contracts/interfaces/V3SpokePoolInterface.sol +++ b/contracts/interfaces/V3SpokePoolInterface.sol @@ -299,7 +299,7 @@ interface V3SpokePoolInterface { function fillV3Relay(V3RelayDataLegacy calldata relayData, uint256 repaymentChainId) external; - function fillV3RelayWithUpdatedDeposit( + function fillRelayWithUpdatedDeposit( V3RelayData calldata relayData, uint256 repaymentChainId, bytes32 repaymentAddress, @@ -309,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 diff --git a/contracts/test/MockSpokePool.sol b/contracts/test/MockSpokePool.sol index fb23cddb1..7d6fb210c 100644 --- a/contracts/test/MockSpokePool.sol +++ b/contracts/test/MockSpokePool.sol @@ -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/test/evm/hardhat/SpokePool.Relay.ts b/test/evm/hardhat/SpokePool.Relay.ts index 94d7abe10..2d159e8e9 100644 --- a/test/evm/hardhat/SpokePool.Relay.ts +++ b/test/evm/hardhat/SpokePool.Relay.ts @@ -421,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); @@ -443,7 +443,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, exclusivityDeadline: 0 }, repaymentChainId, addressToBytes(relayer.address), @@ -464,7 +464,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( _relayData, repaymentChainId, addressToBytes(relayer.address), @@ -477,7 +477,7 @@ 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, @@ -500,7 +500,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -545,7 +545,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(relayer.address) }, repaymentChainId, addressToBytes(relayer.address), @@ -568,7 +568,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -583,7 +583,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, originChainId: relayData.originChainId + 1 }, repaymentChainId, addressToBytes(relayer.address), @@ -598,7 +598,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositId: relayData.depositId.add(toBN(1)) }, repaymentChainId, addressToBytes(relayer.address), @@ -613,7 +613,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -628,7 +628,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -643,7 +643,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -668,7 +668,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, repaymentChainId, addressToBytes(relayer.address), @@ -681,7 +681,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( { ...relayData, depositor: addressToBytes(erc1271.address) }, repaymentChainId, addressToBytes(relayer.address), @@ -697,7 +697,7 @@ describe("SpokePool Relayer Logic", async function () { await expect( spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), @@ -711,7 +711,7 @@ describe("SpokePool Relayer Logic", async function () { it("cannot send updated fill after slow fill", async function () { await spokePool .connect(relayer) - .fillV3RelayWithUpdatedDeposit( + .fillRelayWithUpdatedDeposit( relayData, repaymentChainId, addressToBytes(relayer.address), diff --git a/test/evm/hardhat/SpokePool.SlowRelay.ts b/test/evm/hardhat/SpokePool.SlowRelay.ts index ff848d484..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, "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,15 +80,13 @@ 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, "RequestedSlowFill"); + 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).fillRelay(relayData, consts.repaymentChainId, addressToBytes(relayer.address)); @@ -97,24 +95,22 @@ describe("SpokePool Slow Relay Logic", 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) @@ -187,7 +183,7 @@ describe("SpokePool Slow Relay Logic", async function () { .connect(relayer) .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,7 +285,7 @@ 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) From a28eb830cea25f7807408f0673912da07f06ffa2 Mon Sep 17 00:00:00 2001 From: Chris Maree Date: Mon, 20 Jan 2025 11:43:17 +0100 Subject: [PATCH 24/24] WIP Signed-off-by: Chris Maree --- contracts/SpokePool.sol | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 007a57cc5..888beb905 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -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. */ @@ -989,7 +989,7 @@ abstract contract SpokePool is // 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 + // Convert V3RelayDataLegacy to V3RelayData using the .toBytes32() method. V3RelayData memory convertedRelayData = V3RelayData({ depositor: relayData.depositor.toBytes32(), recipient: relayData.recipient.toBytes32(), @@ -1138,11 +1138,9 @@ 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.encode( - "fillRelay((bytes32,bytes32,bytes32,bytes32,bytes32,uint256,uint256,uint256,uint256,uint32,uint32,bytes),uint256,bytes32)", - relayData, - destinationFillerData.repaymentChainId, - msg.sender.toBytes32() + abi.encodeCall( + V3SpokePoolInterface.fillRelay, + (relayData, destinationFillerData.repaymentChainId, msg.sender.toBytes32()) ) ); if (!success) {