Skip to content

Commit

Permalink
demo: add permit2 flow
Browse files Browse the repository at this point in the history
  • Loading branch information
anna-carroll committed Jul 12, 2024
1 parent 81e9690 commit 7414f2d
Show file tree
Hide file tree
Showing 3 changed files with 203 additions and 0 deletions.
6 changes: 6 additions & 0 deletions src/IEIP712.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

interface IEIP712 {
function DOMAIN_SEPARATOR() external view returns (bytes32);
}
134 changes: 134 additions & 0 deletions src/ISignatureTransfer.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,134 @@
// SPDX-License-Identifier: MIT
pragma solidity ^0.8.24;

import {IEIP712} from "./IEIP712.sol";

/// @title SignatureTransfer
/// @notice Handles ERC20 token transfers through signature based actions
/// @dev Requires user's token approval on the Permit2 contract
interface ISignatureTransfer is IEIP712 {
/// @notice Thrown when the requested amount for a transfer is larger than the permissioned amount
/// @param maxAmount The maximum amount a spender can request to transfer
error InvalidAmount(uint256 maxAmount);

/// @notice Thrown when the number of tokens permissioned to a spender does not match the number of tokens being transferred
/// @dev If the spender does not need to transfer the number of tokens permitted, the spender can request amount 0 to be transferred
error LengthMismatch();

/// @notice Emits an event when the owner successfully invalidates an unordered nonce.
event UnorderedNonceInvalidation(address indexed owner, uint256 word, uint256 mask);

/// @notice The token and amount details for a transfer signed in the permit transfer signature
struct TokenPermissions {
// ERC20 token address
address token;
// the maximum amount that can be spent
uint256 amount;
}

/// @notice The signed permit message for a single token transfer
struct PermitTransferFrom {
TokenPermissions permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}

/// @notice Specifies the recipient address and amount for batched transfers.
/// @dev Recipients and amounts correspond to the index of the signed token permissions array.
/// @dev Reverts if the requested amount is greater than the permitted signed amount.
struct SignatureTransferDetails {
// recipient address
address to;
// spender requested amount
uint256 requestedAmount;
}

/// @notice Used to reconstruct the signed permit message for multiple token transfers
/// @dev Do not need to pass in spender address as it is required that it is msg.sender
/// @dev Note that a user still signs over a spender address
struct PermitBatchTransferFrom {
// the tokens and corresponding amounts permitted for a transfer
TokenPermissions[] permitted;
// a unique value for every token owner's signature to prevent signature replays
uint256 nonce;
// deadline on the permit signature
uint256 deadline;
}

/// @notice A map from token owner address and a caller specified word index to a bitmap. Used to set bits in the bitmap to prevent against signature replay protection
/// @dev Uses unordered nonces so that permit messages do not need to be spent in a certain order
/// @dev The mapping is indexed first by the token owner, then by an index specified in the nonce
/// @dev It returns a uint256 bitmap
/// @dev The index, or wordPosition is capped at type(uint248).max
function nonceBitmap(address, uint256) external view returns (uint256);

/// @notice Transfers a token using a signed permit message
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param signature The signature to verify
function permitTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes calldata signature
) external;

/// @notice Transfers a token using a signed permit message
/// @notice Includes extra data provided by the caller to verify signature over
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @dev Reverts if the requested amount is greater than the permitted signed amount
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails The spender's requested transfer details for the permitted token
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitTransferFrom memory permit,
SignatureTransferDetails calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;

/// @notice Transfers multiple tokens using a signed permit message
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param signature The signature to verify
function permitTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes calldata signature
) external;

/// @notice Transfers multiple tokens using a signed permit message
/// @dev The witness type string must follow EIP712 ordering of nested structs and must include the TokenPermissions type definition
/// @notice Includes extra data provided by the caller to verify signature over
/// @param permit The permit data signed over by the owner
/// @param owner The owner of the tokens to transfer
/// @param transferDetails Specifies the recipient and requested amount for the token transfer
/// @param witness Extra data to include when checking the user signature
/// @param witnessTypeString The EIP-712 type definition for remaining string stub of the typehash
/// @param signature The signature to verify
function permitWitnessTransferFrom(
PermitBatchTransferFrom memory permit,
SignatureTransferDetails[] calldata transferDetails,
address owner,
bytes32 witness,
string calldata witnessTypeString,
bytes calldata signature
) external;

/// @notice Invalidates the bits specified in mask for the bitmap at the word position
/// @dev The wordPos is maxed at type(uint248).max
/// @param wordPos A number to index the nonceBitmap at
/// @param mask A bitmap masked against msg.sender's current bitmap at the word position
function invalidateUnorderedNonces(uint256 wordPos, uint256 mask) external;
}
63 changes: 63 additions & 0 deletions src/Orders.sol
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,9 @@
pragma solidity ^0.8.24;

import {IERC20} from "openzeppelin-contracts/contracts/token/ERC20/IERC20.sol";
import {ISignatureTransfer} from "./ISignatureTransfer.sol";

address constant permit2 = address(0);

/// @notice Tokens sent by the swapper as inputs to the order
/// @dev From ERC-7683
Expand Down Expand Up @@ -54,6 +57,36 @@ abstract contract OrderDestination {
// emit
emit Filled(outputs);
}

function fillPermit2(
uint32[] memory chainIds,
ISignatureTransfer.PermitBatchTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails,
bytes calldata signature
) external {
// generate Input structs from the TransferDetails
Output[] memory outputs = _generateOutputs(chainIds, permit.permitted, transferDetails);

// via permit2 contract, transfer all tokens to this contract && check the permit deadline
ISignatureTransfer(permit2).permitTransferFrom(permit, transferDetails, msg.sender, signature);

// emit
emit Filled(outputs);
}

function _generateOutputs(
uint32[] memory chainIds,
ISignatureTransfer.TokenPermissions[] memory permitted,
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails
) internal view returns (Output[] memory outputs) {
require(chainIds.length == transferDetails.length);
outputs = new Output[](transferDetails.length);
// generate an Input for each permitted transfer
for (uint256 i; i < transferDetails.length; i++) {
outputs[i] =
Output(permitted[i].token, transferDetails[i].requestedAmount, transferDetails[i].to, chainIds[i]);
}
}
}

/// @notice Contract capable of registering initiation of intent-based Orders.
Expand Down Expand Up @@ -110,6 +143,36 @@ abstract contract OrderOrigin {
}
}

function initiatePermit2(
Output[] memory outputs,
ISignatureTransfer.PermitBatchTransferFrom memory permit,
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails,
bytes calldata signature
) external {
// generate Input structs from the TransferDetails
Input[] memory inputs = _generateInputs(permit.permitted, transferDetails);

// via permit2 contract, transfer all tokens to this contract && check the permit deadline
ISignatureTransfer(permit2).permitTransferFrom(permit, transferDetails, msg.sender, signature);

// emit
emit Order(permit.deadline, inputs, outputs);
}

function _generateInputs(
ISignatureTransfer.TokenPermissions[] memory permitted,
ISignatureTransfer.SignatureTransferDetails[] memory transferDetails
) internal view returns (Input[] memory inputs) {
inputs = new Input[](transferDetails.length);
// generate an Input for each permitted transfer
for (uint256 i; i < transferDetails.length; i++) {
// ensure the tokens are being transferred to this address
require(transferDetails[i].to == address(this));
// construct an Input
inputs[i] = Input(permitted[i].token, transferDetails[i].requestedAmount);
}
}

/// @notice Transfer the entire balance of ERC20 tokens to the recipient.
/// @dev Called by the Builder within the same block as users' `initiate` transactions
/// to claim the `inputs`.
Expand Down

0 comments on commit 7414f2d

Please sign in to comment.