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

Commit

Permalink
add example for eip712 and test against contract
Browse files Browse the repository at this point in the history
  • Loading branch information
Ryanmtate committed Oct 5, 2021
1 parent 8ce78f9 commit fa95585
Show file tree
Hide file tree
Showing 3 changed files with 341 additions and 0 deletions.
82 changes: 82 additions & 0 deletions examples/DeriveEip712Test.sol
Original file line number Diff line number Diff line change
@@ -0,0 +1,82 @@
// SPDX-License-Identifier: GPL-3.0-only
pragma solidity ^0.8.0;

contract DeriveEip712Test {
uint256 constant chainId = 1;
bytes32 constant salt = keccak256("eip712-test-75F0CCte");
bytes32 constant EIP712_DOMAIN_TYPEHASH =
keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)"
);

bytes32 constant FOOBAR_DOMAIN_TYPEHASH =
keccak256(
// "FooBar(int256 foo,uint256 bar,bytes fizz,bytes32 buzz,string far,address out)"
"FooBar(int256 foo)"
);

struct FooBar {
int256 foo;
// uint256 bar;
// bytes fizz;
// bytes32 buzz;
// string far;
// address out;
}

constructor() {}

function domainSeparator() public pure returns (bytes32) {
return
keccak256(
abi.encode(
EIP712_DOMAIN_TYPEHASH,
keccak256("Eip712Test"),
keccak256("1"),
chainId,
address(0x0000000000000000000000000000000000000001),
salt
)
);
}

function typeHash() public pure returns (bytes32) {
return FOOBAR_DOMAIN_TYPEHASH;
}

function structHash(FooBar memory fooBar) public pure returns (bytes32) {
return
keccak256(
abi.encode(
typeHash(),
uint256(fooBar.foo)
// fooBar.bar,
// keccak256(fooBar.fizz),
// fooBar.buzz,
// keccak256(abi.encodePacked(fooBar.far)),
// fooBar.out
)
);
}

function encodeEip712(FooBar memory fooBar) public pure returns (bytes32) {
return
keccak256(
abi.encodePacked(
"\\x19\\x01",
domainSeparator(),
structHash(fooBar)
)
);
}

function verifyFooBar(
address signer,
FooBar memory fooBar,
bytes32 r,
bytes32 s,
uint8 v
) public pure returns (bool) {
return signer == ecrecover(encodeEip712(fooBar), v, r, s);
}
}
131 changes: 131 additions & 0 deletions examples/derive_eip712_abi.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,131 @@
[
{
"inputs": [],
"stateMutability": "nonpayable",
"type": "constructor"
},
{
"inputs": [],
"name": "domainSeparator",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "int256",
"name": "foo",
"type": "int256"
}
],
"internalType": "struct DeriveEip712Test.FooBar",
"name": "fooBar",
"type": "tuple"
}
],
"name": "encodeEip712",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"components": [
{
"internalType": "int256",
"name": "foo",
"type": "int256"
}
],
"internalType": "struct DeriveEip712Test.FooBar",
"name": "fooBar",
"type": "tuple"
}
],
"name": "structHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [],
"name": "typeHash",
"outputs": [
{
"internalType": "bytes32",
"name": "",
"type": "bytes32"
}
],
"stateMutability": "pure",
"type": "function"
},
{
"inputs": [
{
"internalType": "address",
"name": "signer",
"type": "address"
},
{
"components": [
{
"internalType": "int256",
"name": "foo",
"type": "int256"
}
],
"internalType": "struct DeriveEip712Test.FooBar",
"name": "fooBar",
"type": "tuple"
},
{
"internalType": "bytes32",
"name": "r",
"type": "bytes32"
},
{
"internalType": "bytes32",
"name": "s",
"type": "bytes32"
},
{
"internalType": "uint8",
"name": "v",
"type": "uint8"
}
],
"name": "verifyFooBar",
"outputs": [
{
"internalType": "bool",
"name": "",
"type": "bool"
}
],
"stateMutability": "pure",
"type": "function"
}
]
128 changes: 128 additions & 0 deletions examples/eip712.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,128 @@
use std::{convert::TryFrom, sync::Arc, time::Duration};

use ethers::{
contract::EthAbiType,
prelude::*,
types::{transaction::eip712::Eip712, Address, I256, U256},
utils::{compile_and_launch_ganache, keccak256, Ganache, Solc},
};
use ethers_derive_eip712::*;

#[derive(Debug, Clone, Eip712, EthAbiType)]
#[eip712(
name = "Eip712Test",
version = "1",
chain_id = 1,
verifying_contract = "0x0000000000000000000000000000000000000001",
salt = "eip712-test-75F0CCte"
)]
struct FooBar {
foo: I256,
// bar: U256,
// fizz: Vec<u8>,
// buzz: [u8; 32],
// far: String,
// out: Address,
}

abigen!(
DeriveEip712Test,
"./examples/derive_eip712_abi.json",
event_derives(serde::Deserialize, serde::Serialize)
);

#[tokio::main]
async fn main() -> anyhow::Result<()> {
let contract_name = "DeriveEip712Test".to_string();
let (compiled, ganache) =
compile_and_launch_ganache(Solc::new("**/DeriveEip712Test.sol"), Ganache::new()).await?;

let wallet: LocalWallet = ganache.keys()[0].clone().into();

let contract = compiled.get(&contract_name).unwrap();

let provider =
Provider::<Http>::try_from(ganache.endpoint())?.interval(Duration::from_millis(10u64));

let client = SignerMiddleware::new(provider, wallet.clone());
let client = Arc::new(client);

let factory = ContractFactory::new(
contract.abi.clone(),
contract.bytecode.clone(),
client.clone(),
);

let contract = factory.deploy(())?.legacy().send().await?;

let addr = contract.address();

let contract = DeriveEip712Test::new(addr, client.clone());

let foo_bar = FooBar {
foo: I256::from(10),
// bar: U256::from(20),
// fizz: b"fizz".to_vec(),
// buzz: keccak256("buzz"),
// far: String::from("space"),
// out: Address::from([0; 20]),
};

let derived_foo_bar = deriveeip712test_mod::FooBar {
foo: foo_bar.foo.clone(),
// bar: foo_bar.bar.clone(),
// fizz: foo_bar.fizz.clone(),
// buzz: foo_bar.buzz.clone(),
// far: foo_bar.far.clone(),
// out: foo_bar.out.clone(),
};

let sig = wallet.sign_typed_data(foo_bar.clone()).await?;

let mut r = [0; 32];
let mut s = [0; 32];
let v = u8::try_from(sig.v)?;

sig.r.to_big_endian(&mut r);
sig.r.to_big_endian(&mut s);

let domain_separator = contract.domain_separator().call().await?;
let type_hash = contract.type_hash().call().await?;
let struct_hash = contract.struct_hash(derived_foo_bar.clone()).call().await?;
let encoded = contract
.encode_eip_712(derived_foo_bar.clone())
.call()
.await?;
let verify = contract
.verify_foo_bar(wallet.address(), derived_foo_bar, r, s, v)
.call()
.await?;

assert_eq!(
domain_separator,
FooBar::domain_separator()?,
"domain separator does not match contract domain separator!"
);

assert_eq!(
type_hash,
FooBar::type_hash()?,
"type hash does not match contract struct type hash!"
);

assert_eq!(
struct_hash,
foo_bar.clone().struct_hash()?,
"struct hash does not match contract struct struct hash!"
);

assert_eq!(
encoded,
foo_bar.encode_eip712()?,
"Encoded value does not match!"
);

assert_eq!(verify, true, "typed data signature failed!");

Ok(())
}

0 comments on commit fa95585

Please sign in to comment.