@agoric/orchestration 0.1.1-dev-fac5579.0.fac5579 → 0.1.1-dev-82a1d65.0.82a1d65
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +17 -17
- package/src/stubs/viem-typedData.js +1 -0
- package/src/utils/abitype.d.ts +41 -16
- package/src/utils/abitype.d.ts.map +1 -1
- package/src/utils/abitype.js +66 -16
- package/src/utils/address.d.ts +1 -0
- package/src/utils/address.d.ts.map +1 -1
- package/src/utils/address.js +11 -0
- package/src/utils/eip712-message-handler.d.ts +15 -1
- package/src/utils/eip712-message-handler.d.ts.map +1 -1
- package/src/utils/eip712-message-handler.js +80 -16
- package/src/utils/eip712-messages.d.ts +5 -5
- package/src/utils/eip712-messages.d.ts.map +1 -1
- package/src/utils/eip712-messages.js +25 -8
- package/src/utils/permit2/signatureTransferHelpers.d.ts.map +1 -1
- package/src/utils/permit2/signatureTransferHelpers.js +4 -1
- package/src/utils/viem-utils/eip712-normalize.d.ts +77 -4
- package/src/utils/viem-utils/eip712-normalize.d.ts.map +1 -1
- package/src/utils/viem-utils/eip712-normalize.js +188 -42
- package/src/vendor/viem/{chunk-XNGNAKRR.js → chunk-7BNMBC44.js} +43 -42
- package/src/vendor/viem/viem-abi.js +1 -1
- package/src/vendor/viem/viem-address.js +1 -1
- package/src/vendor/viem/viem-typedData.d.ts +1 -1
- package/src/vendor/viem/viem-typedData.js +3 -1
|
@@ -23,7 +23,11 @@
|
|
|
23
23
|
|
|
24
24
|
|
|
25
25
|
|
|
26
|
-
|
|
26
|
+
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
|
|
27
31
|
import {
|
|
28
32
|
|
|
29
33
|
|
|
@@ -31,7 +35,7 @@ import {
|
|
|
31
35
|
makeWitness,
|
|
32
36
|
PermitTransferFromTypeParams,
|
|
33
37
|
} from './permit2.js';
|
|
34
|
-
import { sameEvmAddress } from './address.js';
|
|
38
|
+
import { isEvmAddressShape, sameEvmAddress } from './address.js';
|
|
35
39
|
import { normalizeAndValidateEIP712Data } from './viem-utils/eip712-normalize.js';
|
|
36
40
|
|
|
37
41
|
;
|
|
@@ -148,12 +152,18 @@ export const StandaloneEnvelopeTypeParams = [
|
|
|
148
152
|
;
|
|
149
153
|
|
|
150
154
|
|
|
151
|
-
|
|
155
|
+
|
|
156
|
+
|
|
157
|
+
|
|
158
|
+
|
|
159
|
+
|
|
160
|
+
|
|
152
161
|
|
|
153
162
|
;
|
|
154
163
|
|
|
155
164
|
|
|
156
|
-
|
|
165
|
+
|
|
166
|
+
|
|
157
167
|
|
|
158
168
|
;
|
|
159
169
|
|
|
@@ -316,7 +326,7 @@ const validateSchema = (schema ) => {
|
|
|
316
326
|
|
|
317
327
|
|
|
318
328
|
|
|
319
|
-
|
|
329
|
+
|
|
320
330
|
|
|
321
331
|
|
|
322
332
|
|
|
@@ -324,7 +334,7 @@ const validateSchema = (schema ) => {
|
|
|
324
334
|
|
|
325
335
|
|
|
326
336
|
|
|
327
|
-
|
|
337
|
+
|
|
328
338
|
|
|
329
339
|
|
|
330
340
|
|
|
@@ -444,7 +454,7 @@ export const makeEIP712MessageKit = (
|
|
|
444
454
|
|
|
445
455
|
const getWitness = (
|
|
446
456
|
operation ,
|
|
447
|
-
data
|
|
457
|
+
data ,
|
|
448
458
|
) => {
|
|
449
459
|
const witnessTypeParam = getWitnessTypeParam(operation);
|
|
450
460
|
// Normalize away unused `optional` fields (and their now-unreferenced
|
|
@@ -474,7 +484,7 @@ export const makeEIP712MessageKit = (
|
|
|
474
484
|
});
|
|
475
485
|
|
|
476
486
|
const getStandaloneOperationData = (
|
|
477
|
-
data
|
|
487
|
+
data ,
|
|
478
488
|
operation ,
|
|
479
489
|
chainId ,
|
|
480
490
|
verifyingContract ,
|
|
@@ -530,6 +540,13 @@ export const makeEIP712MessageKit = (
|
|
|
530
540
|
`${domainName} domain must include chain ID and verifying contract`,
|
|
531
541
|
);
|
|
532
542
|
}
|
|
543
|
+
// Its declared type may not be `address` (see
|
|
544
|
+
// `extractOperationDetailsFromDataWithAddress`).
|
|
545
|
+
if (!isEvmAddressShape(verifyingContract)) {
|
|
546
|
+
throw new Error(
|
|
547
|
+
`Invalid verifying contract address in ${domainName} domain`,
|
|
548
|
+
);
|
|
549
|
+
}
|
|
533
550
|
|
|
534
551
|
if (validContractAddresses) {
|
|
535
552
|
const chainIdStr = String(chainId);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signatureTransferHelpers.d.ts","sourceRoot":"","sources":["signatureTransferHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAGV,2BAA2B,EAC3B,OAAO,EACP,SAAS,EACT,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAEL,KAAK,aAAa,EAKlB,yBAAyB,EAC1B,MAAM,wBAAwB,CAAC;AAGhC,QAAA,MAAM,YAAY;;;EAGhB,CAAC;AACH,KAAK,eAAe,GAAG,MAAM,OAAO,YAAY,CAAC;AAEjD,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,IAAI,IAAI,eACtC,CAAC;AAYvB,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAG8B,CAAC;AAO1D;;;GAGG;AACH,eAAO,MAAM,8BAA8B,GAAI,QAAQ;IACrD,UAAU,EAAE,OAAO,UAAU,CAAC;CAC/B,aAgCsC,SAAS,WAY/C,CAAC;AAUF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GAAI,uBAE1C;IACD,gBAAgB,EAAE,OAAO,yBAAyB,CAAC;CACpD,SAWA,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3D,OAAO,CAAC,MAAM,IAAI,aAAa,
|
|
1
|
+
{"version":3,"file":"signatureTransferHelpers.d.ts","sourceRoot":"","sources":["signatureTransferHelpers.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,EAGV,2BAA2B,EAC3B,OAAO,EACP,SAAS,EACT,eAAe,EAChB,MAAM,SAAS,CAAC;AAGjB,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AACxD,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,gCAAgC,CAAC;AACjE,OAAO,EAEL,KAAK,aAAa,EAKlB,yBAAyB,EAC1B,MAAM,wBAAwB,CAAC;AAGhC,QAAA,MAAM,YAAY;;;EAGhB,CAAC;AACH,KAAK,eAAe,GAAG,MAAM,OAAO,YAAY,CAAC;AAEjD,eAAO,MAAM,oBAAoB,GAAI,MAAM,MAAM,KAAG,IAAI,IAAI,eACtC,CAAC;AAYvB,QAAA,MAAM,qBAAqB;;;;;;;;;;;;;;;;;;;;;;;;;;;CAG8B,CAAC;AAO1D;;;GAGG;AACH,eAAO,MAAM,8BAA8B,GAAI,QAAQ;IACrD,UAAU,EAAE,OAAO,UAAU,CAAC;CAC/B,aAgCsC,SAAS,WAY/C,CAAC;AAUF;;;;;GAKG;AACH,eAAO,MAAM,4BAA4B,GAAI,uBAE1C;IACD,gBAAgB,EAAE,OAAO,yBAAyB,CAAC;CACpD,SAWA,CAAC;AAEF,wBAAgB,qBAAqB,CACnC,MAAM,EAAE,eAAe,EACvB,gBAAgB,CAAC,EAAE,OAAO,CAAC,MAAM,CAAC,MAAM,GAAG,MAAM,EAAE,OAAO,CAAC,CAAC,GAC3D,OAAO,CAAC,MAAM,IAAI,aAAa,CAmCjC;AAED;;;;;;GAMG;AACH,eAAO,MAAM,4BAA4B,GACvC,CAAC,SAAS,QAAQ,CAAC,kBAAkB,CAAC,EAEtC,OAAO,GACJ,CAAC,IAAI,eAAe,GAAG,MAAM,CAC5B,CAAC,EACD,SAAS,CAAC,GAAG,CAAC,OAAO,qBAAqB,EAAE,CAAC,CAAC,EAAE,CAAC,CAAC,CACnD,GACF,CAAC,eAAe,CAAC,KACjB,CAqBF,CAAC;AAEF,eAAO,MAAM,0BAA0B;;;;;;EAGJ,CAAC;AACpC,eAAO,MAAM,gCAAgC,EAC3C,4CAAqD,CAAC;AAExD,eAAO,MAAM,4BAA4B;;;;;;;;;;;;;;;;;EASN,CAAC;AACpC,eAAO,MAAM,kCAAkC,EAC7C,8CAAuD,CAAC;AAC1D,MAAM,MAAM,wBAAwB,GAAG,2BAA2B,CAAC;IACjE,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,kCAAkC,CAAC;IACxD,UAAU,EAAE,OAAO,4BAA4B,CAAC;CACjD,CAAC,CAAC;AAEH,eAAO,MAAM,iCAAiC;;;;;;;;;;;;;;;;;EASX,CAAC;AACpC,eAAO,MAAM,uCAAuC,EAClD,mDAA4D,CAAC;AAC/D,MAAM,MAAM,6BAA6B,GAAG,2BAA2B,CAAC;IACtE,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,uCAAuC,CAAC;IAC7D,UAAU,EAAE,OAAO,iCAAiC,CAAC;CACtD,CAAC,CAAC;AAEH,eAAO,MAAM,kCAAkC;;;;;;EAGZ,CAAC;AACpC,eAAO,MAAM,wCAAwC,EACnD,oDAA6D,CAAC;AAChE,MAAM,MAAM,8BAA8B,GAAG,2BAA2B,CAAC;IACvE,IAAI,EAAE,OAAO,CAAC;IACd,YAAY,EAAE,OAAO,wCAAwC,CAAC;IAC9D,UAAU,EAAE,OAAO,kCAAkC,CAAC;CACvD,CAAC,CAAC;AAEH,eAAO,MAAM,wCAAwC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBlB,CAAC;AACpC,MAAM,MAAM,gCAAgC,GAAG,2BAA2B,CAAC;IACzE,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,wCAAwC,CAAC;CAC7D,CAAC,CAAC;AAEH,eAAO,MAAM,6CAA6C;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAiBvB,CAAC;AACpC,MAAM,MAAM,qCAAqC,GAC/C,2BAA2B,CAAC;IAC1B,IAAI,EAAE,OAAO,CAAC;IACd,UAAU,EAAE,OAAO,6CAA6C,CAAC;CAClE,CAAC,CAAC;AAEL,eAAO,MAAM,wCAAwC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAMrB,CAAC;AAEjC,eAAO,MAAM,yCAAyC;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;CAMtB,CAAC"}
|
|
@@ -27,7 +27,7 @@ import {
|
|
|
27
27
|
permitWitnessTransferFromTypes,
|
|
28
28
|
TokenPermissionTypeParams,
|
|
29
29
|
} from './signatureTransfer.js';
|
|
30
|
-
import { sameEvmAddress } from '../address.js';
|
|
30
|
+
import { isEvmAddressShape, sameEvmAddress } from '../address.js';
|
|
31
31
|
|
|
32
32
|
const PrimaryTypes = keyMirror({
|
|
33
33
|
PermitBatchWitnessTransferFrom: null,
|
|
@@ -158,6 +158,9 @@ export function validatePermit2Domain(
|
|
|
158
158
|
`Permit2 domain must include chainId and verifyingContract`,
|
|
159
159
|
);
|
|
160
160
|
}
|
|
161
|
+
if (!isEvmAddressShape(verifyingContract)) {
|
|
162
|
+
throw new Error(`Invalid verifying contract address in permit2 domain`);
|
|
163
|
+
}
|
|
161
164
|
|
|
162
165
|
if (permit2Addresses) {
|
|
163
166
|
const chainIdStr = String(chainId);
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* per-message: dropped from `types` if never present on any instance of
|
|
6
6
|
* the struct, otherwise kept with the `optional` marker stripped (treated
|
|
7
7
|
* as required from then on).
|
|
8
|
-
* - struct types
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* - struct types not referenced from `primaryType` through kept fields
|
|
9
|
+
* (e.g. only through a dropped `optional` field) are absent from
|
|
10
|
+
* `types`. Referenced ones are kept even when no value reaches them (e.g.
|
|
11
|
+
* the element type of an empty array), since EIP-712 hashing encodes
|
|
12
|
+
* their definitions regardless. `EIP712Domain` is always preserved if
|
|
13
|
+
* present, since domain values live outside `message`.
|
|
11
14
|
*
|
|
12
15
|
* `onExtraField` controls how *excess* data (fields not declared in
|
|
13
16
|
* `types`, array elements beyond a fixed length) is handled:
|
|
@@ -19,6 +22,21 @@
|
|
|
19
22
|
* data). Dynamic-length arrays are never truncated in any mode, since
|
|
20
23
|
* every element affects the EIP-712 hash.
|
|
21
24
|
*
|
|
25
|
+
* `onNonCanonicalValue` controls how a valid primitive leaf value that
|
|
26
|
+
* isn't in its canonical form is handled. Only integers (given as a
|
|
27
|
+
* `number`, `bigint`, or decimal/hex string -- see `normalizeEIP712Primitive`)
|
|
28
|
+
* have a distinct canonical form: a `number` for an explicit width of at
|
|
29
|
+
* most 48 bits (e.g. `uint32`), else a `bigint` (e.g. `uint64`,
|
|
30
|
+
* `uint256`), matching abitype's TS types.
|
|
31
|
+
* - 'normalize' (default, except with `onExtraField: 'keep'`): replace it
|
|
32
|
+
* with its canonical form. Since `onExtraField: 'keep'` never modifies
|
|
33
|
+
* `message`, combining it with an explicit `onNonCanonicalValue:
|
|
34
|
+
* 'normalize'` is an error (the call throws).
|
|
35
|
+
* - 'keep' (default with `onExtraField: 'keep'`): leave it as is.
|
|
36
|
+
* - 'throw': reject it -- e.g. to check that an already-normalized message
|
|
37
|
+
* also has the expected JS types for a different (trusted) set of
|
|
38
|
+
* `types`, which a field declared with another type would not.
|
|
39
|
+
*
|
|
22
40
|
* Regardless of `onExtraField`, the result is also *validated*, with the
|
|
23
41
|
* goal that if validation succeeds, hashing the result is guaranteed to
|
|
24
42
|
* succeed and to hash exactly the values given:
|
|
@@ -26,10 +44,12 @@
|
|
|
26
44
|
* least their declared number of elements (too few is missing data, not
|
|
27
45
|
* excess, so no mode tolerates it; too many is excess, tolerated only by
|
|
28
46
|
* `'keep'`).
|
|
47
|
+
* - a struct type reached from `primaryType` must not declare the same
|
|
48
|
+
* field more than once (hashing would encode every declaration).
|
|
29
49
|
* - a struct/array field must get an array/object value, and a plain
|
|
30
50
|
* Solidity type (`address`, `bool`, `string`, `uint*`/`int*`, `bytes*`)
|
|
31
51
|
* must get a value of the specific JS type that implies -- see
|
|
32
|
-
* `
|
|
52
|
+
* `normalizeEIP712Primitive`.
|
|
33
53
|
* - primitive leaf values are checked against their type's shape/range,
|
|
34
54
|
* recursing correctly through arrays, which real EIP-712 tooling (e.g.
|
|
35
55
|
* viem's `validateTypedData`) does not.
|
|
@@ -54,11 +74,64 @@ export type NormalizeAndValidateEIP712DataInput = {
|
|
|
54
74
|
export type NormalizeAndValidateEIP712DataOptions = {
|
|
55
75
|
/** default 'drop' */
|
|
56
76
|
onExtraField?: 'drop' | 'throw' | 'keep';
|
|
77
|
+
/**
|
|
78
|
+
* How to handle a valid value not in its canonical form (an integer that
|
|
79
|
+
* isn't a `number` if at most 48 bits wide, else a `bigint`). Default
|
|
80
|
+
* 'normalize', except with `onExtraField: 'keep'`, where it defaults to
|
|
81
|
+
* 'keep' instead: 'keep' mode never modifies `message`, so explicitly
|
|
82
|
+
* passing 'normalize' with it is an error (the call throws).
|
|
83
|
+
*/
|
|
84
|
+
onNonCanonicalValue?: 'normalize' | 'keep' | 'throw';
|
|
57
85
|
};
|
|
58
86
|
export type NormalizeAndValidateEIP712DataResult = {
|
|
59
87
|
message: Record<string, unknown>;
|
|
60
88
|
types: TypesRecord;
|
|
61
89
|
};
|
|
90
|
+
/**
|
|
91
|
+
* Parses an integer given in one of the JS representations accepted for an
|
|
92
|
+
* EIP-712 `(u)int<M>` value: a `bigint`, a safe integer `number`, or a
|
|
93
|
+
* string of unsigned hex (`0x...`) or optionally signed decimal digits (see
|
|
94
|
+
* `INTEGER_STRING_REGEX`). Returns `undefined` for anything else, including
|
|
95
|
+
* non-integer and unsafe-integer numbers (which have likely already lost
|
|
96
|
+
* precision; ethers.js rejects them too). Does not check the value's range
|
|
97
|
+
* for any particular type: see {@link normalizeEIP712Primitive} for that.
|
|
98
|
+
*
|
|
99
|
+
* @param value
|
|
100
|
+
*/
|
|
101
|
+
export declare const parseEIP712Integer: (value: unknown) => bigint | undefined;
|
|
102
|
+
/**
|
|
103
|
+
* Validates a value of a primitive (non-struct, non-array) EIP-712 type,
|
|
104
|
+
* returning its canonical form (only integers have a distinct one: a
|
|
105
|
+
* `number` for an explicit width of at most 48 bits, else a `bigint`,
|
|
106
|
+
* matching abitype's TS types), or throws if it isn't valid. A struct or
|
|
107
|
+
* array type is rejected as unrecognized (`visit` only calls this once it's
|
|
108
|
+
* confirmed `fieldType` is neither).
|
|
109
|
+
* Requires the specific JS type real hashing needs for each Solidity type,
|
|
110
|
+
* not just something that happens to coerce -- e.g. a number given for a
|
|
111
|
+
* `string` field would otherwise hash as the number's own hex encoding,
|
|
112
|
+
* not the string form, a footgun real EIP-712 tooling doesn't catch either.
|
|
113
|
+
*
|
|
114
|
+
* - `address`: string, 20-byte-hex shape (not full EIP-55 checksum -- see
|
|
115
|
+
* the module doc comment for why).
|
|
116
|
+
* - `bool`: JS `boolean`.
|
|
117
|
+
* - `string`: JS `string`.
|
|
118
|
+
* - `bytes` (dynamic): even-length hex string *or* `Uint8Array` -- hashed
|
|
119
|
+
* identically either way.
|
|
120
|
+
* - `bytes<M>` (fixed): even-length hex string only, of exactly `M` bytes
|
|
121
|
+
* -- a `Uint8Array` does not hash successfully here (see the module doc
|
|
122
|
+
* comment). Odd-length hex is rejected for both: ethers.js rejects it,
|
|
123
|
+
* and viem pads the missing nibble on the left for `bytes` but on the
|
|
124
|
+
* right for `bytes<M>`.
|
|
125
|
+
* - `uint<M>`/`int<M>` (not bare `uint`/`int`, see `INTEGER_TYPE_REGEX`):
|
|
126
|
+
* any value {@link parseEIP712Integer} accepts, in range for the bit
|
|
127
|
+
* width/signedness.
|
|
128
|
+
* - anything else: not a real Solidity primitive type, so rejected
|
|
129
|
+
* unconditionally.
|
|
130
|
+
*
|
|
131
|
+
* @param fieldType
|
|
132
|
+
* @param value
|
|
133
|
+
*/
|
|
134
|
+
export declare const normalizeEIP712Primitive: (fieldType: string, value: unknown) => unknown;
|
|
62
135
|
export declare const normalizeAndValidateEIP712Data: (input: NormalizeAndValidateEIP712DataInput, options?: NormalizeAndValidateEIP712DataOptions) => NormalizeAndValidateEIP712DataResult;
|
|
63
136
|
export {};
|
|
64
137
|
//# sourceMappingURL=eip712-normalize.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"eip712-normalize.d.ts","sourceRoot":"","sources":["eip712-normalize.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"eip712-normalize.d.ts","sourceRoot":"","sources":["eip712-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAiEG;AACH,OAAO,KAAK,EAAE,kBAAkB,EAAE,MAAM,eAAe,CAAC;AAExD,KAAK,WAAW,GAAG,MAAM,CAAC,MAAM,EAAE,SAAS,kBAAkB,EAAE,CAAC,CAAC;AAEjE,MAAM,MAAM,mCAAmC,GAAG;IAChD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,WAAW,CAAC;IACnB,WAAW,EAAE,MAAM,CAAC;CACrB,CAAC;AAEF,MAAM,MAAM,qCAAqC,GAAG;IAClD,qBAAqB;IACrB,YAAY,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,MAAM,CAAC;IACzC;;;;;;OAMG;IACH,mBAAmB,CAAC,EAAE,WAAW,GAAG,MAAM,GAAG,OAAO,CAAC;CACtD,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,WAAW,CAAC;CACpB,CAAC;AAsIF;;;;;;;;;;GAUG;AACH,eAAO,MAAM,kBAAkB,GAAI,OAAO,OAAO,KAAG,MAAM,GAAG,SAc5D,CAAC;AAeF;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA+BG;AACH,eAAO,MAAM,wBAAwB,GACnC,WAAW,MAAM,EACjB,OAAO,OAAO,KACb,OAgFF,CAAC;AAwLF,eAAO,MAAM,8BAA8B,GACzC,OAAO,mCAAmC,EAC1C,UAAS,qCAA0C,KAClD,oCA+EF,CAAC"}
|
|
@@ -5,9 +5,12 @@
|
|
|
5
5
|
* per-message: dropped from `types` if never present on any instance of
|
|
6
6
|
* the struct, otherwise kept with the `optional` marker stripped (treated
|
|
7
7
|
* as required from then on).
|
|
8
|
-
* - struct types
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* - struct types not referenced from `primaryType` through kept fields
|
|
9
|
+
* (e.g. only through a dropped `optional` field) are absent from
|
|
10
|
+
* `types`. Referenced ones are kept even when no value reaches them (e.g.
|
|
11
|
+
* the element type of an empty array), since EIP-712 hashing encodes
|
|
12
|
+
* their definitions regardless. `EIP712Domain` is always preserved if
|
|
13
|
+
* present, since domain values live outside `message`.
|
|
11
14
|
*
|
|
12
15
|
* `onExtraField` controls how *excess* data (fields not declared in
|
|
13
16
|
* `types`, array elements beyond a fixed length) is handled:
|
|
@@ -19,6 +22,21 @@
|
|
|
19
22
|
* data). Dynamic-length arrays are never truncated in any mode, since
|
|
20
23
|
* every element affects the EIP-712 hash.
|
|
21
24
|
*
|
|
25
|
+
* `onNonCanonicalValue` controls how a valid primitive leaf value that
|
|
26
|
+
* isn't in its canonical form is handled. Only integers (given as a
|
|
27
|
+
* `number`, `bigint`, or decimal/hex string -- see `normalizeEIP712Primitive`)
|
|
28
|
+
* have a distinct canonical form: a `number` for an explicit width of at
|
|
29
|
+
* most 48 bits (e.g. `uint32`), else a `bigint` (e.g. `uint64`,
|
|
30
|
+
* `uint256`), matching abitype's TS types.
|
|
31
|
+
* - 'normalize' (default, except with `onExtraField: 'keep'`): replace it
|
|
32
|
+
* with its canonical form. Since `onExtraField: 'keep'` never modifies
|
|
33
|
+
* `message`, combining it with an explicit `onNonCanonicalValue:
|
|
34
|
+
* 'normalize'` is an error (the call throws).
|
|
35
|
+
* - 'keep' (default with `onExtraField: 'keep'`): leave it as is.
|
|
36
|
+
* - 'throw': reject it -- e.g. to check that an already-normalized message
|
|
37
|
+
* also has the expected JS types for a different (trusted) set of
|
|
38
|
+
* `types`, which a field declared with another type would not.
|
|
39
|
+
*
|
|
22
40
|
* Regardless of `onExtraField`, the result is also *validated*, with the
|
|
23
41
|
* goal that if validation succeeds, hashing the result is guaranteed to
|
|
24
42
|
* succeed and to hash exactly the values given:
|
|
@@ -26,10 +44,12 @@
|
|
|
26
44
|
* least their declared number of elements (too few is missing data, not
|
|
27
45
|
* excess, so no mode tolerates it; too many is excess, tolerated only by
|
|
28
46
|
* `'keep'`).
|
|
47
|
+
* - a struct type reached from `primaryType` must not declare the same
|
|
48
|
+
* field more than once (hashing would encode every declaration).
|
|
29
49
|
* - a struct/array field must get an array/object value, and a plain
|
|
30
50
|
* Solidity type (`address`, `bool`, `string`, `uint*`/`int*`, `bytes*`)
|
|
31
51
|
* must get a value of the specific JS type that implies -- see
|
|
32
|
-
* `
|
|
52
|
+
* `normalizeEIP712Primitive`.
|
|
33
53
|
* - primitive leaf values are checked against their type's shape/range,
|
|
34
54
|
* recursing correctly through arrays, which real EIP-712 tooling (e.g.
|
|
35
55
|
* viem's `validateTypedData`) does not.
|
|
@@ -57,6 +77,14 @@
|
|
|
57
77
|
|
|
58
78
|
|
|
59
79
|
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
60
88
|
|
|
61
89
|
|
|
62
90
|
|
|
@@ -65,6 +93,7 @@
|
|
|
65
93
|
|
|
66
94
|
|
|
67
95
|
const ARRAY_SUFFIX = /\[(\d*)\]$/u;
|
|
96
|
+
const ARRAY_SUFFIXES = /(?:\[\d*\])+$/u;
|
|
68
97
|
|
|
69
98
|
const splitArrayType = (
|
|
70
99
|
type ,
|
|
@@ -78,17 +107,38 @@ const splitArrayType = (
|
|
|
78
107
|
};
|
|
79
108
|
};
|
|
80
109
|
|
|
110
|
+
// Same shape as `isEvmAddressShape` in `../address.js`, duplicated rather
|
|
111
|
+
// than imported: this module must have no runtime imports (see the module
|
|
112
|
+
// doc comment; `eip712-normalize.xs.test.js` also evaluates its source
|
|
113
|
+
// directly), and `address.js` has runtime dependencies of its own.
|
|
81
114
|
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/u;
|
|
82
115
|
// Solidity `(u)int<M>`: (un)signed integer of `M` bits, `0 < M <= 256`,
|
|
83
|
-
// `M % 8 === 0
|
|
84
|
-
//
|
|
85
|
-
//
|
|
116
|
+
// `M % 8 === 0`. Unlike viem's `integerRegex` (utils/regex.ts), the width
|
|
117
|
+
// is required: EIP-712 has no bare `uint`/`int` aliases, and viem and
|
|
118
|
+
// ethers.js hash them differently (ethers.js encodes the type as
|
|
119
|
+
// `uint256`/`int256`, viem keeps the alias in the type string).
|
|
86
120
|
const INTEGER_TYPE_REGEX =
|
|
87
|
-
/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)
|
|
121
|
+
/^(u?int)(8|16|24|32|40|48|56|64|72|80|88|96|104|112|120|128|136|144|152|160|168|176|184|192|200|208|216|224|232|240|248|256)$/u;
|
|
88
122
|
// Solidity `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`; bare `bytes`
|
|
89
123
|
// (no explicit size) is dynamic-length, so has nothing to check here.
|
|
90
124
|
const BYTES_TYPE_REGEX = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/u;
|
|
91
125
|
const HEX_REGEX = /^0x[0-9a-fA-F]*$/u;
|
|
126
|
+
// String-encoded integers: unsigned hex, or decimal with an optional sign.
|
|
127
|
+
// The subset of what both viem and ethers.js accept *and* hash identically
|
|
128
|
+
// to the equivalent `bigint` (both ultimately defer to JS `BigInt(string)`,
|
|
129
|
+
// but e.g. viem rejects negative hex that ethers accepts, and both accept
|
|
130
|
+
// whitespace, `0b`/`0o` prefixes, etc. that we don't need to). Decimal
|
|
131
|
+
// excludes leading zeros, which `BigInt` ignores but a reader could take
|
|
132
|
+
// for a legacy octal literal (e.g. `041` as 33 rather than 41), and `-0`,
|
|
133
|
+
// which has no distinct meaning (`0` and `+0` are fine). Hex may have
|
|
134
|
+
// leading zeros, as fixed-width hex commonly does.
|
|
135
|
+
const INTEGER_STRING_REGEX = /^(?:\+?0|[-+]?[1-9][0-9]*|0x[0-9a-fA-F]+)$/u;
|
|
136
|
+
// Bounds the work `BigInt` parsing of an untrusted string can do. Generous
|
|
137
|
+
// for any in-range 256-bit value (at most 78 decimal or 64 hex digits),
|
|
138
|
+
// leaving room for some leading zeros in hex.
|
|
139
|
+
const MAX_INTEGER_STRING_LENGTH = 100;
|
|
140
|
+
// abitype types `(u)int<M>` values as `number` for `M <= 48`, else `bigint`.
|
|
141
|
+
const MAX_NUMBER_INTEGER_BITS = 48;
|
|
92
142
|
|
|
93
143
|
const MAX_DESCRIBED_LENGTH = 100;
|
|
94
144
|
|
|
@@ -175,8 +225,52 @@ const describeValue = (value ) => {
|
|
|
175
225
|
};
|
|
176
226
|
|
|
177
227
|
/**
|
|
178
|
-
*
|
|
179
|
-
* (
|
|
228
|
+
* Parses an integer given in one of the JS representations accepted for an
|
|
229
|
+
* EIP-712 `(u)int<M>` value: a `bigint`, a safe integer `number`, or a
|
|
230
|
+
* string of unsigned hex (`0x...`) or optionally signed decimal digits (see
|
|
231
|
+
* `INTEGER_STRING_REGEX`). Returns `undefined` for anything else, including
|
|
232
|
+
* non-integer and unsafe-integer numbers (which have likely already lost
|
|
233
|
+
* precision; ethers.js rejects them too). Does not check the value's range
|
|
234
|
+
* for any particular type: see {@link normalizeEIP712Primitive} for that.
|
|
235
|
+
*
|
|
236
|
+
* @param value
|
|
237
|
+
*/
|
|
238
|
+
export const parseEIP712Integer = (value ) => {
|
|
239
|
+
if (typeof value === 'bigint') return value;
|
|
240
|
+
if (typeof value === 'number') {
|
|
241
|
+
return Number.isSafeInteger(value) ? BigInt(value) : undefined;
|
|
242
|
+
}
|
|
243
|
+
if (
|
|
244
|
+
typeof value !== 'string' ||
|
|
245
|
+
value.length > MAX_INTEGER_STRING_LENGTH ||
|
|
246
|
+
!INTEGER_STRING_REGEX.test(value)
|
|
247
|
+
) {
|
|
248
|
+
return undefined;
|
|
249
|
+
}
|
|
250
|
+
// `BigInt` itself accepts a sign on decimal (not hex) strings.
|
|
251
|
+
return BigInt(value);
|
|
252
|
+
};
|
|
253
|
+
|
|
254
|
+
/**
|
|
255
|
+
* Whether `type` is a primitive EIP-712 type, i.e. one whose values
|
|
256
|
+
* {@link normalizeEIP712Primitive} accepts (not, e.g., a bare `uint`).
|
|
257
|
+
*
|
|
258
|
+
* @param type
|
|
259
|
+
*/
|
|
260
|
+
const isPrimitiveType = (type ) =>
|
|
261
|
+
type === 'address' ||
|
|
262
|
+
type === 'bool' ||
|
|
263
|
+
type === 'string' ||
|
|
264
|
+
INTEGER_TYPE_REGEX.test(type) ||
|
|
265
|
+
BYTES_TYPE_REGEX.test(type);
|
|
266
|
+
|
|
267
|
+
/**
|
|
268
|
+
* Validates a value of a primitive (non-struct, non-array) EIP-712 type,
|
|
269
|
+
* returning its canonical form (only integers have a distinct one: a
|
|
270
|
+
* `number` for an explicit width of at most 48 bits, else a `bigint`,
|
|
271
|
+
* matching abitype's TS types), or throws if it isn't valid. A struct or
|
|
272
|
+
* array type is rejected as unrecognized (`visit` only calls this once it's
|
|
273
|
+
* confirmed `fieldType` is neither).
|
|
180
274
|
* Requires the specific JS type real hashing needs for each Solidity type,
|
|
181
275
|
* not just something that happens to coerce -- e.g. a number given for a
|
|
182
276
|
* `string` field would otherwise hash as the number's own hex encoding,
|
|
@@ -186,24 +280,33 @@ const describeValue = (value ) => {
|
|
|
186
280
|
* the module doc comment for why).
|
|
187
281
|
* - `bool`: JS `boolean`.
|
|
188
282
|
* - `string`: JS `string`.
|
|
189
|
-
* - `bytes` (dynamic): hex string *or* `Uint8Array` -- hashed
|
|
190
|
-
* either way.
|
|
191
|
-
* - `bytes<M>` (fixed): hex string only, of exactly `M` bytes
|
|
192
|
-
* `Uint8Array` does not hash successfully here (see the module doc
|
|
193
|
-
* comment).
|
|
194
|
-
*
|
|
195
|
-
*
|
|
283
|
+
* - `bytes` (dynamic): even-length hex string *or* `Uint8Array` -- hashed
|
|
284
|
+
* identically either way.
|
|
285
|
+
* - `bytes<M>` (fixed): even-length hex string only, of exactly `M` bytes
|
|
286
|
+
* -- a `Uint8Array` does not hash successfully here (see the module doc
|
|
287
|
+
* comment). Odd-length hex is rejected for both: ethers.js rejects it,
|
|
288
|
+
* and viem pads the missing nibble on the left for `bytes` but on the
|
|
289
|
+
* right for `bytes<M>`.
|
|
290
|
+
* - `uint<M>`/`int<M>` (not bare `uint`/`int`, see `INTEGER_TYPE_REGEX`):
|
|
291
|
+
* any value {@link parseEIP712Integer} accepts, in range for the bit
|
|
292
|
+
* width/signedness.
|
|
196
293
|
* - anything else: not a real Solidity primitive type, so rejected
|
|
197
294
|
* unconditionally.
|
|
295
|
+
*
|
|
296
|
+
* @param fieldType
|
|
297
|
+
* @param value
|
|
198
298
|
*/
|
|
199
|
-
const
|
|
299
|
+
export const normalizeEIP712Primitive = (
|
|
300
|
+
fieldType ,
|
|
301
|
+
value ,
|
|
302
|
+
) => {
|
|
200
303
|
const quotedType = quoteName(fieldType);
|
|
201
304
|
|
|
202
305
|
if (fieldType === 'address') {
|
|
203
306
|
if (typeof value !== 'string' || !ADDRESS_REGEX.test(value)) {
|
|
204
307
|
throw new Error(`Invalid EIP-712 address value: ${describeValue(value)}`);
|
|
205
308
|
}
|
|
206
|
-
return;
|
|
309
|
+
return value;
|
|
207
310
|
}
|
|
208
311
|
|
|
209
312
|
if (fieldType === 'bool') {
|
|
@@ -212,7 +315,7 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
212
315
|
`Expected a boolean for EIP-712 type "bool", got ${describeValue(value)}`,
|
|
213
316
|
);
|
|
214
317
|
}
|
|
215
|
-
return;
|
|
318
|
+
return value;
|
|
216
319
|
}
|
|
217
320
|
|
|
218
321
|
if (fieldType === 'string') {
|
|
@@ -221,24 +324,19 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
221
324
|
`Expected a string for EIP-712 type "string", got ${describeValue(value)}`,
|
|
222
325
|
);
|
|
223
326
|
}
|
|
224
|
-
return;
|
|
327
|
+
return value;
|
|
225
328
|
}
|
|
226
329
|
|
|
227
330
|
const integerMatch = fieldType.match(INTEGER_TYPE_REGEX);
|
|
228
331
|
if (integerMatch) {
|
|
229
|
-
|
|
332
|
+
const bigValue = parseEIP712Integer(value);
|
|
333
|
+
if (bigValue === undefined) {
|
|
230
334
|
throw new Error(
|
|
231
|
-
`Expected
|
|
232
|
-
);
|
|
233
|
-
}
|
|
234
|
-
if (typeof value === 'number' && !Number.isInteger(value)) {
|
|
235
|
-
throw new Error(
|
|
236
|
-
`Expected an integer for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
335
|
+
`Expected an integer (bigint, safe integer number, or decimal/hex string) for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
237
336
|
);
|
|
238
337
|
}
|
|
239
338
|
const signed = integerMatch[1] === 'int';
|
|
240
|
-
const bits =
|
|
241
|
-
const bigValue = BigInt(value);
|
|
339
|
+
const bits = Number(integerMatch[2]);
|
|
242
340
|
const max = signed ? 2n ** BigInt(bits - 1) - 1n : 2n ** BigInt(bits) - 1n;
|
|
243
341
|
const min = signed ? -max - 1n : 0n;
|
|
244
342
|
if (bigValue < min || bigValue > max) {
|
|
@@ -246,7 +344,9 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
246
344
|
`Value ${describeValue(value)} is out of range for EIP-712 type ${quotedType} (expected ${min} to ${max})`,
|
|
247
345
|
);
|
|
248
346
|
}
|
|
249
|
-
|
|
347
|
+
// Match abitype's (and so viem's) TS types: an explicit width of at most
|
|
348
|
+
// 48 bits is a `number` (always exact, being under 2^53), else `bigint`.
|
|
349
|
+
return bits <= MAX_NUMBER_INTEGER_BITS ? Number(bigValue) : bigValue;
|
|
250
350
|
}
|
|
251
351
|
|
|
252
352
|
const bytesMatch = fieldType.match(BYTES_TYPE_REGEX);
|
|
@@ -255,22 +355,27 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
255
355
|
// `Uint8Array` interchangeably with a hex string. Fixed `bytes<M>` goes
|
|
256
356
|
// through the generic Solidity ABI path instead, which requires a hex
|
|
257
357
|
// string and throws an unrelated error for a `Uint8Array`.
|
|
258
|
-
if (!bytesMatch[1] && value instanceof Uint8Array) return;
|
|
358
|
+
if (!bytesMatch[1] && value instanceof Uint8Array) return value;
|
|
259
359
|
if (typeof value !== 'string' || !HEX_REGEX.test(value)) {
|
|
260
360
|
throw new Error(
|
|
261
361
|
`Expected a hex string for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
262
362
|
);
|
|
263
363
|
}
|
|
364
|
+
if (value.length % 2 !== 0) {
|
|
365
|
+
throw new Error(
|
|
366
|
+
`Expected an even number of hex digits for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
367
|
+
);
|
|
368
|
+
}
|
|
264
369
|
if (bytesMatch[1]) {
|
|
265
370
|
const expectedSize = Number(bytesMatch[1]);
|
|
266
|
-
const actualSize =
|
|
371
|
+
const actualSize = (value.length - 2) / 2;
|
|
267
372
|
if (actualSize !== expectedSize) {
|
|
268
373
|
throw new Error(
|
|
269
374
|
`Expected EIP-712 type ${quotedType} to be ${expectedSize} bytes, got ${actualSize}: ${describeValue(value)}`,
|
|
270
375
|
);
|
|
271
376
|
}
|
|
272
377
|
}
|
|
273
|
-
return;
|
|
378
|
+
return value;
|
|
274
379
|
}
|
|
275
380
|
|
|
276
381
|
throw new Error(`Unrecognized EIP-712 type ${quotedType}`);
|
|
@@ -295,8 +400,9 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
295
400
|
|
|
296
401
|
/**
|
|
297
402
|
* Walks `value` against `fieldType` (a struct or array field type string).
|
|
298
|
-
* One function covers both,
|
|
299
|
-
*
|
|
403
|
+
* One function covers both, an array of arrays (e.g. `uint256[2][]`)
|
|
404
|
+
* included: each element is visited against the element type, itself an
|
|
405
|
+
* array type (e.g. `uint256[2]`). `getType` hands out the *same* {@link TypeFields}
|
|
300
406
|
* record for every instance of a given struct type name encountered
|
|
301
407
|
* anywhere in the message, which is what lets a single walk resolve
|
|
302
408
|
* `optional` fields and validate required-field presence across
|
|
@@ -315,13 +421,14 @@ const assertValidPrimitive = (fieldType , value ) => {
|
|
|
315
421
|
* Every mode runs these the same way, `keep` included -- missing data is
|
|
316
422
|
* missing data regardless of `onExtraField`. `keep` only skips removing or
|
|
317
423
|
* rejecting *excess* data (extra fields, over-length arrays) and never
|
|
318
|
-
* touches `message
|
|
424
|
+
* touches `message` (so `onNonCanonicalValue` is never 'normalize').
|
|
319
425
|
*/
|
|
320
426
|
const visit = (
|
|
321
427
|
fieldType ,
|
|
322
428
|
value ,
|
|
323
429
|
getType ,
|
|
324
430
|
onExtraField ,
|
|
431
|
+
onNonCanonicalValue ,
|
|
325
432
|
) => {
|
|
326
433
|
const keep = onExtraField === 'keep';
|
|
327
434
|
const { base, length: requiredLength, isArray } = splitArrayType(fieldType);
|
|
@@ -331,8 +438,14 @@ const visit = (
|
|
|
331
438
|
if (!isArray && !baseType) {
|
|
332
439
|
// Not a declared struct or array, so it must be a recognized Solidity
|
|
333
440
|
// primitive with a matching JS value; also catches an unknown type.
|
|
334
|
-
|
|
335
|
-
return value;
|
|
441
|
+
const canonical = normalizeEIP712Primitive(fieldType, value);
|
|
442
|
+
if (canonical === value || onNonCanonicalValue === 'keep') return value;
|
|
443
|
+
if (onNonCanonicalValue === 'throw') {
|
|
444
|
+
throw new Error(
|
|
445
|
+
`Expected a ${typeof canonical} for EIP-712 type ${quoteName(fieldType)}, got ${describeValue(value)}`,
|
|
446
|
+
);
|
|
447
|
+
}
|
|
448
|
+
return canonical;
|
|
336
449
|
}
|
|
337
450
|
|
|
338
451
|
// A declared struct or array needs an actual object/array value. Split
|
|
@@ -443,6 +556,7 @@ const visit = (
|
|
|
443
556
|
value[fieldName],
|
|
444
557
|
getType,
|
|
445
558
|
onExtraField,
|
|
559
|
+
onNonCanonicalValue,
|
|
446
560
|
);
|
|
447
561
|
if (!keep) result[fieldName] = projectedValue;
|
|
448
562
|
}
|
|
@@ -455,6 +569,14 @@ export const normalizeAndValidateEIP712Data = (
|
|
|
455
569
|
) => {
|
|
456
570
|
const { message, types, primaryType } = input;
|
|
457
571
|
const onExtraField = options.onExtraField ?? 'drop';
|
|
572
|
+
const onNonCanonicalValue =
|
|
573
|
+
options.onNonCanonicalValue ??
|
|
574
|
+
(onExtraField === 'keep' ? 'keep' : 'normalize');
|
|
575
|
+
if (onNonCanonicalValue === 'normalize' && onExtraField === 'keep') {
|
|
576
|
+
throw new Error(
|
|
577
|
+
`EIP-712 normalization option onNonCanonicalValue: 'normalize' is incompatible with onExtraField: 'keep', which never modifies the message`,
|
|
578
|
+
);
|
|
579
|
+
}
|
|
458
580
|
|
|
459
581
|
if (!(primaryType in types)) {
|
|
460
582
|
throw new Error(
|
|
@@ -474,6 +596,14 @@ export const normalizeAndValidateEIP712Data = (
|
|
|
474
596
|
// an inherited Object.prototype member (e.g. a field named "toString").
|
|
475
597
|
const fields = Object.create(null);
|
|
476
598
|
for (const field of declared) {
|
|
599
|
+
// EIP-712 hashing encodes every declaration, while `fields` (and so
|
|
600
|
+
// the output types) could only hold one, describing a different type
|
|
601
|
+
// hash than the input.
|
|
602
|
+
if (field.name in fields) {
|
|
603
|
+
throw new Error(
|
|
604
|
+
`Duplicate field ${quoteName(field.name)} in EIP-712 type ${quoteName(typeName)}`,
|
|
605
|
+
);
|
|
606
|
+
}
|
|
477
607
|
fields[field.name] = {
|
|
478
608
|
state: field.optional ? 'optional-unseen' : 'required',
|
|
479
609
|
type: field.type,
|
|
@@ -488,13 +618,29 @@ export const normalizeAndValidateEIP712Data = (
|
|
|
488
618
|
message,
|
|
489
619
|
getType,
|
|
490
620
|
onExtraField,
|
|
621
|
+
onNonCanonicalValue,
|
|
491
622
|
) ;
|
|
492
623
|
|
|
624
|
+
// Output only the fields still required (resolved `optional` ones are
|
|
625
|
+
// dropped). The walk only reaches what the values lead to: e.g. nothing
|
|
626
|
+
// in an empty array. But EIP-712 hashing encodes every struct type a kept
|
|
627
|
+
// field references (transitively) whatever the values, so register those
|
|
628
|
+
// too (iterating a `Map` also visits the entries added while iterating),
|
|
629
|
+
// and check that every other referenced type is a recognized primitive
|
|
630
|
+
// (e.g. not a bare `uint`).
|
|
493
631
|
const outputTypes = {};
|
|
494
632
|
for (const [typeName, fields] of resultTypes) {
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
633
|
+
const outputFields = [];
|
|
634
|
+
for (const fieldName of Object.keys(fields)) {
|
|
635
|
+
const { state, type } = fields[fieldName];
|
|
636
|
+
if (state !== 'required') continue;
|
|
637
|
+
const elementType = type.replace(ARRAY_SUFFIXES, '');
|
|
638
|
+
if (!getType(elementType) && !isPrimitiveType(elementType)) {
|
|
639
|
+
throw new Error(`Unrecognized EIP-712 type ${quoteName(elementType)}`);
|
|
640
|
+
}
|
|
641
|
+
outputFields.push({ name: fieldName, type });
|
|
642
|
+
}
|
|
643
|
+
outputTypes[typeName] = outputFields;
|
|
498
644
|
}
|
|
499
645
|
if ('EIP712Domain' in types) outputTypes.EIP712Domain = types.EIP712Domain;
|
|
500
646
|
|