Notice: it's a beta version, drastic improvement may take place.
-f, --file <path> Data to sign type definitions file path
-d, --dist <path> Destination folder
-c, --contract Generate contract artifacts
-s, --script Generate scripts artifacts
-l, --launch Automatically launch generated tests (if -c -s passed)
-V, --version output the version number
-h, --help display help for command
It is required to create config file definition.ts that exports the needed types with the following structure:
export default {
domain: {
name: 'Sample',
version: '1',
chainId?
verifyingContract?
salt?
},
struct: [
{
name: "TypeOne",
props: [
{ name: 'sample0', type: 'uint256' },
{ name: 'struct1', type: 'Struct', struct: true, omit: true },
],
external: [
{ name: 'sample3', type: 'uint256' },
]
},
{
name: "TypeTwo",
props: [
{ name: 'sample1', type: 'uint256' },
{ name: 'sample4', type: 'TypeOneWithoutExternalsAndStructs', struct: true },
],
external: [
{ name: 'enumerable', type: 'Mocks', enum: true },
{ name: 'sample2', type: 'RelatedStruct', struct: true }
]
}
],
related: [
{
name: "RelatedStruct",
props: [
{ name: 'sample5', type: 'uint256' },
],
external: []
},
{
name: "TypeOneWithoutExternalsAndStructs",
props: [
{ name: 'sample0', type: 'uint256' },
],
external: []
},
]
};
When config is ready there are two consumption options:
- as a package:
npx @guardian-network/signgen gen -c -s -l(not implemented) - inside a project:
- install
pnpm:npm add -g pnpm git clonecd signgenpnpm i- move the
definition.tsfile to the root of the project - nearpackage.json- for default mode - execute:
- for default mode (resulting folder:
output):pnpm cli -c -s -l - for custom mode:
pnpm cli -c -s -l -f "{YOUR_DEFINITION_PATH}" -d "{YOUR_OUTPUT_PATH}"
- for default mode (resulting folder:
- install
- Definition must have at least one message (
def#struct.length > 0) - Message must have at least one field (
def#struct#props.length > 0) - Use explicit types! (
uint => uint256/128/64...) - If parameter is a struct, use
struct: trueflag - If parameter is an enum, use
enum: trueflag - Use
omit: trueflag to exclude the field from the signature but keep it in the message struct (use it only for message struct members - #props) - Do not sign too much data in one message or at least do not use lots of struct fields
- Negative example:
name: "Don't", props: [ { name: 'struct1', type: 'Struct', struct: true }, { name: 'struct2', type: 'Struct', struct: true }, { name: 'struct3', type: 'Struct', struct: true }, { name: 'struct4', type: 'Struct', struct: true }, { name: 'struct5', type: 'Struct', struct: true }, ], external: []
- Negative example:
- Struct arrays are not supported yet
- Negative example:
name: "Don't", props: [ { name: 'struct1', type: 'Struct[]', struct: true }, ], external: []
- Negative example:
- Avoid cyclic dependencies!
- Negative example:
name: "Don't", props: [ { name: 'struct1', type: 'Don't', struct: true }, ], external: []
- Negative example:
- Now the nesting level maximum is limited to 2. It means that if a StructA has a property of type StructB and the StructB has a field of a related type StructC, the StructC won't be resolved in EIP-712 type definitions.
- Negative example:
struct: [ { name: "TypeOne", props: [ { name: 'sample2', type: 'RelatedStruct', struct: true }, ], external: [] }, { name: "TypeTwo", props: [ { name: 'sample4', type: 'TypeOne', struct: true }, ], external: [] } ], related: [ { name: "RelatedStruct", props: [ { name: 'sample5', type: 'uint256' }, ], external: [] } ]
- Negative example:
- Structure types that are used as a parameters for a messages (in
structblock) but are not included in thestructblock must be defined inrelatedblock in the same manner as astruct, exceptrelatedcan not haveexternals - If you want to reuse Struct that is defined in
structblock, redefine it inrelatedblock without externals relatedstruct must not havestruct: truefields (for now)propsandexternalblocks of the same Struct must not share members with the same name (cause they will end up as indistinguishable signature type fields). As far as you won't omit the conflicting one.- Negative example:
name: "Don't", props: [ { name: 'num', type: 'uint256' }, ], external: [ { name: 'num', type: 'uint256' }, ] - Positive example:
name: "Do", props: [ { name: 'omitMe', type: 'uint256', omit: true }, ], external: [ { name: 'omitMe', type: 'uint256'} ]
- Negative example:
- No
externalproperties should be namedaddrordomainSeparator; those are reserved names- Negative example:
name: "Don't", props: [ { name: 'num', type: 'uint256' }, ], external: [ { name: 'addr', type: 'address' }, { name: 'domainSeparator', type: 'bytes32' }, ]
- Negative example:
- All the undefined struct types will be mocked with
{ exists: bool } - All the enums will be mocked with
{ EXISTS } - The given
cli -c -s -lcommand and flags will automatically launch generated tests - Use
pnpm test:e2efor end-to-end testing - Use
pnpm test:unitfor syntax unit testing - Use
pnpm test:pruneto generate output deleting previous output folder (for quick definition changing and regenerating). - Generated tests-specific constraints:
- If
saltis not specified, it won't be included in the domain separator. - If
verifyingContractis not specified, the address of the Signature Verification contract will be used in the domain separator as verifying contract's address. - If
chainIdis not specified, the chain ID of the emulator's blockchain will be used (extracted from the hardhat'ssigner). Change before deploy
- If