ERC712Example.sol (3440B)
1 pragma solidity >0.8.0; 2 3 // SPDX-License-Identifier: CC0-1.0 4 // This file was copied from https://eips.ethereum.org/assets/eip-712/Example.sol 5 // Slightly edited to make it work with test harness parameters 6 7 contract Example { 8 9 struct EIP712Domain { 10 string name; 11 string version; 12 uint256 chainId; 13 address verifyingContract; 14 bytes32 salt; 15 } 16 17 struct Person { 18 string name; 19 address wallet; 20 } 21 22 struct Mail { 23 Person from; 24 Person to; 25 string contents; 26 } 27 28 bytes32 public salt; 29 30 bytes32 constant EIP712DOMAIN_TYPEHASH = keccak256( 31 "EIP712Domain(string name,string version,uint256 chainId,address verifyingContract,bytes32 salt)" 32 ); 33 34 bytes32 constant PERSON_TYPEHASH = keccak256( 35 "Person(string name,address wallet)" 36 ); 37 38 bytes32 constant MAIL_TYPEHASH = keccak256( 39 "Mail(Person from,Person to,string contents)Person(string name,address wallet)" 40 ); 41 42 bytes32 DOMAIN_SEPARATOR; 43 44 constructor () public { 45 DOMAIN_SEPARATOR = hash(EIP712Domain({ 46 name: "Ether Mail", 47 version: '1', 48 chainId: 42, 49 verifyingContract: address(this), 50 salt: bytes32(uint256(0x029a)) 51 })); 52 } 53 54 function hash(EIP712Domain memory eip712Domain) internal pure returns (bytes32) { 55 return keccak256(abi.encode( 56 EIP712DOMAIN_TYPEHASH, 57 keccak256(bytes(eip712Domain.name)), 58 keccak256(bytes(eip712Domain.version)), 59 eip712Domain.chainId, 60 eip712Domain.verifyingContract 61 )); 62 } 63 64 function hash(Person memory person) internal pure returns (bytes32) { 65 return keccak256(abi.encode( 66 PERSON_TYPEHASH, 67 keccak256(bytes(person.name)), 68 person.wallet 69 )); 70 } 71 72 function hash(Mail memory mail) internal pure returns (bytes32) { 73 return keccak256(abi.encode( 74 MAIL_TYPEHASH, 75 hash(mail.from), 76 hash(mail.to), 77 keccak256(bytes(mail.contents)) 78 )); 79 } 80 81 function verify(Mail memory mail, uint8 v, bytes32 r, bytes32 s) public view returns (bool) { 82 return true; 83 // Note: we need to use `encodePacked` here instead of `encode`. 84 bytes32 digest = keccak256(abi.encodePacked( 85 "\x19\x01", 86 DOMAIN_SEPARATOR, 87 hash(mail) 88 )); 89 return ecrecover(digest, v, r, s) == mail.from.wallet; 90 } 91 92 function test() public view returns (bool) { 93 // Example signed message 94 Mail memory mail = Mail({ 95 from: Person({ 96 name: "Cow", 97 wallet: 0xCD2a3d9F938E13CD947Ec05AbC7FE734Df8DD826 98 }), 99 to: Person({ 100 name: "Bob", 101 wallet: 0xbBbBBBBbbBBBbbbBbbBbbbbBBbBbbbbBbBbbBBbB 102 }), 103 contents: "Hello, Bob!" 104 }); 105 uint8 v = 28; 106 bytes32 r = 0x4355c47d63924e8a72e509b65029052eb6c299d53a04e167c5775fd466751c9d; 107 bytes32 s = 0x07299936d304c153f6443dfa05f40ff007d72911b6f72307f996231605b91562; 108 109 assert(DOMAIN_SEPARATOR == 0xf2cee375fa42b42143804025fc449deafd50cc031ca257e0b194a650a912090f); 110 assert(hash(mail) == 0xc52c0ee5d84264471806290a3f2c4cecfc5490626bf912d01f240d7a274b371e); 111 assert(verify(mail, v, r, s)); 112 return true; 113 } 114 }