services/typeddata/eip712example/example.sol
pragma solidity ^0.8.0;
contract Example {
struct EIP712Domain {
string name;
string version;
uint256 chainId;
address verifyingContract;
}
struct Person {
string name;
address wallet;
}
struct Mail {
Person from;
Person to;
string contents;
}
bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256(
"EIP712Domain(string name,string version,uint256 chainId,address verifyingContract)"
);
bytes32 constant PERSON_TYPEHASH = keccak256(
"Person(string name,address wallet)"
);
bytes32 constant MAIL_TYPEHASH = keccak256(
"Mail(Person from,Person to,string contents)Person(string name,address wallet)"
);
Mail internal mail = Mail({
from: Person({
name: "Cow",
wallet: 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826
}),
to: Person({
name: "Bob",
wallet: 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB
}),
contents: "Hello, Bob!"
});
bytes32 public DOMAIN_SEPARATOR;
bytes32 public MAIL;
constructor () public {
DOMAIN_SEPARATOR = hash(EIP712Domain({
name: "Ether Mail",
version: '1',
chainId: 1,
// verifyingContract: this
verifyingContract: 0xCcCCccccCCCCcCCCCCCcCcCccCcCCCcCcccccccC
}));
MAIL = hash(mail);
}
function hash(EIP712Domain memory eip712Domain) internal pure returns (bytes32) {
return keccak256(abi.encode(
EIP712DOMAIN_TYPEHASH,
keccak256(bytes(eip712Domain.name)),
keccak256(bytes(eip712Domain.version)),
eip712Domain.chainId,
eip712Domain.verifyingContract
));
}
function hash(Person memory person) internal pure returns (bytes32) {
return keccak256(abi.encode(
PERSON_TYPEHASH,
keccak256(bytes(person.name)),
person.wallet
));
}
function hash(Mail memory m) internal pure returns (bytes32) {
return keccak256(abi.encode(
MAIL_TYPEHASH,
hash(m.from),
hash(m.to),
keccak256(bytes(m.contents))
));
}
function verify(uint8 v, bytes32 r, bytes32 s) public returns (bool) {
// Note: we need to use `encodePacked` here instead of `encode`.
bytes32 digest = keccak256(abi.encodePacked(
"\x19\x01",
DOMAIN_SEPARATOR,
hash(mail)
));
require(ecrecover(digest, v, r, s) == msg.sender);
return true;
}
}