Skip to content
This repository has been archived by the owner on Oct 19, 2024. It is now read-only.

chore: simplify eip712 example #492

Merged
merged 3 commits into from
Oct 8, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
4 changes: 2 additions & 2 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand All @@ -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"
bytes = "1.1.0"
5 changes: 4 additions & 1 deletion ethers-contract/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -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 }
Expand All @@ -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 = []
Expand Down
114 changes: 28 additions & 86 deletions examples/permit_hash.rs
Original file line number Diff line number Diff line change
@@ -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);
#[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,
}

assert_eq!(
hex::encode(permit_hash),
"7b90248477de48c0b971e0af8951a55974733455191480e1e117c86cc2a6cd03"
);
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));
}