You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Copy file name to clipboardExpand all lines: README.md
+43-36Lines changed: 43 additions & 36 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,8 +1,11 @@
1
1
# EIP 712 Codegen
2
+
This module aims to automate the hard parts of using EIP-712
2
3
3
-
[EIP 712: Sign Typed Data](https://eips.ethereum.org/EIPS/eip-712) as of 2022 is the most human-readable way of getting signatures from user that are easily parsed into solidity structs.
4
4
5
-
[The documentation](https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4) has not always been the greatest, and in particular I think this method has failed to catch on because writing the verification code is a huge pain.
5
+
[EIP 712: Sign Typed Data](https://eips.ethereum.org/EIPS/eip-712) as of 2023 is the most human-readable way of getting signatures from user that are easily parsed into solidity structs.
6
+
7
+
[The documentation](https://docs.metamask.io/guide/signing-data.html#sign-typed-data-v4) is quite dense, and can be hard to get started with.
8
+
6
9
7
10
Well, no more. This module will generate basically all the solidity you need to let users basically sign structs and then mostly just think about the structs you have signed in your code. This should really level up your ability to keep more user actions off-chain and gas-free.
8
11
@@ -20,18 +23,39 @@ As a module, we are exporting typescript definition files, which can help you ge
20
23
21
24
### As a CLI tool:
22
25
23
-
You point it at a typeDef file (defined as a CommonJS module, [as seen in sampleTypes.js](./sampleTypes.js)), and it then prints out some solidity to the console. You can then pipe it into a file.
26
+
`npm i -g eip712-codegen` or `yarn add -g eip712-codegen` to globally install, and then you can run the command line and pipe the output into a solidity file like so:
-e, --entryPoints Type names to be used as entry points [array] [required]
37
+
-l, --log Enable logging [boolean]
38
+
-h, --help Show help [boolean]
39
+
```
40
+
41
+
The `input` file is a typeDef file (defined as a CommonJS module, [as seen in sampleTypes.js](./sampleTypes.js)), and it then prints out some solidity to the console. You can then pipe it into a file. The same typedef format is used by signing code for EIP-712, like when suggesting a signature to MetaMask, so this allows you to define these types once and reuse them on the front and backend.
If you're using [hardhat](hardhat.org/) and their [console.log](https://hardhat.org/hardhat-network/#console-log) feature, you can generate a logged version by adding `log`:
55
+
If you're using [hardhat](hardhat.org/) and their [console.log](https://hardhat.org/hardhat-network/#console-log) feature, you can generate a logged version by adding `--log`:
You'll then need to import this typefile into your contract, and inherit from `EIP712Decoder`.
@@ -68,36 +92,19 @@ You'll also need to include this one method that defines your DomainHash, which
68
92
}
69
93
```
70
94
71
-
There's one more thing you have to do, this part will require the most thinking. You'll have to write the method that verifies the top-level signatures. I have not written codegen for this yet, because I don't know which types you want to use as your entry points, and there are some design decisions that are up to you here (in particular, *your entrypoint types are your user-facing security enforcement*, but here is a sample method for verifying a `SignedDelegation` as defined in our [sampleTypes.js](./sampleTypes) file:
95
+
### Entrypoints
72
96
73
-
```solidity
74
-
function verifyDelegationSignature (SignedDelegation memory signedDelegation) public view returns (address) {
function getDelegationTypedDataHash(Delegation memory delegation) public view returns (bytes32) {
88
-
bytes32 digest = keccak256(abi.encodePacked(
89
-
"\x19\x01",
97
+
The `--entryPoints` flag generates signature verification code for the specified types (which must also be included in the input file). These verification methods will be of the form `verifySigned${YourType}(Signed${YourType} input) returns (address);`. So if you are signing a struct called `Bid` it will generate a method called `verifySignedBid(SignedBid input) returns (address);`
90
98
91
-
// The domainHash is derived from your contract name and address above:
92
-
domainHash,
99
+
Returns an `address` of the account that signed this struct.
93
100
94
-
// This last part is calling one of the generated methods.
95
-
// It must match the name of the struct that is the `primaryType` of this signature.
96
-
GET_DELEGATION_PACKETHASH(delegation)
97
-
));
98
-
return digest;
99
-
}
101
+
The `Signed{Type}` struct format looks like this:
102
+
```solidity
103
+
{
104
+
bytes signature;
105
+
address signer;
106
+
YourType message;
107
+
}
100
108
```
101
-
102
-
From there, you should be good! This library is tested to work with `eth_signTypedData_v4` as implemented in MetaMask. I have not yet tested it with ethers.js or other wallets, but there's a good chance it works for simple types, and a chance it works for arrays and structs as well.
103
-
109
+
For regular EOA signatures, the signer should be set to the zero address (`0x0000000000000000000000000000000000000000`).
110
+
If the `signer` value is set to anything other than the zero address, rather than recover a signature normally, the contract will execute [EIP-1271 style signature recovery](https://eips.ethereum.org/EIPS/eip-1271) which allows contract accounts to perform custom verification logic allowing them to effectively "sign" messages like an EOA does.
0 commit comments