ox 0.14.38 → 0.14.39
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/CHANGELOG.md +14 -0
- package/_cjs/tempo/MultisigConfig.js +71 -15
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +42 -36
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +71 -108
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/MultisigConfig.js +140 -71
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/MultisigOperation.js +42 -37
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +84 -143
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +1 -1
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +137 -73
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/MultisigOperation.d.ts +1 -1
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +81 -75
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/package.json +6 -1
- package/tempo/KeyAuthorization.test-d.ts +12 -0
- package/tempo/KeyAuthorization.test.ts +54 -0
- package/tempo/KeyAuthorization.ts +1 -1
- package/tempo/MultisigConfig.test-d/package.json +6 -0
- package/tempo/MultisigConfig.test-d.ts +52 -0
- package/tempo/MultisigConfig.test.ts +291 -263
- package/tempo/MultisigConfig.ts +250 -95
- package/tempo/MultisigOperation.test-d.ts +6 -0
- package/tempo/MultisigOperation.test.ts +113 -72
- package/tempo/MultisigOperation.ts +41 -35
- package/tempo/SignatureEnvelope.test-d.ts +57 -40
- package/tempo/SignatureEnvelope.test.ts +523 -480
- package/tempo/SignatureEnvelope.ts +250 -257
- package/tempo/multisig.e2e.test.ts +113 -86
- package/version.ts +1 -1
|
@@ -1,339 +1,367 @@
|
|
|
1
|
+
import { Hash, Hex, Rlp } from 'ox'
|
|
1
2
|
import { MultisigConfig } from 'ox/tempo'
|
|
2
3
|
import { describe, expect, test } from 'vitest'
|
|
3
4
|
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
const
|
|
7
|
-
const
|
|
8
|
-
|
|
9
|
-
const singleOwnerConfig = {
|
|
5
|
+
const owner_1 = '0x1111111111111111111111111111111111111111'
|
|
6
|
+
const owner_2 = '0x2222222222222222222222222222222222222222'
|
|
7
|
+
const payload = `0x${'42'.repeat(32)}` as const
|
|
8
|
+
const config = MultisigConfig.from({
|
|
9
|
+
owners: [{ owner: owner_1, weight: 1 }],
|
|
10
10
|
threshold: 1,
|
|
11
|
-
|
|
12
|
-
|
|
11
|
+
})
|
|
12
|
+
const account = MultisigConfig.getAddress(config)
|
|
13
13
|
|
|
14
14
|
describe('from', () => {
|
|
15
|
-
test('
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
15
|
+
test('behavior: normalizes an initial configuration', () => {
|
|
16
|
+
expect(
|
|
17
|
+
MultisigConfig.from({
|
|
18
|
+
owners: [
|
|
19
|
+
{ owner: owner_2, weight: 1 },
|
|
20
|
+
{ owner: owner_1, weight: 1 },
|
|
21
|
+
],
|
|
22
|
+
threshold: 2,
|
|
23
|
+
}),
|
|
24
|
+
).toMatchInlineSnapshot(`
|
|
25
|
+
{
|
|
26
|
+
"owners": [
|
|
27
|
+
{
|
|
28
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
29
|
+
"weight": 1,
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
"owner": "0x2222222222222222222222222222222222222222",
|
|
33
|
+
"weight": 1,
|
|
34
|
+
},
|
|
35
|
+
],
|
|
36
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
37
|
+
"threshold": 2,
|
|
38
|
+
"version": 0n,
|
|
39
|
+
}
|
|
40
|
+
`)
|
|
24
41
|
})
|
|
25
42
|
|
|
26
|
-
test('
|
|
43
|
+
test('behavior: normalizes a numeric configuration version', () => {
|
|
44
|
+
expect(
|
|
45
|
+
MultisigConfig.from({ ...config, version: 1 }),
|
|
46
|
+
).toMatchInlineSnapshot(`
|
|
47
|
+
{
|
|
48
|
+
"owners": [
|
|
49
|
+
{
|
|
50
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
51
|
+
"weight": 1,
|
|
52
|
+
},
|
|
53
|
+
],
|
|
54
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
55
|
+
"threshold": 1,
|
|
56
|
+
"version": 1n,
|
|
57
|
+
}
|
|
58
|
+
`)
|
|
59
|
+
})
|
|
60
|
+
|
|
61
|
+
test('error: rejects an invalid configuration', () => {
|
|
62
|
+
expect(() =>
|
|
63
|
+
MultisigConfig.from({ owners: [], threshold: 0 }),
|
|
64
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
65
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: owners cannot be empty.]`,
|
|
66
|
+
)
|
|
67
|
+
})
|
|
68
|
+
|
|
69
|
+
test('error: rejects an invalid numeric version', () => {
|
|
27
70
|
expect(() =>
|
|
28
|
-
MultisigConfig.from({
|
|
29
|
-
).
|
|
71
|
+
MultisigConfig.from({ ...config, version: 1.5 }),
|
|
72
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
73
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: version must be an unsigned 64-bit integer.]`,
|
|
74
|
+
)
|
|
75
|
+
})
|
|
76
|
+
})
|
|
77
|
+
|
|
78
|
+
describe('fromRpc/toRpc', () => {
|
|
79
|
+
test('behavior: round trips a configuration', () => {
|
|
80
|
+
const rpc = MultisigConfig.toRpc({ ...config, version: 1n })
|
|
81
|
+
expect(rpc).toMatchInlineSnapshot(`
|
|
82
|
+
{
|
|
83
|
+
"owners": [
|
|
84
|
+
{
|
|
85
|
+
"owner": "0x1111111111111111111111111111111111111111",
|
|
86
|
+
"weight": 1,
|
|
87
|
+
},
|
|
88
|
+
],
|
|
89
|
+
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
90
|
+
"threshold": 1,
|
|
91
|
+
"version": "0x1",
|
|
92
|
+
}
|
|
93
|
+
`)
|
|
94
|
+
expect(MultisigConfig.fromRpc(rpc)).toStrictEqual({
|
|
95
|
+
...config,
|
|
96
|
+
version: 1n,
|
|
97
|
+
})
|
|
30
98
|
})
|
|
31
99
|
})
|
|
32
100
|
|
|
33
101
|
describe('getAddress', () => {
|
|
34
|
-
test('matches
|
|
35
|
-
expect(
|
|
102
|
+
test('example: matches the frozen version-0 vector', () => {
|
|
103
|
+
expect(account).toMatchInlineSnapshot(
|
|
36
104
|
`"0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da"`,
|
|
37
105
|
)
|
|
38
106
|
})
|
|
39
107
|
|
|
40
|
-
test('
|
|
108
|
+
test('behavior: includes salt, threshold, and owners', () => {
|
|
41
109
|
expect(
|
|
42
110
|
MultisigConfig.getAddress({
|
|
43
|
-
salt: `0x${'42'.repeat(32)}`,
|
|
44
|
-
threshold: 2,
|
|
45
111
|
owners: [
|
|
46
|
-
{ owner:
|
|
47
|
-
{ owner:
|
|
112
|
+
{ owner: owner_1, weight: 1 },
|
|
113
|
+
{ owner: owner_2, weight: 2 },
|
|
48
114
|
],
|
|
115
|
+
salt: `0x${'42'.repeat(32)}`,
|
|
116
|
+
threshold: 2,
|
|
49
117
|
}),
|
|
50
118
|
).toMatchInlineSnapshot(`"0x0773e28146400643e42cb28f6659b74e7c0b451d"`)
|
|
51
119
|
})
|
|
52
120
|
|
|
53
|
-
test('
|
|
54
|
-
expect(
|
|
55
|
-
MultisigConfig.getAddress(
|
|
56
|
-
)
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
test('differs for a different salt', () => {
|
|
60
|
-
expect(MultisigConfig.getAddress(singleOwnerConfig)).not.toBe(
|
|
61
|
-
MultisigConfig.getAddress({
|
|
62
|
-
...singleOwnerConfig,
|
|
63
|
-
salt: `0x${'42'.repeat(32)}`,
|
|
64
|
-
}),
|
|
121
|
+
test('error: rejects a current configuration', () => {
|
|
122
|
+
expect(() =>
|
|
123
|
+
MultisigConfig.getAddress({ ...config, version: 1n }),
|
|
124
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
125
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
|
|
65
126
|
)
|
|
66
127
|
})
|
|
67
128
|
|
|
68
|
-
test('
|
|
69
|
-
|
|
70
|
-
const a = MultisigConfig.getAddress(singleOwnerConfig)
|
|
71
|
-
const b = MultisigConfig.getAddress(MultisigConfig.from(singleOwnerConfig))
|
|
72
|
-
expect(a).toBe(b)
|
|
129
|
+
test('behavior: accepts numeric zero for an initial configuration', () => {
|
|
130
|
+
expect(MultisigConfig.getAddress({ ...config, version: 0 })).toBe(account)
|
|
73
131
|
})
|
|
132
|
+
})
|
|
74
133
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
).
|
|
134
|
+
describe('getCommitment', () => {
|
|
135
|
+
test('example: matches the frozen initial and current vectors', () => {
|
|
136
|
+
expect({
|
|
137
|
+
current: MultisigConfig.getCommitment({ ...config, version: 1n }),
|
|
138
|
+
currentNumber: MultisigConfig.getCommitment({ ...config, version: 1 }),
|
|
139
|
+
initial: MultisigConfig.getCommitment(config),
|
|
140
|
+
}).toMatchInlineSnapshot(`
|
|
141
|
+
{
|
|
142
|
+
"current": "0x6237ca5930f2265d4fb70a0305dd6ceea4df227053b4a62c304489ede946a2f8",
|
|
143
|
+
"currentNumber": "0x6237ca5930f2265d4fb70a0305dd6ceea4df227053b4a62c304489ede946a2f8",
|
|
144
|
+
"initial": "0xa9e7d1e2ad25e227a4de5f38f3bba31d854ffc8efec46aaa8649097a516bb4ee",
|
|
145
|
+
}
|
|
146
|
+
`)
|
|
82
147
|
})
|
|
83
148
|
})
|
|
84
149
|
|
|
85
150
|
describe('getSignPayload', () => {
|
|
86
|
-
test('matches
|
|
151
|
+
test('example: matches the frozen version-0 vector', () => {
|
|
87
152
|
expect(
|
|
88
|
-
MultisigConfig.getSignPayload({
|
|
89
|
-
payload: `0x${'42'.repeat(32)}`,
|
|
90
|
-
initialConfig: singleOwnerConfig,
|
|
91
|
-
}),
|
|
153
|
+
MultisigConfig.getSignPayload({ account, config, payload }),
|
|
92
154
|
).toMatchInlineSnapshot(
|
|
93
155
|
`"0xbf944a7a752b2cfab0418d5fb4591c5a7ff62976488edce11794d7f35fb34f41"`,
|
|
94
156
|
)
|
|
95
157
|
})
|
|
96
158
|
|
|
97
|
-
test('behavior:
|
|
98
|
-
const account = MultisigConfig.getAddress(singleOwnerConfig)
|
|
99
|
-
const payload = `0x${'42'.repeat(32)}` as const
|
|
159
|
+
test('behavior: binds the configuration version', () => {
|
|
100
160
|
expect(
|
|
101
161
|
MultisigConfig.getSignPayload({
|
|
162
|
+
account,
|
|
163
|
+
config: { version: 1 },
|
|
102
164
|
payload,
|
|
103
|
-
initialConfig: singleOwnerConfig,
|
|
104
165
|
}),
|
|
105
|
-
).toBe(MultisigConfig.getSignPayload({
|
|
106
|
-
})
|
|
107
|
-
|
|
108
|
-
test('differs for a different config version', () => {
|
|
109
|
-
const value = {
|
|
110
|
-
payload: `0x${'42'.repeat(32)}` as const,
|
|
111
|
-
initialConfig: singleOwnerConfig,
|
|
112
|
-
}
|
|
113
|
-
expect(MultisigConfig.getSignPayload(value)).not.toBe(
|
|
114
|
-
MultisigConfig.getSignPayload({ ...value, version: 1n }),
|
|
115
|
-
)
|
|
116
|
-
})
|
|
117
|
-
|
|
118
|
-
test('defaults the config version to zero', () => {
|
|
119
|
-
const value = {
|
|
120
|
-
payload: `0x${'42'.repeat(32)}` as const,
|
|
121
|
-
initialConfig: singleOwnerConfig,
|
|
122
|
-
}
|
|
123
|
-
expect(MultisigConfig.getSignPayload(value)).toBe(
|
|
124
|
-
MultisigConfig.getSignPayload({ ...value, version: 0n }),
|
|
125
|
-
)
|
|
166
|
+
).not.toBe(MultisigConfig.getSignPayload({ account, config, payload }))
|
|
126
167
|
})
|
|
127
168
|
})
|
|
128
169
|
|
|
129
|
-
describe('toTuple
|
|
130
|
-
test('
|
|
131
|
-
|
|
132
|
-
|
|
170
|
+
describe('toTuple/fromTuple', () => {
|
|
171
|
+
test('example: matches the frozen initial and current RLP vectors', () => {
|
|
172
|
+
expect({
|
|
173
|
+
current: Rlp.fromHex(MultisigConfig.toTuple({ ...config, version: 1n })),
|
|
174
|
+
initial: Rlp.fromHex(MultisigConfig.toTuple(config)),
|
|
175
|
+
}).toMatchInlineSnapshot(`
|
|
176
|
+
{
|
|
177
|
+
"current": "0xf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d694111111111111111111111111111111111111111101",
|
|
178
|
+
"initial": "0xf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d694111111111111111111111111111111111111111101",
|
|
179
|
+
}
|
|
180
|
+
`)
|
|
181
|
+
})
|
|
182
|
+
|
|
183
|
+
test('behavior: round trips the complete witness', () => {
|
|
184
|
+
const current = MultisigConfig.from({
|
|
133
185
|
owners: [
|
|
134
|
-
{ owner:
|
|
135
|
-
{ owner:
|
|
186
|
+
{ owner: owner_1, weight: 1 },
|
|
187
|
+
{ owner: owner_2, weight: 2 },
|
|
136
188
|
],
|
|
137
|
-
})
|
|
138
|
-
const tuple = MultisigConfig.toTuple(config)
|
|
139
|
-
expect(MultisigConfig.fromTuple(tuple)).toEqual(config)
|
|
140
|
-
})
|
|
141
|
-
|
|
142
|
-
test('encodes each owner as `[owner, weight]`', () => {
|
|
143
|
-
const [, , owners] = MultisigConfig.toTuple(singleOwnerConfig)
|
|
144
|
-
expect(owners[0]).toEqual([owner1, '0x1'])
|
|
145
|
-
})
|
|
146
|
-
|
|
147
|
-
test('encodes salt as a full 32-byte string (first element)', () => {
|
|
148
|
-
const [salt] = MultisigConfig.toTuple(singleOwnerConfig)
|
|
149
|
-
expect(salt).toBe(MultisigConfig.zeroSalt)
|
|
150
|
-
})
|
|
151
|
-
|
|
152
|
-
test('round-trips a non-zero salt', () => {
|
|
153
|
-
const config = MultisigConfig.from({
|
|
154
|
-
...singleOwnerConfig,
|
|
155
189
|
salt: `0x${'42'.repeat(32)}`,
|
|
190
|
+
threshold: 3,
|
|
191
|
+
version: 1n,
|
|
156
192
|
})
|
|
157
|
-
const tuple = MultisigConfig.toTuple(config)
|
|
158
|
-
expect(tuple[0]).toBe(`0x${'42'.repeat(32)}`)
|
|
159
|
-
expect(MultisigConfig.fromTuple(tuple)).toEqual(config)
|
|
160
|
-
})
|
|
161
|
-
})
|
|
162
|
-
|
|
163
|
-
describe('assert / validate', () => {
|
|
164
|
-
test('valid config', () => {
|
|
165
|
-
expect(MultisigConfig.validate(singleOwnerConfig)).toBe(true)
|
|
166
|
-
})
|
|
167
|
-
|
|
168
|
-
test('empty owners', () => {
|
|
169
|
-
expect(MultisigConfig.validate({ threshold: 1, owners: [] })).toBe(false)
|
|
170
|
-
})
|
|
171
|
-
|
|
172
|
-
test('accepts 48 owners', () => {
|
|
173
|
-
const owners = Array.from({ length: 48 }, (_, i) => ({
|
|
174
|
-
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
175
|
-
weight: 1,
|
|
176
|
-
}))
|
|
177
193
|
expect(
|
|
178
|
-
MultisigConfig.
|
|
179
|
-
|
|
180
|
-
owners,
|
|
181
|
-
}),
|
|
182
|
-
).toBe(true)
|
|
183
|
-
})
|
|
184
|
-
|
|
185
|
-
test('too many owners', () => {
|
|
186
|
-
const owners = Array.from({ length: 49 }, (_, i) => ({
|
|
187
|
-
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
188
|
-
weight: 1,
|
|
189
|
-
}))
|
|
190
|
-
expect(MultisigConfig.validate({ threshold: 1, owners })).toBe(false)
|
|
191
|
-
})
|
|
192
|
-
|
|
193
|
-
test('zero threshold', () => {
|
|
194
|
-
expect(
|
|
195
|
-
MultisigConfig.validate({
|
|
196
|
-
threshold: 0,
|
|
197
|
-
owners: singleOwnerConfig.owners,
|
|
198
|
-
}),
|
|
199
|
-
).toBe(false)
|
|
200
|
-
})
|
|
201
|
-
|
|
202
|
-
test('accepts a uint8 threshold above the signature limit', () => {
|
|
203
|
-
expect(
|
|
204
|
-
MultisigConfig.validate({
|
|
205
|
-
threshold: 9,
|
|
206
|
-
owners: [{ owner: owner1, weight: 9 }],
|
|
207
|
-
}),
|
|
208
|
-
).toBe(true)
|
|
209
|
-
})
|
|
210
|
-
|
|
211
|
-
test('accepts the maximum uint8 threshold', () => {
|
|
212
|
-
expect(
|
|
213
|
-
MultisigConfig.validate({
|
|
214
|
-
threshold: MultisigConfig.maxThreshold,
|
|
215
|
-
owners: [{ owner: owner1, weight: MultisigConfig.maxThreshold }],
|
|
216
|
-
}),
|
|
217
|
-
).toBe(true)
|
|
218
|
-
})
|
|
219
|
-
|
|
220
|
-
test('threshold exceeds uint8 maximum', () => {
|
|
221
|
-
expect(
|
|
222
|
-
MultisigConfig.validate({
|
|
223
|
-
threshold: MultisigConfig.maxThreshold + 1,
|
|
224
|
-
owners: [{ owner: owner1, weight: MultisigConfig.maxThreshold }],
|
|
225
|
-
}),
|
|
226
|
-
).toBe(false)
|
|
227
|
-
})
|
|
228
|
-
|
|
229
|
-
test('fractional threshold', () => {
|
|
230
|
-
expect(
|
|
231
|
-
MultisigConfig.validate({
|
|
232
|
-
threshold: 1.5,
|
|
233
|
-
owners: [{ owner: owner1, weight: 2 }],
|
|
234
|
-
}),
|
|
235
|
-
).toBe(false)
|
|
236
|
-
})
|
|
237
|
-
|
|
238
|
-
test('threshold exceeds total weight', () => {
|
|
239
|
-
expect(
|
|
240
|
-
MultisigConfig.validate({
|
|
241
|
-
threshold: 2,
|
|
242
|
-
owners: singleOwnerConfig.owners,
|
|
243
|
-
}),
|
|
244
|
-
).toBe(false)
|
|
245
|
-
})
|
|
246
|
-
|
|
247
|
-
test('threshold cannot require more than eight owners', () => {
|
|
248
|
-
const owners = Array.from({ length: 9 }, (_, i) => ({
|
|
249
|
-
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
250
|
-
weight: 1,
|
|
251
|
-
}))
|
|
252
|
-
expect(MultisigConfig.validate({ threshold: 9, owners })).toBe(false)
|
|
253
|
-
})
|
|
254
|
-
|
|
255
|
-
test('threshold can be reached by eight of more than eight owners', () => {
|
|
256
|
-
const owners = Array.from({ length: 9 }, (_, i) => ({
|
|
257
|
-
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
258
|
-
weight: i === 0 ? 2 : 1,
|
|
259
|
-
}))
|
|
260
|
-
expect(MultisigConfig.validate({ threshold: 9, owners })).toBe(true)
|
|
194
|
+
MultisigConfig.fromTuple(MultisigConfig.toTuple(current)),
|
|
195
|
+
).toStrictEqual(current)
|
|
261
196
|
})
|
|
197
|
+
})
|
|
262
198
|
|
|
263
|
-
|
|
199
|
+
describe('assert/validate', () => {
|
|
200
|
+
test('example: matches the frozen 48-owner boundary vector', () => {
|
|
201
|
+
const boundary = MultisigConfig.from({
|
|
202
|
+
owners: Array.from({ length: 48 }, (_, index) => ({
|
|
203
|
+
owner: `0x${(index + 1).toString(16).padStart(40, '0')}` as const,
|
|
204
|
+
weight: 1,
|
|
205
|
+
})),
|
|
206
|
+
threshold: 8,
|
|
207
|
+
})
|
|
208
|
+
const rlp = Rlp.fromHex(MultisigConfig.toTuple(boundary))
|
|
209
|
+
expect({
|
|
210
|
+
account: MultisigConfig.getAddress(boundary),
|
|
211
|
+
commitment: MultisigConfig.getCommitment(boundary),
|
|
212
|
+
rlpHash: Hash.keccak256(rlp),
|
|
213
|
+
rlpLength: Hex.size(rlp),
|
|
214
|
+
valid: MultisigConfig.validate(boundary),
|
|
215
|
+
}).toMatchInlineSnapshot(`
|
|
216
|
+
{
|
|
217
|
+
"account": "0x6c67c57e0eed05341137dbf88e4e7a90dc46ef50",
|
|
218
|
+
"commitment": "0x0dc47a7ab45ffa21a01bfd115427e26617b5a57d7ccbea57db2fd4537ba96f56",
|
|
219
|
+
"rlpHash": "0xbaf0d030add91caaa10815d2e99c942f1e39b0d199216973781adb4fc1af6955",
|
|
220
|
+
"rlpLength": 1145,
|
|
221
|
+
"valid": true,
|
|
222
|
+
}
|
|
223
|
+
`)
|
|
224
|
+
})
|
|
225
|
+
|
|
226
|
+
test('behavior: accepts the uint8 weight boundary', () => {
|
|
264
227
|
expect(
|
|
265
228
|
MultisigConfig.validate({
|
|
266
|
-
threshold: MultisigConfig.maxThreshold,
|
|
267
229
|
owners: [
|
|
268
|
-
{ owner:
|
|
269
|
-
{ owner:
|
|
230
|
+
{ owner: owner_1, weight: 128 },
|
|
231
|
+
{ owner: owner_2, weight: 127 },
|
|
270
232
|
],
|
|
233
|
+
threshold: 255,
|
|
271
234
|
}),
|
|
272
|
-
).toBe(
|
|
273
|
-
})
|
|
274
|
-
|
|
275
|
-
test('zero owner weight', () => {
|
|
276
|
-
expect(
|
|
277
|
-
MultisigConfig.validate({
|
|
278
|
-
threshold: 1,
|
|
279
|
-
owners: [{ owner: owner1, weight: 0 }],
|
|
280
|
-
}),
|
|
281
|
-
).toBe(false)
|
|
282
|
-
})
|
|
283
|
-
|
|
284
|
-
test('fractional owner weight', () => {
|
|
285
|
-
expect(
|
|
286
|
-
MultisigConfig.validate({
|
|
287
|
-
threshold: 1,
|
|
288
|
-
owners: [{ owner: owner1, weight: 1.5 }],
|
|
289
|
-
}),
|
|
290
|
-
).toBe(false)
|
|
235
|
+
).toBe(true)
|
|
291
236
|
})
|
|
292
237
|
|
|
293
|
-
test(
|
|
294
|
-
|
|
295
|
-
|
|
238
|
+
test.each([
|
|
239
|
+
{ config: { owners: [], threshold: 1 }, name: 'empty owners' },
|
|
240
|
+
{
|
|
241
|
+
config: {
|
|
242
|
+
owners: Array.from({ length: 49 }, (_, index) => ({
|
|
243
|
+
owner: `0x${(index + 1).toString(16).padStart(40, '0')}` as const,
|
|
244
|
+
weight: 1,
|
|
245
|
+
})),
|
|
296
246
|
threshold: 1,
|
|
247
|
+
},
|
|
248
|
+
name: '49 owners',
|
|
249
|
+
},
|
|
250
|
+
{
|
|
251
|
+
config: { owners: config.owners, threshold: 0 },
|
|
252
|
+
name: 'zero threshold',
|
|
253
|
+
},
|
|
254
|
+
{
|
|
255
|
+
config: {
|
|
297
256
|
owners: [
|
|
298
257
|
{
|
|
299
|
-
owner: '0x0000000000000000000000000000000000000000',
|
|
258
|
+
owner: '0x0000000000000000000000000000000000000000' as const,
|
|
300
259
|
weight: 1,
|
|
301
260
|
},
|
|
302
261
|
],
|
|
303
|
-
}),
|
|
304
|
-
).toBe(false)
|
|
305
|
-
})
|
|
306
|
-
|
|
307
|
-
test('unsorted owners', () => {
|
|
308
|
-
expect(
|
|
309
|
-
MultisigConfig.validate({
|
|
310
262
|
threshold: 1,
|
|
263
|
+
},
|
|
264
|
+
name: 'zero owner',
|
|
265
|
+
},
|
|
266
|
+
{
|
|
267
|
+
config: {
|
|
268
|
+
owners: [{ owner: owner_1, weight: 0 }],
|
|
269
|
+
threshold: 1,
|
|
270
|
+
},
|
|
271
|
+
name: 'zero weight',
|
|
272
|
+
},
|
|
273
|
+
{
|
|
274
|
+
config: {
|
|
311
275
|
owners: [
|
|
312
|
-
{ owner:
|
|
313
|
-
{ owner:
|
|
276
|
+
{ owner: owner_1, weight: 1 },
|
|
277
|
+
{ owner: owner_1, weight: 1 },
|
|
314
278
|
],
|
|
315
|
-
}),
|
|
316
|
-
).toBe(false)
|
|
317
|
-
})
|
|
318
|
-
|
|
319
|
-
test('duplicate owners', () => {
|
|
320
|
-
expect(
|
|
321
|
-
MultisigConfig.validate({
|
|
322
279
|
threshold: 1,
|
|
280
|
+
},
|
|
281
|
+
name: 'duplicate owner',
|
|
282
|
+
},
|
|
283
|
+
{
|
|
284
|
+
config: {
|
|
323
285
|
owners: [
|
|
324
|
-
{ owner:
|
|
325
|
-
{ owner:
|
|
286
|
+
{ owner: owner_2, weight: 1 },
|
|
287
|
+
{ owner: owner_1, weight: 1 },
|
|
326
288
|
],
|
|
327
|
-
|
|
328
|
-
|
|
289
|
+
threshold: 1,
|
|
290
|
+
},
|
|
291
|
+
name: 'unsorted owners',
|
|
292
|
+
},
|
|
293
|
+
{
|
|
294
|
+
config: {
|
|
295
|
+
owners: [
|
|
296
|
+
{ owner: owner_1, weight: 128 },
|
|
297
|
+
{ owner: owner_2, weight: 128 },
|
|
298
|
+
],
|
|
299
|
+
threshold: 255,
|
|
300
|
+
},
|
|
301
|
+
name: 'weight overflow',
|
|
302
|
+
},
|
|
303
|
+
{
|
|
304
|
+
config: {
|
|
305
|
+
owners: Array.from({ length: 9 }, (_, index) => ({
|
|
306
|
+
owner:
|
|
307
|
+
`0x${(index + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
308
|
+
weight: 1,
|
|
309
|
+
})),
|
|
310
|
+
threshold: 9,
|
|
311
|
+
},
|
|
312
|
+
name: 'unreachable threshold',
|
|
313
|
+
},
|
|
314
|
+
{
|
|
315
|
+
config: { ...config, salt: '0x42' as const },
|
|
316
|
+
name: 'short salt',
|
|
317
|
+
},
|
|
318
|
+
{
|
|
319
|
+
config: { ...config, version: -1n },
|
|
320
|
+
name: 'negative version',
|
|
321
|
+
},
|
|
322
|
+
{
|
|
323
|
+
config: { ...config, version: -1 },
|
|
324
|
+
name: 'negative numeric version',
|
|
325
|
+
},
|
|
326
|
+
{
|
|
327
|
+
config: { ...config, version: MultisigConfig.maxVersion + 1n },
|
|
328
|
+
name: 'version overflow',
|
|
329
|
+
},
|
|
330
|
+
{
|
|
331
|
+
config: { ...config, version: 1.5 },
|
|
332
|
+
name: 'fractional numeric version',
|
|
333
|
+
},
|
|
334
|
+
{
|
|
335
|
+
config: { ...config, version: Number.MAX_SAFE_INTEGER + 1 },
|
|
336
|
+
name: 'unsafe numeric version',
|
|
337
|
+
},
|
|
338
|
+
])('error: rejects $name', ({ config }) => {
|
|
339
|
+
expect(MultisigConfig.validate(config as MultisigConfig.Input)).toBe(false)
|
|
329
340
|
})
|
|
341
|
+
})
|
|
330
342
|
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
343
|
+
test('exports', () => {
|
|
344
|
+
expect(Object.keys(MultisigConfig)).toMatchInlineSnapshot(`
|
|
345
|
+
[
|
|
346
|
+
"maxNestingDepth",
|
|
347
|
+
"maxOwnerSignatureBytes",
|
|
348
|
+
"maxOwners",
|
|
349
|
+
"maxSignatures",
|
|
350
|
+
"maxThreshold",
|
|
351
|
+
"maxVersion",
|
|
352
|
+
"signatureTypeByte",
|
|
353
|
+
"zeroSalt",
|
|
354
|
+
"assert",
|
|
355
|
+
"from",
|
|
356
|
+
"fromRpc",
|
|
357
|
+
"fromTuple",
|
|
358
|
+
"getAddress",
|
|
359
|
+
"getCommitment",
|
|
360
|
+
"getSignPayload",
|
|
361
|
+
"toRpc",
|
|
362
|
+
"toTuple",
|
|
363
|
+
"validate",
|
|
364
|
+
"InvalidConfigError",
|
|
365
|
+
]
|
|
366
|
+
`)
|
|
339
367
|
})
|