@agoric/orchestration 0.1.1-dev-94f7dd3.0.94f7dd3 → 0.1.1-dev-0cd653b.0.0cd653b
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 +16 -16
- package/src/utils/abitype.d.ts +62 -1
- package/src/utils/abitype.d.ts.map +1 -1
- package/src/utils/abitype.js +89 -1
- package/src/utils/permit2/signatureTransfer.d.ts +3 -4
- package/src/utils/permit2/signatureTransfer.d.ts.map +1 -1
- package/src/utils/permit2/signatureTransfer.js +3 -4
- package/src/utils/permit2/signatureTransferHelpers.d.ts.map +1 -1
- package/src/utils/permit2/signatureTransferHelpers.js +7 -2
- package/src/utils/viem-utils/eip712-normalize.d.ts +64 -0
- package/src/utils/viem-utils/eip712-normalize.d.ts.map +1 -0
- package/src/utils/viem-utils/eip712-normalize.js +501 -0
- package/src/utils/viem.d.ts +1 -0
- package/src/utils/viem.d.ts.map +1 -1
- package/src/utils/viem.js +1 -0
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@agoric/orchestration",
|
|
3
|
-
"version": "0.1.1-dev-
|
|
3
|
+
"version": "0.1.1-dev-0cd653b.0.0cd653b",
|
|
4
4
|
"description": "Chain abstraction for Agoric's orchestration clients",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "index.js",
|
|
@@ -35,18 +35,18 @@
|
|
|
35
35
|
},
|
|
36
36
|
"homepage": "https://github.com/Agoric/agoric-sdk#readme",
|
|
37
37
|
"dependencies": {
|
|
38
|
-
"@agoric/async-flow": "0.1.1-dev-
|
|
39
|
-
"@agoric/cosmic-proto": "0.4.1-dev-
|
|
40
|
-
"@agoric/ertp": "0.16.3-dev-
|
|
41
|
-
"@agoric/internal": "0.3.3-dev-
|
|
42
|
-
"@agoric/network": "0.1.1-dev-
|
|
43
|
-
"@agoric/notifier": "0.6.3-dev-
|
|
44
|
-
"@agoric/store": "0.9.3-dev-
|
|
45
|
-
"@agoric/time": "0.3.3-dev-
|
|
46
|
-
"@agoric/vat-data": "0.5.3-dev-
|
|
47
|
-
"@agoric/vow": "0.1.1-dev-
|
|
48
|
-
"@agoric/zoe": "0.26.3-dev-
|
|
49
|
-
"@agoric/zone": "0.2.3-dev-
|
|
38
|
+
"@agoric/async-flow": "0.1.1-dev-0cd653b.0.0cd653b",
|
|
39
|
+
"@agoric/cosmic-proto": "0.4.1-dev-0cd653b.0.0cd653b",
|
|
40
|
+
"@agoric/ertp": "0.16.3-dev-0cd653b.0.0cd653b",
|
|
41
|
+
"@agoric/internal": "0.3.3-dev-0cd653b.0.0cd653b",
|
|
42
|
+
"@agoric/network": "0.1.1-dev-0cd653b.0.0cd653b",
|
|
43
|
+
"@agoric/notifier": "0.6.3-dev-0cd653b.0.0cd653b",
|
|
44
|
+
"@agoric/store": "0.9.3-dev-0cd653b.0.0cd653b",
|
|
45
|
+
"@agoric/time": "0.3.3-dev-0cd653b.0.0cd653b",
|
|
46
|
+
"@agoric/vat-data": "0.5.3-dev-0cd653b.0.0cd653b",
|
|
47
|
+
"@agoric/vow": "0.1.1-dev-0cd653b.0.0cd653b",
|
|
48
|
+
"@agoric/zoe": "0.26.3-dev-0cd653b.0.0cd653b",
|
|
49
|
+
"@agoric/zone": "0.2.3-dev-0cd653b.0.0cd653b",
|
|
50
50
|
"@cosmjs/encoding": "^0.36.0",
|
|
51
51
|
"@endo/base64": "^1.1.1",
|
|
52
52
|
"@endo/common": "^1.4.0",
|
|
@@ -61,8 +61,8 @@
|
|
|
61
61
|
"bs58": "^6.0.0"
|
|
62
62
|
},
|
|
63
63
|
"devDependencies": {
|
|
64
|
-
"@agoric/swingset-liveslots": "0.10.3-dev-
|
|
65
|
-
"@agoric/vats": "0.15.2-dev-
|
|
64
|
+
"@agoric/swingset-liveslots": "0.10.3-dev-0cd653b.0.0cd653b",
|
|
65
|
+
"@agoric/vats": "0.15.2-dev-0cd653b.0.0cd653b",
|
|
66
66
|
"@chain-registry/client": "^1.53.194",
|
|
67
67
|
"@cosmjs/amino": "^0.36.0",
|
|
68
68
|
"@cosmjs/proto-signing": "^0.36.0",
|
|
@@ -98,5 +98,5 @@
|
|
|
98
98
|
"typeCoverage": {
|
|
99
99
|
"atLeast": 96.68
|
|
100
100
|
},
|
|
101
|
-
"gitHead": "
|
|
101
|
+
"gitHead": "0cd653b11224a7bf48e42d387532844b4c6e091f"
|
|
102
102
|
}
|
package/src/utils/abitype.d.ts
CHANGED
|
@@ -1,6 +1,67 @@
|
|
|
1
|
-
import type { TypedDataType, TypedData } from 'abitype';
|
|
1
|
+
import type { AbiParameterToPrimitiveType, TypedDataType, TypedData } from 'abitype';
|
|
2
|
+
import type { Simplify } from '@agoric/internal';
|
|
2
3
|
export type TypedDataParameter<TN extends string = string, TT extends string = TypedDataType | keyof TypedData | `${keyof TypedData}[${string | ''}]`> = {
|
|
3
4
|
name: TN;
|
|
4
5
|
type: TT;
|
|
6
|
+
/**
|
|
7
|
+
* Repo-local extension, not part of the EIP-712 spec: marks this field as
|
|
8
|
+
* not required to be present on every instance of the struct in a given
|
|
9
|
+
* message. Must be resolved by `normalizeEIP712Data` (dropped, or stripped
|
|
10
|
+
* to a plain required field) before the type record is used by any real
|
|
11
|
+
* EIP-712 tooling (e.g. viem's `hashStruct`/`validateTypedData`), which
|
|
12
|
+
* doesn't understand it.
|
|
13
|
+
*/
|
|
14
|
+
optional?: boolean;
|
|
5
15
|
};
|
|
16
|
+
/**
|
|
17
|
+
* Depth-agnostic replacement for abitype's `TypedDataToPrimitiveTypes`,
|
|
18
|
+
* which has no notion of this repo's `optional` field marker at all -- every
|
|
19
|
+
* field of every struct it touches infers as required. Given the full
|
|
20
|
+
* type-graph record `TD` and the name `K` of the struct to convert, this
|
|
21
|
+
* recursively derives the same TS type abitype would for that struct, but
|
|
22
|
+
* applies `optional` at every struct it encounters, however deeply nested
|
|
23
|
+
* (directly, through another struct field, or through an array element
|
|
24
|
+
* type).
|
|
25
|
+
*
|
|
26
|
+
* Values for plain Solidity primitive fields (and arrays of them) are still
|
|
27
|
+
* computed via abitype's own `AbiParameterToPrimitiveType` -- only the
|
|
28
|
+
* struct/array *graph walk* is reimplemented here, to weave in `optional`.
|
|
29
|
+
* Unlike abitype's version, this has no self/circular-reference detection:
|
|
30
|
+
* none of this repo's type graphs are self-referencing, so it wasn't worth
|
|
31
|
+
* reproducing.
|
|
32
|
+
*/
|
|
33
|
+
export type TypedDataToStructType<TD extends Record<string, readonly TypedDataParameter[]>, K extends keyof TD & string> = StructToType<TD, TD[K]>;
|
|
34
|
+
type StructToType<TD extends Record<string, readonly TypedDataParameter[]>, Fields extends readonly TypedDataParameter[]> = Simplify<{
|
|
35
|
+
[F in Exclude<Fields[number], {
|
|
36
|
+
optional: true;
|
|
37
|
+
}> as F['name']]: FieldToType<TD, F['type']>;
|
|
38
|
+
} & {
|
|
39
|
+
[F in Extract<Fields[number], {
|
|
40
|
+
optional: true;
|
|
41
|
+
}> as F['name']]?: FieldToType<TD, F['type']>;
|
|
42
|
+
}>;
|
|
43
|
+
/**
|
|
44
|
+
* `Foo[3]` / `Foo[]` -> `{elem: 'Foo'; size: '3' | ''}`; non-array ->
|
|
45
|
+
* `undefined`, NOT `never` -- `never` is a subtype of everything, so
|
|
46
|
+
* `ParseArrayType<T> extends {elem: ...; size: ...}` would then vacuously
|
|
47
|
+
* match every non-array `T` too (mirrors abitype's own `undefined` fallback
|
|
48
|
+
* in `MaybeExtractArrayParameterType`, for the same reason).
|
|
49
|
+
*/
|
|
50
|
+
type ParseArrayType<T extends string> = T extends `${infer Elem}[${infer Size}]` ? {
|
|
51
|
+
elem: Elem;
|
|
52
|
+
size: Size;
|
|
53
|
+
} : undefined;
|
|
54
|
+
/** Mirrors abitype's own (unexported) `Tuple` helper. */
|
|
55
|
+
type FixedLengthTuple<T, N extends number, Acc extends unknown[] = []> = Acc['length'] extends N ? Acc : FixedLengthTuple<T, N, [T, ...Acc]>;
|
|
56
|
+
type FieldToType<TD extends Record<string, readonly TypedDataParameter[]>, FieldType extends string> = ParseArrayType<FieldType> extends {
|
|
57
|
+
elem: infer Elem extends string;
|
|
58
|
+
size: infer Size extends string;
|
|
59
|
+
} ? Elem extends keyof TD & string ? Size extends `${infer N extends number}` ? FixedLengthTuple<StructToType<TD, TD[Elem]>, N> : readonly StructToType<TD, TD[Elem]>[] : AbiParameterToPrimitiveType<{
|
|
60
|
+
name: string;
|
|
61
|
+
type: FieldType;
|
|
62
|
+
}> : FieldType extends keyof TD & string ? StructToType<TD, TD[FieldType]> : AbiParameterToPrimitiveType<{
|
|
63
|
+
name: string;
|
|
64
|
+
type: FieldType;
|
|
65
|
+
}>;
|
|
66
|
+
export {};
|
|
6
67
|
//# sourceMappingURL=abitype.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"abitype.d.ts","sourceRoot":"","sources":["abitype.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"abitype.d.ts","sourceRoot":"","sources":["abitype.ts"],"names":[],"mappings":"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"}
|
package/src/utils/abitype.js
CHANGED
|
@@ -1,4 +1,9 @@
|
|
|
1
|
-
|
|
1
|
+
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
2
7
|
|
|
3
8
|
// Redefine abitype's TypedDataParameter to make it generic
|
|
4
9
|
|
|
@@ -10,4 +15,87 @@
|
|
|
10
15
|
|
|
11
16
|
|
|
12
17
|
|
|
18
|
+
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
13
27
|
|
|
28
|
+
|
|
29
|
+
/**
|
|
30
|
+
* Depth-agnostic replacement for abitype's `TypedDataToPrimitiveTypes`,
|
|
31
|
+
* which has no notion of this repo's `optional` field marker at all -- every
|
|
32
|
+
* field of every struct it touches infers as required. Given the full
|
|
33
|
+
* type-graph record `TD` and the name `K` of the struct to convert, this
|
|
34
|
+
* recursively derives the same TS type abitype would for that struct, but
|
|
35
|
+
* applies `optional` at every struct it encounters, however deeply nested
|
|
36
|
+
* (directly, through another struct field, or through an array element
|
|
37
|
+
* type).
|
|
38
|
+
*
|
|
39
|
+
* Values for plain Solidity primitive fields (and arrays of them) are still
|
|
40
|
+
* computed via abitype's own `AbiParameterToPrimitiveType` -- only the
|
|
41
|
+
* struct/array *graph walk* is reimplemented here, to weave in `optional`.
|
|
42
|
+
* Unlike abitype's version, this has no self/circular-reference detection:
|
|
43
|
+
* none of this repo's type graphs are self-referencing, so it wasn't worth
|
|
44
|
+
* reproducing.
|
|
45
|
+
*/
|
|
46
|
+
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* `Foo[3]` / `Foo[]` -> `{elem: 'Foo'; size: '3' | ''}`; non-array ->
|
|
70
|
+
* `undefined`, NOT `never` -- `never` is a subtype of everything, so
|
|
71
|
+
* `ParseArrayType<T> extends {elem: ...; size: ...}` would then vacuously
|
|
72
|
+
* match every non-array `T` too (mirrors abitype's own `undefined` fallback
|
|
73
|
+
* in `MaybeExtractArrayParameterType`, for the same reason).
|
|
74
|
+
*/
|
|
75
|
+
|
|
76
|
+
|
|
77
|
+
|
|
78
|
+
|
|
79
|
+
/** Mirrors abitype's own (unexported) `Tuple` helper. */
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
|
|
83
|
+
|
|
84
|
+
|
|
85
|
+
|
|
86
|
+
|
|
87
|
+
|
|
88
|
+
|
|
89
|
+
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
|
|
95
|
+
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
|
|
99
|
+
|
|
100
|
+
|
|
101
|
+
|
|
@@ -10,23 +10,22 @@
|
|
|
10
10
|
*
|
|
11
11
|
* Some stylistic changes have also been made to conform to our code style.
|
|
12
12
|
*/
|
|
13
|
-
import type { TypedDataToPrimitiveTypes } from 'abitype';
|
|
14
13
|
import type { Address, TypedData, TypedDataDefinition } from 'viem';
|
|
15
|
-
import type { TypedDataParameter } from '../abitype.js';
|
|
14
|
+
import type { TypedDataParameter, TypedDataToStructType } from '../abitype.js';
|
|
16
15
|
export declare const PERMIT2_DOMAIN_NAME = "Permit2";
|
|
17
16
|
interface WitnessDefinition<T extends TypedData = TypedData, TD extends TypedDataParameter<string, Extract<keyof T, string>> = TypedDataParameter<'witness', Extract<keyof T, string>>> {
|
|
18
17
|
witnessField: TD;
|
|
19
18
|
witnessTypes: T;
|
|
20
19
|
}
|
|
21
20
|
export interface Witness<T extends TypedData = TypedData, TD extends TypedDataParameter<string, Extract<keyof T, string>> = TypedDataParameter<'witness', Extract<keyof T, string>>> extends WitnessDefinition<T, TD> {
|
|
22
|
-
witness:
|
|
21
|
+
witness: TypedDataToStructType<T, TD['type']>;
|
|
23
22
|
}
|
|
24
23
|
/**
|
|
25
24
|
* Helper to help caller check that the `data` matches the inferred witness types
|
|
26
25
|
*
|
|
27
26
|
* @see {@link https://www.typescriptlang.org/docs/handbook/release-notes/typescript-5-4.html#the-noinfer-utility-type}
|
|
28
27
|
*/
|
|
29
|
-
export declare function makeWitness<T extends TypedData, TD extends TypedDataParameter<string, Extract<keyof T, string>>>(data: NoInfer<
|
|
28
|
+
export declare function makeWitness<T extends TypedData, TD extends TypedDataParameter<string, Extract<keyof T, string>>>(data: NoInfer<TypedDataToStructType<T, TD['type']>>, types: T, typeParam: TD): Witness<T, TD>;
|
|
30
29
|
export type TokenPermissions = {
|
|
31
30
|
token: Address;
|
|
32
31
|
amount: bigint;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"signatureTransfer.d.ts","sourceRoot":"","sources":["signatureTransfer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,OAAO,KAAK,
|
|
1
|
+
{"version":3,"file":"signatureTransfer.d.ts","sourceRoot":"","sources":["signatureTransfer.ts"],"names":[],"mappings":"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"}
|
|
@@ -11,14 +11,13 @@
|
|
|
11
11
|
* Some stylistic changes have also been made to conform to our code style.
|
|
12
12
|
*/
|
|
13
13
|
|
|
14
|
-
|
|
15
14
|
|
|
16
15
|
|
|
17
16
|
|
|
18
17
|
|
|
19
18
|
|
|
20
19
|
|
|
21
|
-
|
|
20
|
+
|
|
22
21
|
|
|
23
22
|
export const PERMIT2_DOMAIN_NAME = 'Permit2';
|
|
24
23
|
|
|
@@ -37,7 +36,7 @@ export const PERMIT2_DOMAIN_NAME = 'Permit2';
|
|
|
37
36
|
|
|
38
37
|
|
|
39
38
|
|
|
40
|
-
|
|
39
|
+
|
|
41
40
|
|
|
42
41
|
|
|
43
42
|
/**
|
|
@@ -49,7 +48,7 @@ export function makeWitness(
|
|
|
49
48
|
|
|
50
49
|
|
|
51
50
|
|
|
52
|
-
data
|
|
51
|
+
data ,
|
|
53
52
|
types ,
|
|
54
53
|
typeParam ,
|
|
55
54
|
) {
|
|
@@ -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":"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"}
|
|
@@ -149,7 +149,7 @@ export function validatePermit2Domain(
|
|
|
149
149
|
throw new Error(`Missing domain in permit2 data`);
|
|
150
150
|
}
|
|
151
151
|
|
|
152
|
-
const { chainId, verifyingContract, name } = domain;
|
|
152
|
+
const { chainId, verifyingContract, name, ...extra } = domain;
|
|
153
153
|
if (name !== PERMIT2_DOMAIN_NAME) {
|
|
154
154
|
throw new Error(`Invalid permit2 domain name: ${name}`);
|
|
155
155
|
}
|
|
@@ -170,7 +170,12 @@ export function validatePermit2Domain(
|
|
|
170
170
|
}
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
|
|
173
|
+
const extraKeys = Object.keys(extra);
|
|
174
|
+
if (extraKeys.length) {
|
|
175
|
+
throw new Error(
|
|
176
|
+
`Unexpected field(s) in permit2 domain: ${extraKeys.join(', ')}`,
|
|
177
|
+
);
|
|
178
|
+
}
|
|
174
179
|
}
|
|
175
180
|
|
|
176
181
|
/**
|
|
@@ -0,0 +1,64 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Normalizes *and validates* EIP-712 typed data (`{message, types,
|
|
3
|
+
* primaryType}`) in a single pass:
|
|
4
|
+
* - `optional` struct fields (see {@link TypedDataParameter}) are resolved
|
|
5
|
+
* per-message: dropped from `types` if never present on any instance of
|
|
6
|
+
* the struct, otherwise kept with the `optional` marker stripped (treated
|
|
7
|
+
* as required from then on).
|
|
8
|
+
* - struct types never actually encountered while walking `message` from
|
|
9
|
+
* `primaryType` are absent from `types`. `EIP712Domain` is always
|
|
10
|
+
* preserved if present, since domain values live outside `message`.
|
|
11
|
+
*
|
|
12
|
+
* `onExtraField` controls how *excess* data (fields not declared in
|
|
13
|
+
* `types`, array elements beyond a fixed length) is handled:
|
|
14
|
+
* - 'drop' (default): silently remove it from `message`.
|
|
15
|
+
* - 'throw': reject it — use when `types` is the trusted/expected shape
|
|
16
|
+
* and `message` is untrusted, to catch data smuggled in outside what's
|
|
17
|
+
* declared.
|
|
18
|
+
* - 'keep': leave `message` untouched (no projection, no removal of excess
|
|
19
|
+
* data). Dynamic-length arrays are never truncated in any mode, since
|
|
20
|
+
* every element affects the EIP-712 hash.
|
|
21
|
+
*
|
|
22
|
+
* Regardless of `onExtraField`, the result is also *validated*, with the
|
|
23
|
+
* goal that if validation succeeds, hashing the result is guaranteed to
|
|
24
|
+
* succeed and to hash exactly the values given:
|
|
25
|
+
* - every declared field must be present. Fixed-length arrays must have at
|
|
26
|
+
* least their declared number of elements (too few is missing data, not
|
|
27
|
+
* excess, so no mode tolerates it; too many is excess, tolerated only by
|
|
28
|
+
* `'keep'`).
|
|
29
|
+
* - a struct/array field must get an array/object value, and a plain
|
|
30
|
+
* Solidity type (`address`, `bool`, `string`, `uint*`/`int*`, `bytes*`)
|
|
31
|
+
* must get a value of the specific JS type that implies -- see
|
|
32
|
+
* `assertValidPrimitive`.
|
|
33
|
+
* - primitive leaf values are checked against their type's shape/range,
|
|
34
|
+
* recursing correctly through arrays, which real EIP-712 tooling (e.g.
|
|
35
|
+
* viem's `validateTypedData`) does not.
|
|
36
|
+
*
|
|
37
|
+
* No runtime dependency on `viem`/`abitype`, so this can run on-chain. Two
|
|
38
|
+
* known exceptions to the validate-implies-hash invariant, both left
|
|
39
|
+
* unclosed since closing them needs a hashing dependency:
|
|
40
|
+
* - `address` values are only checked for 20-byte-hex shape, not EIP-55
|
|
41
|
+
* checksum. Real hashing lowercases addresses before encoding (so casing
|
|
42
|
+
* never affects the hash), but still separately rejects a mixed-case
|
|
43
|
+
* value whose checksum doesn't match, as a typo safety net.
|
|
44
|
+
* - a `Uint8Array` given for a fixed `bytes<M>` field is rejected rather
|
|
45
|
+
* than converted to the equivalent hex string.
|
|
46
|
+
*/
|
|
47
|
+
import type { TypedDataParameter } from '../abitype.js';
|
|
48
|
+
type TypesRecord = Record<string, readonly TypedDataParameter[]>;
|
|
49
|
+
export type NormalizeAndValidateEIP712DataInput = {
|
|
50
|
+
message: Record<string, unknown>;
|
|
51
|
+
types: TypesRecord;
|
|
52
|
+
primaryType: string;
|
|
53
|
+
};
|
|
54
|
+
export type NormalizeAndValidateEIP712DataOptions = {
|
|
55
|
+
/** default 'drop' */
|
|
56
|
+
onExtraField?: 'drop' | 'throw' | 'keep';
|
|
57
|
+
};
|
|
58
|
+
export type NormalizeAndValidateEIP712DataResult = {
|
|
59
|
+
message: Record<string, unknown>;
|
|
60
|
+
types: TypesRecord;
|
|
61
|
+
};
|
|
62
|
+
export declare const normalizeAndValidateEIP712Data: (input: NormalizeAndValidateEIP712DataInput, options?: NormalizeAndValidateEIP712DataOptions) => NormalizeAndValidateEIP712DataResult;
|
|
63
|
+
export {};
|
|
64
|
+
//# sourceMappingURL=eip712-normalize.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"eip712-normalize.d.ts","sourceRoot":"","sources":["eip712-normalize.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA6CG;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;CAC1C,CAAC;AAEF,MAAM,MAAM,oCAAoC,GAAG;IACjD,OAAO,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;IACjC,KAAK,EAAE,WAAW,CAAC;CACpB,CAAC;AAkYF,eAAO,MAAM,8BAA8B,GACzC,OAAO,mCAAmC,EAC1C,UAAS,qCAA0C,KAClD,oCA+CF,CAAC"}
|
|
@@ -0,0 +1,501 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @file Normalizes *and validates* EIP-712 typed data (`{message, types,
|
|
3
|
+
* primaryType}`) in a single pass:
|
|
4
|
+
* - `optional` struct fields (see {@link TypedDataParameter}) are resolved
|
|
5
|
+
* per-message: dropped from `types` if never present on any instance of
|
|
6
|
+
* the struct, otherwise kept with the `optional` marker stripped (treated
|
|
7
|
+
* as required from then on).
|
|
8
|
+
* - struct types never actually encountered while walking `message` from
|
|
9
|
+
* `primaryType` are absent from `types`. `EIP712Domain` is always
|
|
10
|
+
* preserved if present, since domain values live outside `message`.
|
|
11
|
+
*
|
|
12
|
+
* `onExtraField` controls how *excess* data (fields not declared in
|
|
13
|
+
* `types`, array elements beyond a fixed length) is handled:
|
|
14
|
+
* - 'drop' (default): silently remove it from `message`.
|
|
15
|
+
* - 'throw': reject it — use when `types` is the trusted/expected shape
|
|
16
|
+
* and `message` is untrusted, to catch data smuggled in outside what's
|
|
17
|
+
* declared.
|
|
18
|
+
* - 'keep': leave `message` untouched (no projection, no removal of excess
|
|
19
|
+
* data). Dynamic-length arrays are never truncated in any mode, since
|
|
20
|
+
* every element affects the EIP-712 hash.
|
|
21
|
+
*
|
|
22
|
+
* Regardless of `onExtraField`, the result is also *validated*, with the
|
|
23
|
+
* goal that if validation succeeds, hashing the result is guaranteed to
|
|
24
|
+
* succeed and to hash exactly the values given:
|
|
25
|
+
* - every declared field must be present. Fixed-length arrays must have at
|
|
26
|
+
* least their declared number of elements (too few is missing data, not
|
|
27
|
+
* excess, so no mode tolerates it; too many is excess, tolerated only by
|
|
28
|
+
* `'keep'`).
|
|
29
|
+
* - a struct/array field must get an array/object value, and a plain
|
|
30
|
+
* Solidity type (`address`, `bool`, `string`, `uint*`/`int*`, `bytes*`)
|
|
31
|
+
* must get a value of the specific JS type that implies -- see
|
|
32
|
+
* `assertValidPrimitive`.
|
|
33
|
+
* - primitive leaf values are checked against their type's shape/range,
|
|
34
|
+
* recursing correctly through arrays, which real EIP-712 tooling (e.g.
|
|
35
|
+
* viem's `validateTypedData`) does not.
|
|
36
|
+
*
|
|
37
|
+
* No runtime dependency on `viem`/`abitype`, so this can run on-chain. Two
|
|
38
|
+
* known exceptions to the validate-implies-hash invariant, both left
|
|
39
|
+
* unclosed since closing them needs a hashing dependency:
|
|
40
|
+
* - `address` values are only checked for 20-byte-hex shape, not EIP-55
|
|
41
|
+
* checksum. Real hashing lowercases addresses before encoding (so casing
|
|
42
|
+
* never affects the hash), but still separately rejects a mixed-case
|
|
43
|
+
* value whose checksum doesn't match, as a typo safety net.
|
|
44
|
+
* - a `Uint8Array` given for a fixed `bytes<M>` field is rejected rather
|
|
45
|
+
* than converted to the equivalent hex string.
|
|
46
|
+
*/
|
|
47
|
+
|
|
48
|
+
|
|
49
|
+
|
|
50
|
+
|
|
51
|
+
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
|
|
61
|
+
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
const ARRAY_SUFFIX = /\[(\d*)\]$/u;
|
|
68
|
+
|
|
69
|
+
const splitArrayType = (
|
|
70
|
+
type ,
|
|
71
|
+
) => {
|
|
72
|
+
const match = type.match(ARRAY_SUFFIX);
|
|
73
|
+
if (!match) return { base: type, isArray: false };
|
|
74
|
+
return {
|
|
75
|
+
base: type.slice(0, -match[0].length),
|
|
76
|
+
length: match[1] ? Number(match[1]) : undefined,
|
|
77
|
+
isArray: true,
|
|
78
|
+
};
|
|
79
|
+
};
|
|
80
|
+
|
|
81
|
+
const ADDRESS_REGEX = /^0x[a-fA-F0-9]{40}$/u;
|
|
82
|
+
// Solidity `(u)int<M>`: (un)signed integer of `M` bits, `0 < M <= 256`,
|
|
83
|
+
// `M % 8 === 0`; bare `uint`/`int` (no explicit width) means 256 bits.
|
|
84
|
+
// Mirrors viem's `integerRegex` (utils/regex.ts) -- duplicated rather than
|
|
85
|
+
// imported so this module has no runtime dependency on `viem`.
|
|
86
|
+
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)?$/u;
|
|
88
|
+
// Solidity `bytes<M>`: binary type of `M` bytes, `0 < M <= 32`; bare `bytes`
|
|
89
|
+
// (no explicit size) is dynamic-length, so has nothing to check here.
|
|
90
|
+
const BYTES_TYPE_REGEX = /^bytes([1-9]|1[0-9]|2[0-9]|3[0-2])?$/u;
|
|
91
|
+
const HEX_REGEX = /^0x[0-9a-fA-F]*$/u;
|
|
92
|
+
|
|
93
|
+
const MAX_DESCRIBED_LENGTH = 100;
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Slices `str` to at most `MAX_DESCRIBED_LENGTH` characters, appending
|
|
97
|
+
* `...` if it was cut. Bounds length only -- doesn't escape or annotate;
|
|
98
|
+
* `truncate` and `describeValue`'s string case each build on this
|
|
99
|
+
* differently (an appended `(N chars total)` annotation, or JSON escaping,
|
|
100
|
+
* respectively), so the raw slicing lives in one place.
|
|
101
|
+
*/
|
|
102
|
+
const truncateRaw = (str ) =>
|
|
103
|
+
str.length > MAX_DESCRIBED_LENGTH
|
|
104
|
+
? `${str.slice(0, MAX_DESCRIBED_LENGTH)}...`
|
|
105
|
+
: str;
|
|
106
|
+
|
|
107
|
+
/**
|
|
108
|
+
* Bounds a string embedded in an error message -- `message` (and, in
|
|
109
|
+
* 'throw' mode, `types`) can come from an untrusted source, so a field
|
|
110
|
+
* name, value, or joined list of them could otherwise be arbitrarily large.
|
|
111
|
+
* Bounds length only, and doesn't escape the result -- see `quoteName` for
|
|
112
|
+
* a quoted-and-escaped identifier suitable for embedding directly in a
|
|
113
|
+
* message (manually wrapping this in literal quotes instead is unsafe: an
|
|
114
|
+
* embedded quote character in `str` would break out of them).
|
|
115
|
+
*/
|
|
116
|
+
const truncate = (str ) =>
|
|
117
|
+
str.length > MAX_DESCRIBED_LENGTH
|
|
118
|
+
? `${truncateRaw(str)}(${str.length} chars total)`
|
|
119
|
+
: str;
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Quotes and escapes an identifier (a struct/field type or field name) for
|
|
123
|
+
* embedding in an error message. Escaping via `JSON.stringify` happens
|
|
124
|
+
* *after* truncating and covers the whole (possibly-annotated) result, so
|
|
125
|
+
* an embedded quote, backslash, or newline in the identifier can't break
|
|
126
|
+
* out of the message's own quoting, and the `(N chars total)` annotation
|
|
127
|
+
* can't itself get chopped off by a later truncation step (there isn't
|
|
128
|
+
* one).
|
|
129
|
+
*/
|
|
130
|
+
const quoteName = (str ) => JSON.stringify(truncate(str));
|
|
131
|
+
|
|
132
|
+
const MAX_DESCRIBED_ITEMS = 10;
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Quotes and escapes each name in `names` (see `quoteName`) and joins them
|
|
136
|
+
* for an error message -- bounding not just each individual name's length
|
|
137
|
+
* but also, since attacker-controlled data can make the *list itself*
|
|
138
|
+
* arbitrarily long (e.g. very many extra fields on a struct), the number of
|
|
139
|
+
* items included. Cuts off whole items rather than slicing the joined
|
|
140
|
+
* string's raw characters, so the result can never end mid-quote the way
|
|
141
|
+
* `truncate`-ing an already-joined-and-quoted string could.
|
|
142
|
+
*/
|
|
143
|
+
const quoteNameList = (names ) => {
|
|
144
|
+
const shown = names.slice(0, MAX_DESCRIBED_ITEMS).map(quoteName).join(', ');
|
|
145
|
+
const omitted = names.length - MAX_DESCRIBED_ITEMS;
|
|
146
|
+
return omitted > 0 ? `${shown}, and ${omitted} more` : shown;
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
/**
|
|
150
|
+
* A short, bounded description of a value for error messages. Only
|
|
151
|
+
* `string`/`number`/`bigint` can be arbitrarily long themselves; anything
|
|
152
|
+
* else is described by shape/type rather than rendered.
|
|
153
|
+
*/
|
|
154
|
+
const describeValue = (value ) => {
|
|
155
|
+
if (value === null) return 'null';
|
|
156
|
+
if (typeof value === 'string') {
|
|
157
|
+
if (value.length > MAX_DESCRIBED_LENGTH) {
|
|
158
|
+
// The `(N chars total)` annotation goes *outside* the JSON-quoted,
|
|
159
|
+
// truncated value (unlike `quoteName`), so it reads as metadata about
|
|
160
|
+
// the value rather than part of it.
|
|
161
|
+
return `${JSON.stringify(truncateRaw(value))}(${value.length} chars total)`;
|
|
162
|
+
}
|
|
163
|
+
return JSON.stringify(value);
|
|
164
|
+
}
|
|
165
|
+
// Truncate the digits *before* appending the `n` suffix -- appending
|
|
166
|
+
// first and truncating the combined string risks slicing the `n` itself
|
|
167
|
+
// off a long enough value, leaving output that no longer looks like a
|
|
168
|
+
// bigint.
|
|
169
|
+
if (typeof value === 'bigint') return `${truncate(`${value}`)}n`;
|
|
170
|
+
if (typeof value === 'number') return truncate(String(value));
|
|
171
|
+
if (value instanceof Uint8Array)
|
|
172
|
+
return `a Uint8Array with ${value.length} byte(s)`;
|
|
173
|
+
if (Array.isArray(value)) return `an array with ${value.length} element(s)`;
|
|
174
|
+
return `a ${typeof value}`;
|
|
175
|
+
};
|
|
176
|
+
|
|
177
|
+
/**
|
|
178
|
+
* Validates a value declared as a non-struct, non-array EIP-712 type
|
|
179
|
+
* (`visit` only calls this once it's confirmed `fieldType` is neither).
|
|
180
|
+
* Requires the specific JS type real hashing needs for each Solidity type,
|
|
181
|
+
* not just something that happens to coerce -- e.g. a number given for a
|
|
182
|
+
* `string` field would otherwise hash as the number's own hex encoding,
|
|
183
|
+
* not the string form, a footgun real EIP-712 tooling doesn't catch either.
|
|
184
|
+
*
|
|
185
|
+
* - `address`: string, 20-byte-hex shape (not full EIP-55 checksum -- see
|
|
186
|
+
* the module doc comment for why).
|
|
187
|
+
* - `bool`: JS `boolean`.
|
|
188
|
+
* - `string`: JS `string`.
|
|
189
|
+
* - `bytes` (dynamic): hex string *or* `Uint8Array` -- hashed identically
|
|
190
|
+
* either way.
|
|
191
|
+
* - `bytes<M>` (fixed): hex string only, of exactly `M` bytes -- a
|
|
192
|
+
* `Uint8Array` does not hash successfully here (see the module doc
|
|
193
|
+
* comment).
|
|
194
|
+
* - `uint<M>`/`int<M>` (bare `uint`/`int` means 256 bits): `number` or
|
|
195
|
+
* `bigint`, an integer, in range for the bit width/signedness.
|
|
196
|
+
* - anything else: not a real Solidity primitive type, so rejected
|
|
197
|
+
* unconditionally.
|
|
198
|
+
*/
|
|
199
|
+
const assertValidPrimitive = (fieldType , value ) => {
|
|
200
|
+
const quotedType = quoteName(fieldType);
|
|
201
|
+
|
|
202
|
+
if (fieldType === 'address') {
|
|
203
|
+
if (typeof value !== 'string' || !ADDRESS_REGEX.test(value)) {
|
|
204
|
+
throw new Error(`Invalid EIP-712 address value: ${describeValue(value)}`);
|
|
205
|
+
}
|
|
206
|
+
return;
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
if (fieldType === 'bool') {
|
|
210
|
+
if (typeof value !== 'boolean') {
|
|
211
|
+
throw new Error(
|
|
212
|
+
`Expected a boolean for EIP-712 type "bool", got ${describeValue(value)}`,
|
|
213
|
+
);
|
|
214
|
+
}
|
|
215
|
+
return;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
if (fieldType === 'string') {
|
|
219
|
+
if (typeof value !== 'string') {
|
|
220
|
+
throw new Error(
|
|
221
|
+
`Expected a string for EIP-712 type "string", got ${describeValue(value)}`,
|
|
222
|
+
);
|
|
223
|
+
}
|
|
224
|
+
return;
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
const integerMatch = fieldType.match(INTEGER_TYPE_REGEX);
|
|
228
|
+
if (integerMatch) {
|
|
229
|
+
if (typeof value !== 'number' && typeof value !== 'bigint') {
|
|
230
|
+
throw new Error(
|
|
231
|
+
`Expected a number or bigint for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
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)}`,
|
|
237
|
+
);
|
|
238
|
+
}
|
|
239
|
+
const signed = integerMatch[1] === 'int';
|
|
240
|
+
const bits = integerMatch[2] ? Number(integerMatch[2]) : 256;
|
|
241
|
+
const bigValue = BigInt(value);
|
|
242
|
+
const max = signed ? 2n ** BigInt(bits - 1) - 1n : 2n ** BigInt(bits) - 1n;
|
|
243
|
+
const min = signed ? -max - 1n : 0n;
|
|
244
|
+
if (bigValue < min || bigValue > max) {
|
|
245
|
+
throw new Error(
|
|
246
|
+
`Value ${describeValue(value)} is out of range for EIP-712 type ${quotedType} (expected ${min} to ${max})`,
|
|
247
|
+
);
|
|
248
|
+
}
|
|
249
|
+
return;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
const bytesMatch = fieldType.match(BYTES_TYPE_REGEX);
|
|
253
|
+
if (bytesMatch) {
|
|
254
|
+
// Dynamic `bytes` is hashed directly via `keccak256`, which accepts a
|
|
255
|
+
// `Uint8Array` interchangeably with a hex string. Fixed `bytes<M>` goes
|
|
256
|
+
// through the generic Solidity ABI path instead, which requires a hex
|
|
257
|
+
// string and throws an unrelated error for a `Uint8Array`.
|
|
258
|
+
if (!bytesMatch[1] && value instanceof Uint8Array) return;
|
|
259
|
+
if (typeof value !== 'string' || !HEX_REGEX.test(value)) {
|
|
260
|
+
throw new Error(
|
|
261
|
+
`Expected a hex string for EIP-712 type ${quotedType}, got ${describeValue(value)}`,
|
|
262
|
+
);
|
|
263
|
+
}
|
|
264
|
+
if (bytesMatch[1]) {
|
|
265
|
+
const expectedSize = Number(bytesMatch[1]);
|
|
266
|
+
const actualSize = Math.ceil((value.length - 2) / 2);
|
|
267
|
+
if (actualSize !== expectedSize) {
|
|
268
|
+
throw new Error(
|
|
269
|
+
`Expected EIP-712 type ${quotedType} to be ${expectedSize} bytes, got ${actualSize}: ${describeValue(value)}`,
|
|
270
|
+
);
|
|
271
|
+
}
|
|
272
|
+
}
|
|
273
|
+
return;
|
|
274
|
+
}
|
|
275
|
+
|
|
276
|
+
throw new Error(`Unrecognized EIP-712 type ${quotedType}`);
|
|
277
|
+
};
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* A field declared without `optional` starts `required`; one declared
|
|
281
|
+
* `optional` starts `optional-unseen` and transitions from there as
|
|
282
|
+
* instances of its struct are visited -- see `visit`.
|
|
283
|
+
*/
|
|
284
|
+
;
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* A struct's fields, keyed by name, tracking each field's {@link
|
|
288
|
+
* FieldState}. Also doubles as an array-typed field's per-element record
|
|
289
|
+
* (see `visit`'s `isArray` branch) -- but even then, the keys involved are
|
|
290
|
+
* always strings: `for...in`/`in` read array indices as strings, never as
|
|
291
|
+
* `number`.
|
|
292
|
+
*/
|
|
293
|
+
;
|
|
294
|
+
|
|
295
|
+
/** Looks up (and lazily builds) the {@link TypeFields} for a struct type name, or `undefined` for a primitive leaf. */
|
|
296
|
+
;
|
|
297
|
+
|
|
298
|
+
/**
|
|
299
|
+
* Walks `value` against `fieldType` (a struct or array field type string).
|
|
300
|
+
* One function covers both, since a field's declared type is never an
|
|
301
|
+
* array of arrays. `getType` hands out the *same* {@link TypeFields}
|
|
302
|
+
* record for every instance of a given struct type name encountered
|
|
303
|
+
* anywhere in the message, which is what lets a single walk resolve
|
|
304
|
+
* `optional` fields and validate required-field presence across
|
|
305
|
+
* repeated/nested uses of the same type:
|
|
306
|
+
*
|
|
307
|
+
* - `optional-unseen` + present => `required` (this instance's presence
|
|
308
|
+
* makes the field required everywhere, including instances already
|
|
309
|
+
* visited or yet to be visited).
|
|
310
|
+
* - `optional-unseen` + absent => `optional-seen`.
|
|
311
|
+
* - `optional-seen` + present => conflict: an earlier instance confirmed
|
|
312
|
+
* the field absent, this one sets it. Rejected immediately.
|
|
313
|
+
* - `optional-seen` + absent => stays `optional-seen`.
|
|
314
|
+
* - `required` + present => stays `required`.
|
|
315
|
+
* - `required` + absent => rejected immediately.
|
|
316
|
+
*
|
|
317
|
+
* Every mode runs these the same way, `keep` included -- missing data is
|
|
318
|
+
* missing data regardless of `onExtraField`. `keep` only skips removing or
|
|
319
|
+
* rejecting *excess* data (extra fields, over-length arrays) and never
|
|
320
|
+
* touches `message`.
|
|
321
|
+
*/
|
|
322
|
+
const visit = (
|
|
323
|
+
fieldType ,
|
|
324
|
+
value ,
|
|
325
|
+
getType ,
|
|
326
|
+
onExtraField ,
|
|
327
|
+
) => {
|
|
328
|
+
const keep = onExtraField === 'keep';
|
|
329
|
+
const { base, length: requiredLength, isArray } = splitArrayType(fieldType);
|
|
330
|
+
// Registers `base` in the output types even if no element ends up visited.
|
|
331
|
+
const baseType = getType(base);
|
|
332
|
+
|
|
333
|
+
if (!isArray && !baseType) {
|
|
334
|
+
// Not a declared struct or array, so it must be a recognized Solidity
|
|
335
|
+
// primitive with a matching JS value; also catches an unknown type.
|
|
336
|
+
assertValidPrimitive(fieldType, value);
|
|
337
|
+
return value;
|
|
338
|
+
}
|
|
339
|
+
|
|
340
|
+
// A declared struct or array needs an actual object/array value. Split
|
|
341
|
+
// into two distinct messages (rather than interpolating `isArray` into
|
|
342
|
+
// one) so each is independently greppable.
|
|
343
|
+
if (typeof value !== 'object' || value === null) {
|
|
344
|
+
if (isArray) {
|
|
345
|
+
throw new Error(
|
|
346
|
+
`Expected an array for EIP-712 type ${quoteName(fieldType)}, got ${describeValue(value)}`,
|
|
347
|
+
);
|
|
348
|
+
} else {
|
|
349
|
+
throw new Error(
|
|
350
|
+
`Expected an object for EIP-712 type ${quoteName(fieldType)}, got ${describeValue(value)}`,
|
|
351
|
+
);
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
let type ;
|
|
356
|
+
let result ;
|
|
357
|
+
if (isArray) {
|
|
358
|
+
const actualLength = value.length;
|
|
359
|
+
if (typeof actualLength !== 'number' || Number.isNaN(actualLength)) {
|
|
360
|
+
throw new Error(
|
|
361
|
+
`Expected an array-like value (with a numeric \`length\`) for EIP-712 type ${quoteName(fieldType)}`,
|
|
362
|
+
);
|
|
363
|
+
}
|
|
364
|
+
if (requiredLength !== undefined) {
|
|
365
|
+
// Too few is missing data: rejected in every mode, 'keep' included.
|
|
366
|
+
if (actualLength < requiredLength) {
|
|
367
|
+
throw new Error(
|
|
368
|
+
`Array field ${quoteName(fieldType)} has ${actualLength} elements, expected at least ${requiredLength}`,
|
|
369
|
+
);
|
|
370
|
+
}
|
|
371
|
+
// Too many is excess data: only 'throw' rejects it ('drop' truncates
|
|
372
|
+
// below, 'keep' leaves it alone).
|
|
373
|
+
if (onExtraField === 'throw' && actualLength > requiredLength) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
`Array field ${quoteName(fieldType)} has ${actualLength} elements, expected at most ${requiredLength}`,
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
}
|
|
379
|
+
const length = keep
|
|
380
|
+
? actualLength
|
|
381
|
+
: Math.min(actualLength, requiredLength ?? actualLength);
|
|
382
|
+
result = keep ? value : new Array(length);
|
|
383
|
+
type = Array.from({ length }, () => ({
|
|
384
|
+
state: 'required' ,
|
|
385
|
+
type: base,
|
|
386
|
+
})) ;
|
|
387
|
+
} else {
|
|
388
|
+
// Guaranteed defined (the `!isArray && !baseType` case already
|
|
389
|
+
// returned above); this is just for TS's narrowing.
|
|
390
|
+
if (!baseType) {
|
|
391
|
+
throw new Error(`Unrecognized EIP-712 type ${quoteName(fieldType)}`);
|
|
392
|
+
}
|
|
393
|
+
// Plain object, not null-prototype: this ends up in the returned
|
|
394
|
+
// `message`, which may need to be Endo-Passable (e.g. across a Zoe/exo
|
|
395
|
+
// boundary), and a null-prototype object fails that check.
|
|
396
|
+
result = keep ? value : {};
|
|
397
|
+
type = baseType;
|
|
398
|
+
}
|
|
399
|
+
|
|
400
|
+
if (onExtraField === 'throw') {
|
|
401
|
+
const extraKeys = Object.keys(value).filter(key => !(key in type));
|
|
402
|
+
if (extraKeys.length) {
|
|
403
|
+
throw new Error(
|
|
404
|
+
`Unexpected field(s) on EIP-712 type ${quoteName(fieldType)}: ${quoteNameList(extraKeys)}`,
|
|
405
|
+
);
|
|
406
|
+
}
|
|
407
|
+
}
|
|
408
|
+
|
|
409
|
+
// `fieldName in obj`, not `hasOwnProperty`: matches how viem itself reads
|
|
410
|
+
// field values (plain property access resolves the prototype chain).
|
|
411
|
+
// `type` must be null-prototype or a fresh array-literal record (so
|
|
412
|
+
// there's no inherited-property risk to guard against here), but this
|
|
413
|
+
// check protects against future refactorings.
|
|
414
|
+
if (Object.getPrototypeOf(type) && !Array.isArray(type)) {
|
|
415
|
+
throw new Error(
|
|
416
|
+
`EIP-712 type ${quoteName(fieldType)} must be described by a null-prototype object or an array`,
|
|
417
|
+
);
|
|
418
|
+
}
|
|
419
|
+
// eslint-disable-next-line guard-for-in
|
|
420
|
+
for (const fieldName in type) {
|
|
421
|
+
const field = type[fieldName];
|
|
422
|
+
const present = fieldName in value;
|
|
423
|
+
|
|
424
|
+
if (present) {
|
|
425
|
+
if (field.state === 'optional-seen') {
|
|
426
|
+
throw new Error(
|
|
427
|
+
`Field ${quoteName(fieldName)} of EIP-712 type ${quoteName(fieldType)} is present here but was missing on another instance of the same type -- it must be consistently present or consistently absent`,
|
|
428
|
+
);
|
|
429
|
+
}
|
|
430
|
+
field.state = 'required';
|
|
431
|
+
} else if (field.state === 'required') {
|
|
432
|
+
throw new Error(
|
|
433
|
+
`Missing required field ${quoteName(fieldName)} for EIP-712 type ${quoteName(fieldType)}`,
|
|
434
|
+
);
|
|
435
|
+
} else {
|
|
436
|
+
field.state = 'optional-seen';
|
|
437
|
+
continue;
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
const projectedValue = visit(
|
|
441
|
+
field.type,
|
|
442
|
+
value[fieldName],
|
|
443
|
+
getType,
|
|
444
|
+
onExtraField,
|
|
445
|
+
);
|
|
446
|
+
if (!keep) result[fieldName] = projectedValue;
|
|
447
|
+
}
|
|
448
|
+
return result;
|
|
449
|
+
};
|
|
450
|
+
|
|
451
|
+
export const normalizeAndValidateEIP712Data = (
|
|
452
|
+
input ,
|
|
453
|
+
options = {},
|
|
454
|
+
) => {
|
|
455
|
+
const { message, types, primaryType } = input;
|
|
456
|
+
const onExtraField = options.onExtraField ?? 'drop';
|
|
457
|
+
|
|
458
|
+
if (!(primaryType in types)) {
|
|
459
|
+
throw new Error(
|
|
460
|
+
`Unknown EIP-712 primary type ${quoteName(primaryType)} (expected one of ${quoteNameList(Object.keys(types))})`,
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
|
|
464
|
+
// Built lazily as struct types are encountered walking `message`, and
|
|
465
|
+
// shared across every instance of a given type name -- see `visit`.
|
|
466
|
+
const resultTypes = new Map ();
|
|
467
|
+
const getType = typeName => {
|
|
468
|
+
const cached = resultTypes.get(typeName);
|
|
469
|
+
if (cached) return cached;
|
|
470
|
+
const declared = types[typeName];
|
|
471
|
+
if (!declared) return undefined; // primitive leaf
|
|
472
|
+
// Null-prototype so `fieldName in type` in `visit` can't collide with
|
|
473
|
+
// an inherited Object.prototype member (e.g. a field named "toString").
|
|
474
|
+
const fields = Object.create(null);
|
|
475
|
+
for (const field of declared) {
|
|
476
|
+
fields[field.name] = {
|
|
477
|
+
state: field.optional ? 'optional-unseen' : 'required',
|
|
478
|
+
type: field.type,
|
|
479
|
+
};
|
|
480
|
+
}
|
|
481
|
+
resultTypes.set(typeName, fields);
|
|
482
|
+
return fields;
|
|
483
|
+
};
|
|
484
|
+
|
|
485
|
+
const projectedMessage = visit(
|
|
486
|
+
primaryType,
|
|
487
|
+
message,
|
|
488
|
+
getType,
|
|
489
|
+
onExtraField,
|
|
490
|
+
) ;
|
|
491
|
+
|
|
492
|
+
const outputTypes = {};
|
|
493
|
+
for (const [typeName, fields] of resultTypes) {
|
|
494
|
+
outputTypes[typeName] = Object.entries(fields)
|
|
495
|
+
.filter(([, field]) => field.state === 'required')
|
|
496
|
+
.map(([name, field]) => ({ name, type: field.type }));
|
|
497
|
+
}
|
|
498
|
+
if ('EIP712Domain' in types) outputTypes.EIP712Domain = types.EIP712Domain;
|
|
499
|
+
|
|
500
|
+
return { message: projectedMessage, types: outputTypes };
|
|
501
|
+
};
|
package/src/utils/viem.d.ts
CHANGED
package/src/utils/viem.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"viem.d.ts","sourceRoot":"","sources":["viem.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC"}
|
|
1
|
+
{"version":3,"file":"viem.d.ts","sourceRoot":"","sources":["viem.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAEH,cAAc,+BAA+B,CAAC;AAC9C,cAAc,uBAAuB,CAAC;AACtC,cAAc,kCAAkC,CAAC"}
|
package/src/utils/viem.js
CHANGED