Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: add permit2 entrypoints to the periphery #782

Merged
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
01f7e37
feat: add permit2 entrypoints to the periphery
bmzig Nov 29, 2024
c6b08de
Merge branch 'spokepool-periphery-multiple-exchanges' into bz/permit2…
nicholaspai Dec 1, 2024
3542114
Update test/evm/foundry/local/SpokePoolPeriphery.t.sol
nicholaspai Dec 1, 2024
6131d16
Update SpokePoolPeriphery.t.sol
nicholaspai Dec 1, 2024
4262f6e
Merge branch 'spokepool-periphery-multiple-exchanges' into bz/permit2…
nicholaspai Dec 2, 2024
0446725
move permit2 to proxy
nicholaspai Dec 2, 2024
4f3b2d6
fix permit2
bmzig Dec 2, 2024
ff13c12
wip: swap arguments refactor
bmzig Dec 3, 2024
7b6bd44
implement isValidSignature
bmzig Dec 3, 2024
aa31f5b
1271
bmzig Dec 3, 2024
93b90cd
simplify isValidSignature
bmzig Dec 3, 2024
9eda8c3
rebase
nicholaspai Dec 4, 2024
a9f019d
Merge branch 'spokepool-periphery-multiple-exchanges' into bz/permit2…
nicholaspai Dec 4, 2024
761c5f3
rebase /programs on master
nicholaspai Dec 4, 2024
19a38d4
clean up comments
nicholaspai Dec 4, 2024
458f67c
rebase programs
nicholaspai Dec 4, 2024
e5473c5
fix: consolidate structs so that permit2 witnesses cover inputs
bmzig Dec 4, 2024
e116240
begin permit2 unit tests
bmzig Dec 5, 2024
1c42606
wip
nicholaspai Dec 5, 2024
0402a6b
rebase
nicholaspai Dec 5, 2024
9acb4a2
Update SpokePoolPeriphery.t.sol
nicholaspai Dec 5, 2024
1927810
move type definitions to interface
bmzig Dec 5, 2024
7e4611d
fix permit2 test
bmzig Dec 5, 2024
e3cfb18
transfer type tests
bmzig Dec 5, 2024
f773316
rename EIP1271Signature to Permi2Approval
bmzig Dec 6, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
598 changes: 320 additions & 278 deletions contracts/SpokePoolV3Periphery.sol

Large diffs are not rendered by default.

19 changes: 19 additions & 0 deletions contracts/external/interfaces/IPermit2.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,19 @@
pragma solidity ^0.8.0;

interface IPermit2 {
struct PermitDetails {
address token;
uint160 amount;
uint48 expiration;
uint48 nonce;
}

struct PermitSingle {
PermitDetails details;
address spender;
uint256 sigDeadline;
}

struct TokenPermissions {
address token;
uint256 amount;
Expand All @@ -18,6 +31,12 @@ interface IPermit2 {
uint256 requestedAmount;
}

function permit(
address owner,
PermitSingle memory permitSingle,
bytes calldata signature
) external;

function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
Expand Down
260 changes: 95 additions & 165 deletions contracts/interfaces/SpokePoolV3PeripheryInterface.sol
Original file line number Diff line number Diff line change
Expand Up @@ -5,32 +5,11 @@ import { IERC20 } from "@openzeppelin/contracts/token/ERC20/IERC20.sol";
import { IERC20Permit } from "@openzeppelin/contracts/token/ERC20/extensions/IERC20Permit.sol";
import { IERC20Auth } from "../external/interfaces/IERC20Auth.sol";
import { SpokePoolV3Periphery } from "../SpokePoolV3Periphery.sol";
import { PeripherySigningLib } from "../libraries/PeripherySigningLib.sol";
import { IPermit2 } from "../external/interfaces/IPermit2.sol";

interface SpokePoolV3PeripheryProxyInterface {
/**
* @notice Swaps tokens on this chain via specified router before submitting Across deposit atomically.
* Caller can specify their slippage tolerance for the swap and Across deposit params.
* @dev If swapToken or acrossInputToken are the native token for this chain then this function might fail.
* the assumption is that this function will handle only ERC20 tokens.
* @param swapToken Address of the token that will be swapped for acrossInputToken.
* @param acrossInputToken Address of the token that will be bridged via Across as the inputToken.
* @param exchange Address of the exchange contract to call.
* @param routerCalldata ABI encoded function data to call on router. Should form a swap of swapToken for
* enough of acrossInputToken, otherwise this function will revert.
* @param swapTokenAmount Amount of swapToken to swap for a minimum amount of depositData.inputToken.
* @param minExpectedInputTokenAmount Minimum amount of received depositData.inputToken that we'll submit bridge
* deposit with.
* @param depositData Specifies the Across deposit params we'll send after the swap.
*/
function swapAndBridge(
IERC20 swapToken,
IERC20 acrossInputToken,
address exchange,
bytes calldata routerCalldata,
uint256 swapTokenAmount,
uint256 minExpectedInputTokenAmount,
SpokePoolV3Periphery.DepositData calldata depositData
) external;
function swapAndBridge(SpokePoolV3PeripheryInterface.SwapAndDepositData calldata swapAndDepositData) external;
}

/**
Expand All @@ -41,27 +20,75 @@ interface SpokePoolV3PeripheryProxyInterface {
* @custom:security-contact [email protected]
*/
interface SpokePoolV3PeripheryInterface {
/**
* @notice Passthrough function to `depositV3()` 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.
* This contract should only be used for native token deposits, as this problem only exists for native tokens.
* @param recipient Address to receive funds at on destination chain.
* @param inputToken Token to lock into this contract to initiate deposit.
* @param inputAmount Amount of tokens to deposit.
* @param outputAmount Amount of tokens to receive on destination chain.
* @param destinationChainId Denotes network where user will receive funds from SpokePool by a relayer.
* @param quoteTimestamp Timestamp used by relayers to compute this deposit's realizedLPFeePct which is paid
* to LP pool on HubPool.
* @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 exclusiveRelayer Address of the relayer who has exclusive rights to fill this deposit. Can be set to
* 0x0 if no period is desired. If so, then must set exclusivityParameter to 0.
* @param exclusivityParameter Timestamp or offset, after which any relayer can fill this deposit. Must set
* to 0 if exclusiveRelayer is set to 0x0, and vice versa.
* @param fillDeadline Timestamp after which this deposit can no longer be filled.
*/
// Enum describing the method of transferring tokens to an exchange.
enum TransferType {
// Approve the exchange so that it may transfer tokens from this contract.
Approval,
// Transfer tokens to the exchange before calling it in this contract.
Transfer,
// Approve the exchange by authorizing a transfer with Permit2.
Permit2Approval
}

// Params we'll need caller to pass in to specify an Across Deposit. The input token will be swapped into first
// before submitting a bridge deposit, which is why we don't include the input token amount as it is not known
// until after the swap.
struct BaseDepositData {
// Token deposited on origin chain.
address inputToken;
// Token received on destination chain.
address outputToken;
// Amount of output token to be received by recipient.
uint256 outputAmount;
// The account credited with deposit who can submit speedups to the Across deposit.
address depositor;
// The account that will receive the output token on the destination chain. If the output token is
// wrapped native token, then if this is an EOA then they will receive native token on the destination
// chain and if this is a contract then they will receive an ERC20.
address recipient;
// The destination chain identifier.
uint256 destinationChainId;
// The account that can exclusively fill the deposit before the exclusivity parameter.
address exclusiveRelayer;
// Timestamp of the deposit used by system to charge fees. Must be within short window of time into the past
// relative to this chain's current time or deposit will revert.
uint32 quoteTimestamp;
// The timestamp on the destination chain after which this deposit can no longer be filled.
uint32 fillDeadline;
// The timestamp or offset on the destination chain after which anyone can fill the deposit. A detailed description on
// how the parameter is interpreted by the V3 spoke pool can be found at https://github.com/across-protocol/contracts/blob/fa67f5e97eabade68c67127f2261c2d44d9b007e/contracts/SpokePool.sol#L476
uint32 exclusivityParameter;
// Data that is forwarded to the recipient if the recipient is a contract.
bytes message;
}

// Minimum amount of parameters needed to perform a swap on an exchange specified. We include information beyond just the router calldata
// and exchange address so that we may ensure that the swap was performed properly.
struct SwapAndDepositData {
// Deposit data to use when interacting with the Across spoke pool.
BaseDepositData depositData;
// Token to swap.
address swapToken;
// Address of the exchange to use in the swap.
address exchange;
// Method of transferring tokens to the exchange.
TransferType transferType;
// Amount of the token to swap on the exchange.
uint256 swapTokenAmount;
// Minimum output amount of the exchange, and, by extension, the minimum required amount to deposit into an Across spoke pool.
uint256 minExpectedInputTokenAmount;
// The calldata to use when calling the exchange.
bytes routerCalldata;
}

// Extended deposit data to be used specifically for signing off on periphery deposits.
struct DepositData {
// Deposit data describing the parameters for the V3 Across deposit.
BaseDepositData baseDepositData;
// The precise input amount to deposit into the spoke pool.
uint256 inputAmount;
}

function deposit(
address recipient,
address inputToken,
Expand All @@ -75,144 +102,47 @@ interface SpokePoolV3PeripheryInterface {
bytes memory message
) external payable;

/**
* @notice Swaps tokens on this chain via specified router before submitting Across deposit atomically.
* Caller can specify their slippage tolerance for the swap and Across deposit params.
* @dev If msg.value is 0, then this function is only callable by the proxy contract, to protect against
* approval abuse attacks where a user has set an approval on this contract to spend any ERC20 token.
* @dev If swapToken or acrossInputToken are the native token for this chain then this function might fail.
* the assumption is that this function will handle only ERC20 tokens.
* @param swapToken Address of the token that will be swapped for acrossInputToken.
* @param acrossInputToken Address of the token that will be bridged via Across as the inputToken.
* @param exchange Address of the exchange contract to call.
* @param routerCalldata ABI encoded function data to call on router. Should form a swap of swapToken for
* enough of acrossInputToken, otherwise this function will revert.
* @param swapTokenAmount Amount of swapToken to swap for a minimum amount of depositData.inputToken.
* @param minExpectedInputTokenAmount Minimum amount of received depositData.inputToken that we'll submit bridge
* deposit with.
* @param depositData Specifies the Across deposit params we'll send after the swap.
*/
function swapAndBridge(
IERC20 swapToken,
IERC20 acrossInputToken,
address exchange,
bytes calldata routerCalldata,
uint256 swapTokenAmount,
uint256 minExpectedInputTokenAmount,
SpokePoolV3Periphery.DepositData calldata depositData
) external payable;
function swapAndBridge(SwapAndDepositData calldata swapAndDepositData) external payable;

/**
* @notice Swaps an EIP-2612 token on this chain via specified router before submitting Across deposit atomically.
* Caller can specify their slippage tolerance for the swap and Across deposit params.
* @dev If swapToken does not implement `permit` to the specifications of EIP-2612, this function will fail.
* @param swapToken Address of the token that will be swapped for acrossInputToken.
* @param acrossInputToken Address of the token that will be bridged via Across as the inputToken.
* @param exchange Address of the exchange contract to call.
* @param routerCalldata ABI encoded function data to call on router. Should form a swap of swapToken for
* enough of acrossInputToken, otherwise this function will revert.
* @param swapTokenAmount Amount of swapToken to swap for a minimum amount of depositData.inputToken.
* @param minExpectedInputTokenAmount Minimum amount of received depositData.inputToken that we'll submit bridge
* deposit with.
* @param depositData Specifies the Across deposit params we'll send after the swap.
* @param deadline Deadline before which the permit signature is valid.
* @param v v of the permit signature.
* @param r r of the permit signature.
* @param s s of the permit signature.
*/
function swapAndBridgeWithPermit(
IERC20Permit swapToken,
IERC20 acrossInputToken,
address exchange,
bytes calldata routerCalldata,
uint256 swapTokenAmount,
uint256 minExpectedInputTokenAmount,
SpokePoolV3Periphery.DepositData calldata depositData,
SwapAndDepositData calldata swapAndDepositData,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
bytes calldata permitSignature
) external;

function swapAndBridgeWithPermit2(
address signatureOwner,
SwapAndDepositData calldata swapAndDepositData,
IPermit2.PermitTransferFrom calldata permit,
bytes calldata signature
) external;

/**
* @notice Swaps an EIP-3009 token on this chain via specified router before submitting Across deposit atomically.
* Caller can specify their slippage tolerance for the swap and Across deposit params.
* @dev If swapToken does not implement `receiveWithAuthorization` to the specifications of EIP-3009, this call will revert.
* @param swapToken Address of the token that will be swapped for acrossInputToken.
* @param acrossInputToken Address of the token that will be bridged via Across as the inputToken.
* @param exchange Address of the exchange contract to call.
* @param routerCalldata ABI encoded function data to call on router. Should form a swap of swapToken for
* enough of acrossInputToken, otherwise this function will revert.
* @param swapTokenAmount Amount of swapToken to swap for a minimum amount of depositData.inputToken.
* @param minExpectedInputTokenAmount Minimum amount of received depositData.inputToken that we'll submit bridge
* deposit with.
* @param depositData Specifies the Across deposit params we'll send after the swap.
* @param validAfter The unix time after which the `receiveWithAuthorization` signature is valid.
* @param validBefore The unix time before which the `receiveWithAuthorization` signature is valid.
* @param nonce Unique nonce used in the `receiveWithAuthorization` signature.
* @param v v of the EIP-3009 signature.
* @param r r of the EIP-3009 signature.
* @param s s of the EIP-3009 signature.
*/
function swapAndBridgeWithAuthorization(
IERC20Auth swapToken,
IERC20 acrossInputToken,
address exchange,
bytes calldata routerCalldata,
uint256 swapTokenAmount,
uint256 minExpectedInputTokenAmount,
SpokePoolV3Periphery.DepositData calldata depositData,
SwapAndDepositData calldata swapAndDepositData,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
bytes calldata receiveWithAuthSignature
) external;

/**
* @notice Deposits an EIP-2612 token Across input token into the Spoke Pool contract.
* @dev If `acrossInputToken` does not implement `permit` to the specifications of EIP-2612, this function will fail.
* @param acrossInputToken EIP-2612 compliant token to deposit.
* @param acrossInputAmount Amount of the input token to deposit.
* @param depositData Specifies the Across deposit params to send.
* @param deadline Deadline before which the permit signature is valid.
* @param v v of the permit signature.
* @param r r of the permit signature.
* @param s s of the permit signature.
*/
function depositWithPermit(
IERC20Permit acrossInputToken,
uint256 acrossInputAmount,
SpokePoolV3Periphery.DepositData calldata depositData,
DepositData calldata depositData,
uint256 deadline,
uint8 v,
bytes32 r,
bytes32 s
bytes calldata permitSignature
) external;

function depositWithPermit2(
address signatureOwner,
DepositData calldata depositData,
IPermit2.PermitTransferFrom calldata permit,
bytes calldata signature
) external;

/**
* @notice Deposits an EIP-3009 compliant Across input token into the Spoke Pool contract.
* @dev If `acrossInputToken` does not implement `receiveWithAuthorization` to the specifications of EIP-3009, this call will revert.
* @param acrossInputToken EIP-3009 compliant token to deposit.
* @param acrossInputAmount Amount of the input token to deposit.
* @param depositData Specifies the Across deposit params to send.
* @param validAfter The unix time after which the `receiveWithAuthorization` signature is valid.
* @param validBefore The unix time before which the `receiveWithAuthorization` signature is valid.
* @param nonce Unique nonce used in the `receiveWithAuthorization` signature.
* @param v v of the EIP-3009 signature.
* @param r r of the EIP-3009 signature.
* @param s s of the EIP-3009 signature.
*/
function depositWithAuthorization(
IERC20Auth acrossInputToken,
uint256 acrossInputAmount,
SpokePoolV3Periphery.DepositData calldata depositData,
DepositData calldata depositData,
uint256 validAfter,
uint256 validBefore,
bytes32 nonce,
uint8 v,
bytes32 r,
bytes32 s
bytes calldata receiveWithAuthSignature
) external;
}
Loading
Loading