diff --git a/contracts/SpokePool.sol b/contracts/SpokePool.sol index 1789f3883..aa3131578 100644 --- a/contracts/SpokePool.sol +++ b/contracts/SpokePool.sol @@ -480,6 +480,42 @@ abstract contract SpokePool is ); } + /** + * @notice This is a simple wrapper for deposit() that sets the quoteTimestamp to the current SpokePool timestamp. + * @notice This function is intended for multisig depositors who can accept some LP fee uncertainty in order to lift + * the quoteTimestamp buffer constraint. + * @dev Re-orgs may produce invalid fills if the quoteTimestamp moves across a change in HubPool utilisation. + * @dev The existing function modifiers are already enforced by deposit(), so no additional modifiers are imposed. + * @param recipient Address to receive funds at on destination chain. + * @param originToken Token to lock into this contract to initiate deposit. + * @param amount Amount of tokens to deposit. Will be amount of tokens to receive less fees. + * @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer. + * @param relayerFeePct % of deposit amount taken out to incentivize a fast relayer. + * @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. + * @param maxCount used to protect the depositor from frontrunning to guarantee their quote remains valid. + */ + function depositNow( + address recipient, + address originToken, + uint256 amount, + uint256 destinationChainId, + int64 relayerFeePct, + bytes memory message, + uint256 maxCount + ) public payable { + deposit( + recipient, + originToken, + amount, + destinationChainId, + relayerFeePct, + uint32(getCurrentTime()), + message, + maxCount + ); + } + /** * @notice Convenience method that depositor can use to signal to relayer to use updated fee. * @notice Relayer should only use events emitted by this function to submit fills with updated fees, otherwise they diff --git a/contracts/interfaces/SpokePoolInterface.sol b/contracts/interfaces/SpokePoolInterface.sol index 1ba0edcb1..c7369a470 100644 --- a/contracts/interfaces/SpokePoolInterface.sol +++ b/contracts/interfaces/SpokePoolInterface.sol @@ -98,6 +98,16 @@ interface SpokePoolInterface { uint256 maxCount ) external payable; + function depositNow( + address recipient, + address originToken, + uint256 amount, + uint256 destinationChainId, + int64 relayerFeePct, + bytes memory message, + uint256 maxCount + ) external payable; + function speedUpDeposit( address depositor, int64 updatedRelayerFeePct, diff --git a/hardhat.config.ts b/hardhat.config.ts index 477427f1b..ac0ceccff 100644 --- a/hardhat.config.ts +++ b/hardhat.config.ts @@ -44,6 +44,7 @@ const config: HardhatUserConfig = { "contracts/Boba_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Optimism_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/Base_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, + "contracts/Polygon_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/test/MockSpokePoolV2.sol": LARGE_CONTRACT_COMPILER_SETTINGS, "contracts/test/MockOptimism_SpokePool.sol": LARGE_CONTRACT_COMPILER_SETTINGS, }, diff --git a/test/SpokePool.Deposit.ts b/test/SpokePool.Deposit.ts index d3bfa05c9..3a71bfbd6 100644 --- a/test/SpokePool.Deposit.ts +++ b/test/SpokePool.Deposit.ts @@ -341,6 +341,68 @@ describe("SpokePool Depositor Logic", async function () { ).to.emit(spokePool, "FundsDeposited"); }); + it("quoteTimestamp is set correctly by depositNow()", async function () { + // ERC20 deposit + await expect( + spokePool + .connect(depositor) + .depositNow( + recipient.address, + erc20.address, + amountToDeposit.toString(), + destinationChainId.toString(), + depositRelayerFeePct.toString(), + "0x", + maxUint256 + ) + ) + .to.emit(spokePool, "FundsDeposited") + .withArgs( + amountToDeposit, + destinationChainId, + destinationChainId, + depositRelayerFeePct, + 0, + currentSpokePoolTime, + erc20.address, + recipient.address, + depositor.address, + "0x" + ); + + // Native token deposit - amount != msg.value + await expect( + spokePool + .connect(depositor) + .depositNow( + recipient.address, + weth.address, + amountToDeposit.toString(), + destinationChainId.toString(), + depositRelayerFeePct.toString(), + "0x", + maxUint256, + { value: amountToDeposit.add(1) } + ) + ).to.be.revertedWith("msg.value must match amount"); + + // Native token deposit - amount == msg.value. + await expect(() => + spokePool + .connect(depositor) + .depositNow( + recipient.address, + weth.address, + amountToDeposit.toString(), + destinationChainId.toString(), + depositRelayerFeePct.toString(), + "0x", + maxUint256, + { value: amountToDeposit } + ) + ).to.changeEtherBalances([depositor, weth], [amountToDeposit.mul(toBN("-1")), amountToDeposit]); + }); + it("maxCount is too low", async function () { const revertReason = "Above max count";