diff --git a/Cargo.toml b/Cargo.toml index da413e10..9a1525ee 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -81,7 +81,7 @@ ethers-signers = { version = "^0.5.0", default-features = false, path = "./ether ethers-middleware = { version = "^0.5.0", default-features = false, path = "./ethers-middleware" } [dev-dependencies] -ethers-contract = { version = "^0.5.0", default-features = false, path = "./ethers-contract", features = ["abigen"] } +ethers-contract = { version = "^0.5.0", default-features = false, path = "./ethers-contract", features = ["abigen", "eip712"] } ethers-providers = { version = "^0.5.0", default-features = false, path = "./ethers-providers", features = ["ws", "ipc"] } anyhow = "1.0.39" @@ -90,4 +90,4 @@ serde = { version = "1.0.124", features = ["derive"] } serde_json = "1.0.64" tokio = { version = "1.5", features = ["macros", "rt-multi-thread"] } hex = "0.4.3" -bytes = "1.1.0" \ No newline at end of file +bytes = "1.1.0" diff --git a/ethers-contract/Cargo.toml b/ethers-contract/Cargo.toml index b7afff18..664ef667 100644 --- a/ethers-contract/Cargo.toml +++ b/ethers-contract/Cargo.toml @@ -11,9 +11,10 @@ keywords = ["ethereum", "web3", "celo", "ethers"] [dependencies] ethers-providers = { version = "^0.5.0", path = "../ethers-providers", default-features = false } -ethers-core = { version = "^0.5.0", path = "../ethers-core", default-features = false, features = ["eip712"]} +ethers-core = { version = "^0.5.0", path = "../ethers-core", default-features = false } ethers-contract-abigen = { version = "^0.5.0", path = "ethers-contract-abigen", optional = true } ethers-contract-derive = { version = "^0.5.0", path = "ethers-contract-derive", optional = true } +ethers-derive-eip712 = { version = "0.1.0", path = "../ethers-core/ethers-derive-eip712", optional = true } serde = { version = "1.0.124", default-features = false } serde_json = { version = "1.0.64", default-features = false } @@ -29,12 +30,14 @@ ethers-providers = { version = "^0.5.0", path = "../ethers-providers", default-f ethers-signers = { version = "^0.5.0", path = "../ethers-signers" } ethers-contract-abigen = { version = "^0.5.0", path = "ethers-contract-abigen" } ethers-contract-derive = { version = "^0.5.0", path = "ethers-contract-derive" } +ethers-core = { version = "^0.5.0", path = "../ethers-core", default-features = false, features = ["eip712"]} ethers-derive-eip712 = { version = "0.1.0", path = "../ethers-core/ethers-derive-eip712"} [target.'cfg(not(target_arch = "wasm32"))'.dev-dependencies] tokio = { version = "1.5", default-features = false, features = ["macros"] } [features] +eip712 = ["ethers-derive-eip712", "ethers-core/eip712"] abigen = ["ethers-contract-abigen", "ethers-contract-derive"] celo = ["legacy", "ethers-core/celo", "ethers-core/celo", "ethers-providers/celo"] legacy = [] diff --git a/examples/permit_hash.rs b/examples/permit_hash.rs index db6c4cc0..28537243 100644 --- a/examples/permit_hash.rs +++ b/examples/permit_hash.rs @@ -1,94 +1,36 @@ use ethers::{ - abi, - abi::Token, - prelude::U256, - types::Address, - utils, - utils::{keccak256, parse_ether}, + contract::Eip712, + contract::EthAbiType, + core::types::transaction::eip712::Eip712, + types::{Address, U256}, }; -const UNISWAP_V2_USDC_ETH_PAIR: &'static str = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc"; - // Generate the EIP712 permit hash to sign for a Uniswap V2 pair. // https://eips.ethereum.org/EIPS/eip-712 // https://eips.ethereum.org/EIPS/eip-2612 -fn main() { - // Test data - let owner: Address = "0x617072Cb2a1897192A9d301AC53fC541d35c4d9D" - .parse() - .unwrap(); - let spender: Address = "0x2819c144D5946404C0516B6f817a960dB37D4929" - .parse() - .unwrap(); - let value = parse_ether(10).unwrap(); - let nonce = U256::from(1); - let deadline = U256::from(3133728498 as u32); - let verifying_contract: Address = UNISWAP_V2_USDC_ETH_PAIR.parse().unwrap(); - let name = "Uniswap V2"; - let version = "1"; - let chainid = 1; - - // Typehash for the permit() function - let permit_typehash = utils::keccak256( - "Permit(address owner,address spender,uint256 value,uint256 nonce,uint256 deadline)", - ); - // Typehash for the struct used to generate the domain separator - let domain_typehash = keccak256( - "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)", - ); - - // Corresponds to solidity's abi.encode() - let domain_separator_input = abi::encode(&vec![ - Token::Uint(U256::from(domain_typehash)), - Token::Uint(U256::from(keccak256(&name))), - Token::Uint(U256::from(keccak256(&version))), - Token::Uint(U256::from(chainid)), - Token::Address(verifying_contract), - ]); - - // Corresponds to the following solidity: - // DOMAIN_SEPARATOR = keccak256( - // abi.encode( - // keccak256('EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)'), - // keccak256(bytes(name)), - // keccak256(bytes('1')), - // chainId, - // address(this) - // ) - // ); - let domain_separator = keccak256(&domain_separator_input); - - // Corresponds to solidity's abi.encode() - let struct_input = abi::encode(&vec![ - Token::Uint(U256::from(permit_typehash)), - Token::Address(owner), - Token::Address(spender), - Token::Uint(value), - Token::Uint(nonce), - Token::Uint(deadline), - ]); - let struct_hash = keccak256(&struct_input); - - // Corresponds to solidity's abi.encodePacked() - let digest_input = [ - &[0x19, 0x01], - domain_separator.as_ref(), - struct_hash.as_ref(), - ] - .concat(); - - // Matches the following solidity: - // bytes32 digest = keccak256( - // abi.encodePacked( - // '\x19\x01', - // DOMAIN_SEPARATOR, - // keccak256(abi.encode(PERMIT_TYPEHASH, owner, spender, value, nonces[owner]++, deadline)) - // ) - // ); - let permit_hash = keccak256(&digest_input); - - assert_eq!( - hex::encode(permit_hash), - "7b90248477de48c0b971e0af8951a55974733455191480e1e117c86cc2a6cd03" - ); +#[derive(Eip712, EthAbiType, Clone)] +#[eip712( + name = "Uniswap V2", + version = "1", + chain_id = 1, + verifying_contract = "0xB4e16d0168e52d35CaCD2c6185b44281Ec28C9Dc" +)] +struct Permit { + owner: Address, + spender: Address, + value: U256, + nonce: U256, + deadline: U256, +} + +fn main() { + let permit = Permit { + owner: Address::random(), + spender: Address::random(), + value: 100.into(), + nonce: 0.into(), + deadline: U256::MAX, + }; + let permit_hash = permit.encode_eip712().unwrap(); + println!("Permit hash: 0x{}", hex::encode(permit_hash)); }