Skip to content

Commit

Permalink
feat: refactor permit types and add ERC20 permit support (#91)
Browse files Browse the repository at this point in the history
* feat: refactor permit types and add ERC20 permit support

Replaced custom `Permit` structs with standardized ones like `IERC721Permit::Permit` and `IERC20Permit::Permit`. This change enhances compatibility with token contracts and simplifies permit data handling. Also introduced a helper function for generating EIP-2612 domain and values for ERC20 permits.

* Add `SolStruct` bound to `ERC20PermitData` struct

Enforce `SolStruct` constraint on `ERC20PermitData` generic parameter. This ensures type safety by requiring the generic parameter to adhere to the `SolStruct` trait.
  • Loading branch information
shuhuiluo authored Oct 12, 2024
1 parent 4b90045 commit 674727c
Show file tree
Hide file tree
Showing 3 changed files with 133 additions and 20 deletions.
40 changes: 36 additions & 4 deletions src/abi.rs
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,20 @@ sol! {

function burn(uint256 tokenId) external payable;

function safeTransferFrom(address from, address to, uint256 tokenId) external;

function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

interface IERC721Permit {
#[derive(Debug, PartialEq, Eq)]
struct Permit {
address spender;
uint256 tokenId;
uint256 nonce;
uint256 deadline;
}

function permit(
address spender,
uint256 tokenId,
Expand All @@ -87,17 +101,35 @@ sol! {
bytes32 r,
bytes32 s
) external payable;

function safeTransferFrom(address from, address to, uint256 tokenId) external;

function safeTransferFrom(address from, address to, uint256 tokenId, bytes calldata data) external;
}

interface ISelfPermit {
function selfPermit(address token, uint256 value, uint256 deadline, uint8 v, bytes32 r, bytes32 s) external payable;
function selfPermitAllowed(address token, uint256 nonce, uint256 expiry, uint8 v, bytes32 r, bytes32 s) external payable;
}

interface IERC20Permit {
#[derive(Debug, PartialEq, Eq)]
struct Permit {
address owner;
address spender;
uint256 value;
uint256 nonce;
uint256 deadline;
}
}

interface IDaiPermit {
#[derive(Debug, PartialEq, Eq)]
struct Permit {
address holder;
address spender;
uint256 nonce;
uint256 expiry;
bool allowed;
}
}

interface IPeripheryPaymentsWithFee {
function unwrapWETH9(uint256 amountMinimum, address recipient) external payable;

Expand Down
20 changes: 5 additions & 15 deletions src/nonfungible_position_manager.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
use crate::prelude::{Error, *};
use alloy_primitives::{Bytes, Signature, U256};
use alloy_sol_types::{eip712_domain, sol, Eip712Domain, SolCall};
use alloy_sol_types::{eip712_domain, Eip712Domain, SolCall};
use uniswap_sdk_core::prelude::*;

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -66,20 +66,10 @@ pub struct CollectOptions<Currency0: BaseCurrency, Currency1: BaseCurrency> {
pub recipient: Address,
}

sol! {
#[derive(Debug, PartialEq, Eq)]
struct Permit {
address spender;
uint256 tokenId;
uint256 nonce;
uint256 deadline;
}
}

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct NFTPermitData {
pub domain: Eip712Domain,
pub values: Permit,
pub values: IERC721Permit::Permit,
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
Expand Down Expand Up @@ -341,7 +331,7 @@ where

if let Some(permit) = options.permit {
calldatas.push(
INonfungiblePositionManager::permitCall {
IERC721Permit::permitCall {
spender: permit.spender,
tokenId: token_id,
deadline: permit.deadline,
Expand Down Expand Up @@ -451,7 +441,7 @@ pub fn safe_transfer_from_parameters(options: SafeTransferOptions) -> MethodPara
/// use alloy_sol_types::SolStruct;
/// use uniswap_v3_sdk::prelude::*;
///
/// let permit = Permit {
/// let permit = IERC721Permit::Permit {
/// spender: address!("0000000000000000000000000000000000000002"),
/// tokenId: uint!(1_U256),
/// nonce: uint!(1_U256),
Expand All @@ -477,7 +467,7 @@ pub fn safe_transfer_from_parameters(options: SafeTransferOptions) -> MethodPara
#[inline]
#[must_use]
pub const fn get_permit_data(
permit: Permit,
permit: IERC721Permit::Permit,
position_manager: Address,
chain_id: u64,
) -> NFTPermitData {
Expand Down
93 changes: 92 additions & 1 deletion src/self_permit.rs
Original file line number Diff line number Diff line change
@@ -1,8 +1,99 @@
use super::abi::ISelfPermit;
use alloy_primitives::{Bytes, Signature, U256};
use alloy_sol_types::SolCall;
use alloy_sol_types::{eip712_domain, Eip712Domain, SolCall, SolStruct};
use uniswap_sdk_core::prelude::*;

#[derive(Debug, Clone, PartialEq, Eq)]
pub struct ERC20PermitData<P: SolStruct> {
pub domain: Eip712Domain,
pub values: P,
}

/// Get the EIP-2612 domain and values to sign for an ERC20 permit.
///
/// ## Arguments
///
/// * `permit`: The ERC20 permit
/// * `name`: The name of the contract
/// * `version`: The version of the contract
/// * `token`: The address of the token
/// * `chain_id`: The chain ID
///
/// ## Returns
///
/// The EIP-2612 domain and values to sign
///
/// ## Examples
///
/// ```
/// use alloy_primitives::{address, b256, uint, Signature, B256};
/// use alloy_signer::SignerSync;
/// use alloy_signer_local::PrivateKeySigner;
/// use alloy_sol_types::SolStruct;
/// use uniswap_v3_sdk::prelude::*;
///
/// let signer = PrivateKeySigner::random();
/// let permit = IERC20Permit::Permit {
/// owner: signer.address(),
/// spender: address!("0000000000000000000000000000000000000002"),
/// value: uint!(1_U256),
/// nonce: uint!(1_U256),
/// deadline: uint!(123_U256),
/// };
/// assert_eq!(
/// permit.eip712_type_hash(),
/// b256!("6e71edae12b1b97f4d1f60370fef10105fa2faae0126114a169c64845d6126c9")
/// );
/// assert_eq!(
/// IDaiPermit::Permit {
/// holder: signer.address(),
/// spender: address!("0000000000000000000000000000000000000002"),
/// nonce: uint!(1_U256),
/// expiry: uint!(123_U256),
/// allowed: true,
/// }
/// .eip712_type_hash(),
/// b256!("ea2aa0a1be11a07ed86d755c93467f4f82362b452371d1ba94d1715123511acb")
/// );
///
/// let data = get_erc20_permit_data(
/// permit,
/// "ONE",
/// "1",
/// address!("0000000000000000000000000000000000000001"),
/// 1,
/// );
///
/// // Derive the EIP-712 signing hash.
/// let hash: B256 = data.values.eip712_signing_hash(&data.domain);
///
/// let signature: Signature = signer.sign_hash_sync(&hash).unwrap();
/// assert_eq!(
/// signature.recover_address_from_prehash(&hash).unwrap(),
/// signer.address()
/// );
/// ```
#[inline]
#[must_use]
pub fn get_erc20_permit_data<P: SolStruct>(
permit: P,
name: &'static str,
version: &'static str,
token: Address,
chain_id: u64,
) -> ERC20PermitData<P> {
let domain = eip712_domain! {
name: name,
version: version,
chain_id: chain_id,
verifying_contract: token,
};
ERC20PermitData {
domain,
values: permit,
}
}

#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub struct StandardPermitArguments {
pub signature: Signature,
Expand Down

0 comments on commit 674727c

Please sign in to comment.