ox 0.14.37 → 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 +29 -0
- package/_cjs/tempo/MultisigConfig.js +71 -15
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/MultisigOperation.js +241 -19
- 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 +331 -19
- 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/tempo/index.js +2 -2
- 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 +169 -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/tempo/index.d.ts +2 -2
- 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 +25 -0
- package/tempo/MultisigOperation.test.ts +754 -22
- package/tempo/MultisigOperation.ts +548 -17
- package/tempo/SignatureEnvelope.test-d.ts +57 -40
- package/tempo/SignatureEnvelope.test.ts +523 -480
- package/tempo/SignatureEnvelope.ts +250 -257
- package/tempo/index.ts +2 -2
- package/tempo/multisig.e2e.test.ts +113 -86
- package/version.ts +1 -1
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Address, P256 } from 'ox'
|
|
1
|
+
import { Address, Hash, P256 } from 'ox'
|
|
2
2
|
import {
|
|
3
3
|
KeyAuthorization,
|
|
4
4
|
MultisigConfig,
|
|
@@ -10,11 +10,14 @@ import { describe, expect, test } from 'vitest'
|
|
|
10
10
|
|
|
11
11
|
const owners = [1n, 2n, 3n]
|
|
12
12
|
.map((value, index) => {
|
|
13
|
+
const privateKey = `0x${value.toString(16).padStart(64, '0')}` as const
|
|
13
14
|
const publicKey = P256.getPublicKey({
|
|
14
|
-
privateKey
|
|
15
|
+
privateKey,
|
|
15
16
|
})
|
|
16
17
|
return {
|
|
17
18
|
address: Address.fromPublicKey(publicKey),
|
|
19
|
+
privateKey,
|
|
20
|
+
publicKey,
|
|
18
21
|
signature: {
|
|
19
22
|
prehash: false,
|
|
20
23
|
publicKey,
|
|
@@ -33,6 +36,7 @@ const config = MultisigConfig.from({
|
|
|
33
36
|
],
|
|
34
37
|
threshold: 2,
|
|
35
38
|
})
|
|
39
|
+
const initializedConfig = MultisigConfig.from({ ...config, version: 1n })
|
|
36
40
|
const account = MultisigConfig.getAddress(config)
|
|
37
41
|
const ownerSignature_1 = owners[0]!.signature
|
|
38
42
|
const approval_1 = SignatureEnvelope.serialize(ownerSignature_1)
|
|
@@ -59,23 +63,23 @@ const keyAuthorization = KeyAuthorization.serialize(
|
|
|
59
63
|
|
|
60
64
|
const transactionHash_ = MultisigConfig.getSignPayload({
|
|
61
65
|
account,
|
|
66
|
+
config: initializedConfig,
|
|
62
67
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
63
68
|
TxEnvelopeTempo.deserialize(transaction),
|
|
64
69
|
),
|
|
65
|
-
version: 1n,
|
|
66
70
|
})
|
|
67
71
|
const keyAuthorizationHash = MultisigConfig.getSignPayload({
|
|
68
72
|
account,
|
|
73
|
+
config: initializedConfig,
|
|
69
74
|
payload: KeyAuthorization.getSignPayload(
|
|
70
75
|
KeyAuthorization.deserialize(keyAuthorization),
|
|
71
76
|
),
|
|
72
|
-
version: 1n,
|
|
73
77
|
})
|
|
74
78
|
|
|
75
79
|
const base = {
|
|
76
80
|
account,
|
|
77
81
|
approvals: [approval_1],
|
|
78
|
-
config,
|
|
82
|
+
config: initializedConfig,
|
|
79
83
|
configVersion: 1n,
|
|
80
84
|
createdAt: 1,
|
|
81
85
|
init: false,
|
|
@@ -101,6 +105,701 @@ const keyAuthorizationPending = {
|
|
|
101
105
|
type: 'keyAuthorization',
|
|
102
106
|
} as const
|
|
103
107
|
|
|
108
|
+
function signApproval(
|
|
109
|
+
owner: (typeof owners)[number],
|
|
110
|
+
hash: `0x${string}`,
|
|
111
|
+
extraEntropy: false | `0x${string}` = false,
|
|
112
|
+
) {
|
|
113
|
+
return SignatureEnvelope.serialize({
|
|
114
|
+
prehash: false,
|
|
115
|
+
publicKey: owner.publicKey,
|
|
116
|
+
signature: P256.sign({
|
|
117
|
+
extraEntropy,
|
|
118
|
+
payload: hash,
|
|
119
|
+
privateKey: owner.privateKey,
|
|
120
|
+
}),
|
|
121
|
+
type: 'p256',
|
|
122
|
+
})
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
function approvalAddresses(
|
|
126
|
+
approvals: readonly `0x${string}`[],
|
|
127
|
+
hash: `0x${string}`,
|
|
128
|
+
) {
|
|
129
|
+
return approvals.map((approval) =>
|
|
130
|
+
SignatureEnvelope.extractAddress({
|
|
131
|
+
payload: hash,
|
|
132
|
+
signature: SignatureEnvelope.deserialize(approval),
|
|
133
|
+
}),
|
|
134
|
+
)
|
|
135
|
+
}
|
|
136
|
+
|
|
137
|
+
describe('getHash', () => {
|
|
138
|
+
test('transaction and key authorization', () => {
|
|
139
|
+
expect({
|
|
140
|
+
keyAuthorization: MultisigOperation.getHash({
|
|
141
|
+
account,
|
|
142
|
+
configVersion: 1n,
|
|
143
|
+
keyAuthorization,
|
|
144
|
+
type: 'keyAuthorization',
|
|
145
|
+
}),
|
|
146
|
+
transaction: MultisigOperation.getHash({
|
|
147
|
+
account,
|
|
148
|
+
configVersion: 1n,
|
|
149
|
+
transaction,
|
|
150
|
+
type: 'transaction',
|
|
151
|
+
}),
|
|
152
|
+
}).toMatchInlineSnapshot(`
|
|
153
|
+
{
|
|
154
|
+
"keyAuthorization": "0xf6995eb69c12c03d3d13b357abf7cac22b4effe52fba8ff02e5aef26161f77a0",
|
|
155
|
+
"transaction": "0xcdbc24a8fb192f799c5d166b13a99fb29cbb15e71a5e730988d6f8b3c5959d02",
|
|
156
|
+
}
|
|
157
|
+
`)
|
|
158
|
+
})
|
|
159
|
+
})
|
|
160
|
+
|
|
161
|
+
describe('selectApprovals', () => {
|
|
162
|
+
test('selects a deterministic weighted quorum', async () => {
|
|
163
|
+
const config = MultisigConfig.from({
|
|
164
|
+
owners: [
|
|
165
|
+
{ owner: owners[0]!.address, weight: 2 },
|
|
166
|
+
{ owner: owners[1]!.address, weight: 1 },
|
|
167
|
+
{ owner: owners[2]!.address, weight: 1 },
|
|
168
|
+
],
|
|
169
|
+
threshold: 3,
|
|
170
|
+
})
|
|
171
|
+
const account = MultisigConfig.getAddress(config)
|
|
172
|
+
const hash = MultisigOperation.getHash({
|
|
173
|
+
account,
|
|
174
|
+
configVersion: 1n,
|
|
175
|
+
transaction,
|
|
176
|
+
type: 'transaction',
|
|
177
|
+
})
|
|
178
|
+
const approvals = owners.map((owner) => signApproval(owner, hash))
|
|
179
|
+
const alternate = signApproval(owners[0]!, hash, `0x${'01'.repeat(32)}`)
|
|
180
|
+
const selection = await MultisigOperation.selectApprovals({
|
|
181
|
+
account,
|
|
182
|
+
approvals: [
|
|
183
|
+
approvals[2]!,
|
|
184
|
+
approvals[0]!,
|
|
185
|
+
approvals[1]!,
|
|
186
|
+
alternate,
|
|
187
|
+
approvals[0]!,
|
|
188
|
+
],
|
|
189
|
+
config,
|
|
190
|
+
hash,
|
|
191
|
+
})
|
|
192
|
+
const reversed = await MultisigOperation.selectApprovals({
|
|
193
|
+
account,
|
|
194
|
+
approvals: [
|
|
195
|
+
approvals[0]!,
|
|
196
|
+
alternate,
|
|
197
|
+
approvals[1]!,
|
|
198
|
+
approvals[0]!,
|
|
199
|
+
approvals[2]!,
|
|
200
|
+
],
|
|
201
|
+
config,
|
|
202
|
+
hash,
|
|
203
|
+
})
|
|
204
|
+
|
|
205
|
+
expect(reversed).toStrictEqual(selection)
|
|
206
|
+
expect({
|
|
207
|
+
...selection,
|
|
208
|
+
approvals: approvalAddresses(selection.approvals, hash),
|
|
209
|
+
selectedApprovals: approvalAddresses(selection.selectedApprovals, hash),
|
|
210
|
+
}).toMatchInlineSnapshot(`
|
|
211
|
+
{
|
|
212
|
+
"approvals": [
|
|
213
|
+
"0x07e1ed8ea0e9601e5546b0a03aed683df3601407",
|
|
214
|
+
"0x288f0cd85005f34168f731a468aef268c2f9456f",
|
|
215
|
+
"0xd3a9f047ad43d7e2e4e7e491f1fe2e657a2651b6",
|
|
216
|
+
],
|
|
217
|
+
"selectedApprovals": [
|
|
218
|
+
"0x07e1ed8ea0e9601e5546b0a03aed683df3601407",
|
|
219
|
+
"0x288f0cd85005f34168f731a468aef268c2f9456f",
|
|
220
|
+
],
|
|
221
|
+
"signatureCount": 2,
|
|
222
|
+
"threshold": 3,
|
|
223
|
+
"weight": 3,
|
|
224
|
+
}
|
|
225
|
+
`)
|
|
226
|
+
})
|
|
227
|
+
|
|
228
|
+
test('counts a nested owner only after its quorum', async () => {
|
|
229
|
+
const childConfig = MultisigConfig.from({
|
|
230
|
+
owners: [
|
|
231
|
+
{ owner: owners[1]!.address, weight: 1 },
|
|
232
|
+
{ owner: owners[2]!.address, weight: 1 },
|
|
233
|
+
],
|
|
234
|
+
threshold: 2,
|
|
235
|
+
})
|
|
236
|
+
const child = MultisigConfig.getAddress(childConfig)
|
|
237
|
+
const config = MultisigConfig.from({
|
|
238
|
+
owners: [
|
|
239
|
+
{ owner: owners[0]!.address, weight: 1 },
|
|
240
|
+
{ owner: child, weight: 2 },
|
|
241
|
+
],
|
|
242
|
+
threshold: 2,
|
|
243
|
+
})
|
|
244
|
+
const account = MultisigConfig.getAddress(config)
|
|
245
|
+
const hash = MultisigOperation.getHash({
|
|
246
|
+
account,
|
|
247
|
+
configVersion: 1n,
|
|
248
|
+
transaction,
|
|
249
|
+
type: 'transaction',
|
|
250
|
+
})
|
|
251
|
+
const childHash = MultisigConfig.getSignPayload({
|
|
252
|
+
account: child,
|
|
253
|
+
config: { version: 2n },
|
|
254
|
+
payload: hash,
|
|
255
|
+
})
|
|
256
|
+
const childApprovals = [
|
|
257
|
+
signApproval(owners[1]!, childHash),
|
|
258
|
+
signApproval(owners[2]!, childHash),
|
|
259
|
+
]
|
|
260
|
+
const rootApproval = signApproval(owners[0]!, hash)
|
|
261
|
+
const resolveConfig: MultisigOperation.selectApprovals.ResolveConfig = ({
|
|
262
|
+
account,
|
|
263
|
+
}) => {
|
|
264
|
+
if (!Address.isEqual(account, child)) throw new Error('unknown account')
|
|
265
|
+
return { config: childConfig, version: 2n }
|
|
266
|
+
}
|
|
267
|
+
const partial = await MultisigOperation.selectApprovals({
|
|
268
|
+
account,
|
|
269
|
+
approvals: [
|
|
270
|
+
rootApproval,
|
|
271
|
+
SignatureEnvelope.serialize({
|
|
272
|
+
account: child,
|
|
273
|
+
config: MultisigConfig.from({ ...childConfig, version: 2n }),
|
|
274
|
+
signatures: [SignatureEnvelope.deserialize(childApprovals[0]!)],
|
|
275
|
+
type: 'multisig',
|
|
276
|
+
}),
|
|
277
|
+
],
|
|
278
|
+
config,
|
|
279
|
+
hash,
|
|
280
|
+
resolveConfig,
|
|
281
|
+
})
|
|
282
|
+
const complete = await MultisigOperation.selectApprovals({
|
|
283
|
+
account,
|
|
284
|
+
approvals: [
|
|
285
|
+
rootApproval,
|
|
286
|
+
SignatureEnvelope.serialize({
|
|
287
|
+
account: child,
|
|
288
|
+
config: MultisigConfig.from({ ...childConfig, version: 2n }),
|
|
289
|
+
signatures: childApprovals.map((approval) =>
|
|
290
|
+
SignatureEnvelope.deserialize(approval),
|
|
291
|
+
),
|
|
292
|
+
type: 'multisig',
|
|
293
|
+
}),
|
|
294
|
+
],
|
|
295
|
+
config,
|
|
296
|
+
hash,
|
|
297
|
+
resolveConfig,
|
|
298
|
+
})
|
|
299
|
+
|
|
300
|
+
expect({
|
|
301
|
+
complete: {
|
|
302
|
+
...complete,
|
|
303
|
+
approvals: approvalAddresses(complete.approvals, hash),
|
|
304
|
+
selectedApprovals: approvalAddresses(complete.selectedApprovals, hash),
|
|
305
|
+
},
|
|
306
|
+
partial: {
|
|
307
|
+
...partial,
|
|
308
|
+
approvals: approvalAddresses(partial.approvals, hash),
|
|
309
|
+
selectedApprovals: approvalAddresses(partial.selectedApprovals, hash),
|
|
310
|
+
},
|
|
311
|
+
}).toMatchInlineSnapshot(`
|
|
312
|
+
{
|
|
313
|
+
"complete": {
|
|
314
|
+
"approvals": [
|
|
315
|
+
"0x07e1ed8ea0e9601e5546b0a03aed683df3601407",
|
|
316
|
+
"0xe2d2c3c2fc4b17af341cc5c1a459af9606167e8a",
|
|
317
|
+
],
|
|
318
|
+
"selectedApprovals": [
|
|
319
|
+
"0xe2d2c3c2fc4b17af341cc5c1a459af9606167e8a",
|
|
320
|
+
],
|
|
321
|
+
"signatureCount": 1,
|
|
322
|
+
"threshold": 2,
|
|
323
|
+
"weight": 2,
|
|
324
|
+
},
|
|
325
|
+
"partial": {
|
|
326
|
+
"approvals": [
|
|
327
|
+
"0x07e1ed8ea0e9601e5546b0a03aed683df3601407",
|
|
328
|
+
"0xe2d2c3c2fc4b17af341cc5c1a459af9606167e8a",
|
|
329
|
+
],
|
|
330
|
+
"selectedApprovals": [
|
|
331
|
+
"0x07e1ed8ea0e9601e5546b0a03aed683df3601407",
|
|
332
|
+
],
|
|
333
|
+
"signatureCount": 1,
|
|
334
|
+
"threshold": 2,
|
|
335
|
+
"weight": 1,
|
|
336
|
+
},
|
|
337
|
+
}
|
|
338
|
+
`)
|
|
339
|
+
})
|
|
340
|
+
|
|
341
|
+
test('rejects invalid and non-owner approvals', async () => {
|
|
342
|
+
const hash = MultisigOperation.getHash({
|
|
343
|
+
account,
|
|
344
|
+
configVersion: 1n,
|
|
345
|
+
transaction,
|
|
346
|
+
type: 'transaction',
|
|
347
|
+
})
|
|
348
|
+
const invalid = signApproval(
|
|
349
|
+
owners[0]!,
|
|
350
|
+
`0x${'ff'.repeat(32)}` as `0x${string}`,
|
|
351
|
+
)
|
|
352
|
+
const nonOwner = signApproval(owners[2]!, hash)
|
|
353
|
+
|
|
354
|
+
await expect(
|
|
355
|
+
MultisigOperation.selectApprovals({
|
|
356
|
+
account,
|
|
357
|
+
approvals: [invalid],
|
|
358
|
+
config,
|
|
359
|
+
hash,
|
|
360
|
+
}),
|
|
361
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
362
|
+
`[MultisigOperation.InvalidApprovalError: Invalid multisig approval: signature from owner 0x07e1ed8ea0e9601e5546b0a03aed683df3601407 is invalid.]`,
|
|
363
|
+
)
|
|
364
|
+
await expect(
|
|
365
|
+
MultisigOperation.selectApprovals({
|
|
366
|
+
account,
|
|
367
|
+
approvals: [nonOwner],
|
|
368
|
+
config,
|
|
369
|
+
hash,
|
|
370
|
+
}),
|
|
371
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
372
|
+
`[MultisigOperation.InvalidApprovalError: Invalid multisig approval: signature is from non-owner 0xd3a9f047ad43d7e2e4e7e491f1fe2e657a2651b6.]`,
|
|
373
|
+
)
|
|
374
|
+
})
|
|
375
|
+
|
|
376
|
+
test('requires a resolver for nested owners', async () => {
|
|
377
|
+
const childConfig = MultisigConfig.from({
|
|
378
|
+
owners: [{ owner: owners[1]!.address, weight: 1 }],
|
|
379
|
+
threshold: 1,
|
|
380
|
+
})
|
|
381
|
+
const child = MultisigConfig.getAddress(childConfig)
|
|
382
|
+
const config = MultisigConfig.from({
|
|
383
|
+
owners: [{ owner: child, weight: 1 }],
|
|
384
|
+
threshold: 1,
|
|
385
|
+
})
|
|
386
|
+
const account = MultisigConfig.getAddress(config)
|
|
387
|
+
const hash = MultisigOperation.getHash({
|
|
388
|
+
account,
|
|
389
|
+
configVersion: 1n,
|
|
390
|
+
transaction,
|
|
391
|
+
type: 'transaction',
|
|
392
|
+
})
|
|
393
|
+
const childHash = MultisigConfig.getSignPayload({
|
|
394
|
+
account: child,
|
|
395
|
+
config: { version: 1n },
|
|
396
|
+
payload: hash,
|
|
397
|
+
})
|
|
398
|
+
|
|
399
|
+
await expect(
|
|
400
|
+
MultisigOperation.selectApprovals({
|
|
401
|
+
account,
|
|
402
|
+
approvals: [
|
|
403
|
+
SignatureEnvelope.serialize({
|
|
404
|
+
account: child,
|
|
405
|
+
config: MultisigConfig.from({ ...childConfig, version: 1n }),
|
|
406
|
+
signatures: [
|
|
407
|
+
SignatureEnvelope.deserialize(
|
|
408
|
+
signApproval(owners[1]!, childHash),
|
|
409
|
+
),
|
|
410
|
+
],
|
|
411
|
+
type: 'multisig',
|
|
412
|
+
}),
|
|
413
|
+
],
|
|
414
|
+
config,
|
|
415
|
+
hash,
|
|
416
|
+
}),
|
|
417
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
418
|
+
`[MultisigOperation.InvalidApprovalError: Invalid multisig approval: nested multisig owner 0x7888d60e9cc26c8569d394fce435c248f1e49c3b requires a config resolver.]`,
|
|
419
|
+
)
|
|
420
|
+
})
|
|
421
|
+
|
|
422
|
+
test('rejects keychain and accepts initial nested approvals', async () => {
|
|
423
|
+
const childConfig = MultisigConfig.from({
|
|
424
|
+
owners: [{ owner: owners[1]!.address, weight: 1 }],
|
|
425
|
+
threshold: 1,
|
|
426
|
+
})
|
|
427
|
+
const child = MultisigConfig.getAddress(childConfig)
|
|
428
|
+
const config = MultisigConfig.from({
|
|
429
|
+
owners: [
|
|
430
|
+
{ owner: owners[0]!.address, weight: 1 },
|
|
431
|
+
{ owner: child, weight: 1 },
|
|
432
|
+
],
|
|
433
|
+
threshold: 1,
|
|
434
|
+
})
|
|
435
|
+
const account = MultisigConfig.getAddress(config)
|
|
436
|
+
const hash = MultisigOperation.getHash({
|
|
437
|
+
account,
|
|
438
|
+
configVersion: 1n,
|
|
439
|
+
transaction,
|
|
440
|
+
type: 'transaction',
|
|
441
|
+
})
|
|
442
|
+
const childHash = MultisigConfig.getSignPayload({
|
|
443
|
+
account: child,
|
|
444
|
+
config: childConfig,
|
|
445
|
+
payload: hash,
|
|
446
|
+
})
|
|
447
|
+
|
|
448
|
+
await expect(
|
|
449
|
+
MultisigOperation.selectApprovals({
|
|
450
|
+
account,
|
|
451
|
+
approvals: [
|
|
452
|
+
SignatureEnvelope.serialize({
|
|
453
|
+
inner: SignatureEnvelope.deserialize(
|
|
454
|
+
signApproval(owners[0]!, hash),
|
|
455
|
+
),
|
|
456
|
+
type: 'keychain',
|
|
457
|
+
userAddress: owners[0]!.address,
|
|
458
|
+
}),
|
|
459
|
+
],
|
|
460
|
+
config,
|
|
461
|
+
hash,
|
|
462
|
+
}),
|
|
463
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
464
|
+
`[MultisigOperation.InvalidApprovalError: Invalid multisig approval: keychain signatures cannot approve a multisig operation.]`,
|
|
465
|
+
)
|
|
466
|
+
const selection = await MultisigOperation.selectApprovals({
|
|
467
|
+
account,
|
|
468
|
+
approvals: [
|
|
469
|
+
SignatureEnvelope.serialize({
|
|
470
|
+
account: child,
|
|
471
|
+
config: childConfig,
|
|
472
|
+
signatures: [
|
|
473
|
+
SignatureEnvelope.deserialize(signApproval(owners[1]!, childHash)),
|
|
474
|
+
],
|
|
475
|
+
type: 'multisig',
|
|
476
|
+
}),
|
|
477
|
+
],
|
|
478
|
+
config,
|
|
479
|
+
hash,
|
|
480
|
+
resolveConfig: () => ({ config: childConfig, version: 0n }),
|
|
481
|
+
})
|
|
482
|
+
expect({
|
|
483
|
+
signatureCount: selection.signatureCount,
|
|
484
|
+
threshold: selection.threshold,
|
|
485
|
+
weight: selection.weight,
|
|
486
|
+
}).toMatchInlineSnapshot(`
|
|
487
|
+
{
|
|
488
|
+
"signatureCount": 1,
|
|
489
|
+
"threshold": 1,
|
|
490
|
+
"weight": 1,
|
|
491
|
+
}
|
|
492
|
+
`)
|
|
493
|
+
})
|
|
494
|
+
|
|
495
|
+
test('rejects nested account cycles while serializing', () => {
|
|
496
|
+
const config = MultisigConfig.from({
|
|
497
|
+
owners: [{ owner: account, weight: 1 }],
|
|
498
|
+
threshold: 1,
|
|
499
|
+
})
|
|
500
|
+
const hash = MultisigOperation.getHash({
|
|
501
|
+
account,
|
|
502
|
+
configVersion: 2n,
|
|
503
|
+
transaction,
|
|
504
|
+
type: 'transaction',
|
|
505
|
+
})
|
|
506
|
+
const nestedHash = MultisigConfig.getSignPayload({
|
|
507
|
+
account,
|
|
508
|
+
config: { version: 2n },
|
|
509
|
+
payload: hash,
|
|
510
|
+
})
|
|
511
|
+
|
|
512
|
+
expect(() =>
|
|
513
|
+
SignatureEnvelope.serialize({
|
|
514
|
+
account,
|
|
515
|
+
config: MultisigConfig.from({ ...config, version: 2n }),
|
|
516
|
+
signatures: [
|
|
517
|
+
SignatureEnvelope.deserialize(signApproval(owners[0]!, nestedHash)),
|
|
518
|
+
],
|
|
519
|
+
type: 'multisig',
|
|
520
|
+
}),
|
|
521
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
522
|
+
`[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig account cannot be an owner.]`,
|
|
523
|
+
)
|
|
524
|
+
})
|
|
525
|
+
|
|
526
|
+
test('rejects excess nesting depth', async () => {
|
|
527
|
+
const grandchildConfig = MultisigConfig.from({
|
|
528
|
+
owners: [{ owner: owners[2]!.address, weight: 1 }],
|
|
529
|
+
threshold: 1,
|
|
530
|
+
})
|
|
531
|
+
const grandchild = MultisigConfig.getAddress(grandchildConfig)
|
|
532
|
+
const childConfig = MultisigConfig.from({
|
|
533
|
+
owners: [{ owner: grandchild, weight: 1 }],
|
|
534
|
+
threshold: 1,
|
|
535
|
+
})
|
|
536
|
+
const child = MultisigConfig.getAddress(childConfig)
|
|
537
|
+
const config = MultisigConfig.from({
|
|
538
|
+
owners: [{ owner: child, weight: 1 }],
|
|
539
|
+
threshold: 1,
|
|
540
|
+
})
|
|
541
|
+
const account = MultisigConfig.getAddress(config)
|
|
542
|
+
const hash = MultisigOperation.getHash({
|
|
543
|
+
account,
|
|
544
|
+
configVersion: 1n,
|
|
545
|
+
transaction,
|
|
546
|
+
type: 'transaction',
|
|
547
|
+
})
|
|
548
|
+
const childHash = MultisigConfig.getSignPayload({
|
|
549
|
+
account: child,
|
|
550
|
+
config: { version: 1n },
|
|
551
|
+
payload: hash,
|
|
552
|
+
})
|
|
553
|
+
const grandchildHash = MultisigConfig.getSignPayload({
|
|
554
|
+
account: grandchild,
|
|
555
|
+
config: { version: 1n },
|
|
556
|
+
payload: childHash,
|
|
557
|
+
})
|
|
558
|
+
|
|
559
|
+
await expect(
|
|
560
|
+
MultisigOperation.selectApprovals({
|
|
561
|
+
account,
|
|
562
|
+
approvals: [
|
|
563
|
+
SignatureEnvelope.serialize({
|
|
564
|
+
account: child,
|
|
565
|
+
config: MultisigConfig.from({ ...childConfig, version: 1n }),
|
|
566
|
+
signatures: [
|
|
567
|
+
{
|
|
568
|
+
account: grandchild,
|
|
569
|
+
config: MultisigConfig.from({
|
|
570
|
+
...grandchildConfig,
|
|
571
|
+
version: 1n,
|
|
572
|
+
}),
|
|
573
|
+
signatures: [
|
|
574
|
+
SignatureEnvelope.deserialize(
|
|
575
|
+
signApproval(owners[2]!, grandchildHash),
|
|
576
|
+
),
|
|
577
|
+
],
|
|
578
|
+
type: 'multisig',
|
|
579
|
+
},
|
|
580
|
+
],
|
|
581
|
+
type: 'multisig',
|
|
582
|
+
}),
|
|
583
|
+
],
|
|
584
|
+
config,
|
|
585
|
+
hash,
|
|
586
|
+
resolveConfig: ({ account }) => {
|
|
587
|
+
if (Address.isEqual(account, child))
|
|
588
|
+
return { config: childConfig, version: 1n }
|
|
589
|
+
return { config: grandchildConfig, version: 1n }
|
|
590
|
+
},
|
|
591
|
+
}),
|
|
592
|
+
).rejects.toThrowErrorMatchingInlineSnapshot(
|
|
593
|
+
`[MultisigOperation.InvalidApprovalError: Invalid multisig approval: nested multisig owner 0xf529c8f6b2b4c72102af4cfe5eeb55b7d45dede2 is invalid.]`,
|
|
594
|
+
)
|
|
595
|
+
})
|
|
596
|
+
})
|
|
597
|
+
|
|
598
|
+
describe('serializeTransaction', () => {
|
|
599
|
+
test('initialized and bootstrap transactions', async () => {
|
|
600
|
+
const results = []
|
|
601
|
+
for (const init of [false, true]) {
|
|
602
|
+
const configVersion = init ? 0n : 1n
|
|
603
|
+
const hash = MultisigOperation.getHash({
|
|
604
|
+
account,
|
|
605
|
+
configVersion,
|
|
606
|
+
transaction,
|
|
607
|
+
type: 'transaction',
|
|
608
|
+
})
|
|
609
|
+
const selection = await MultisigOperation.selectApprovals({
|
|
610
|
+
account,
|
|
611
|
+
approvals: [
|
|
612
|
+
signApproval(owners[1]!, hash),
|
|
613
|
+
signApproval(owners[0]!, hash),
|
|
614
|
+
],
|
|
615
|
+
config,
|
|
616
|
+
hash,
|
|
617
|
+
})
|
|
618
|
+
const operation = MultisigOperation.from({
|
|
619
|
+
account,
|
|
620
|
+
approvals: selection.approvals,
|
|
621
|
+
config,
|
|
622
|
+
configVersion,
|
|
623
|
+
createdAt: 1,
|
|
624
|
+
hash,
|
|
625
|
+
init,
|
|
626
|
+
signatureCount: selection.signatureCount,
|
|
627
|
+
status: 'pending',
|
|
628
|
+
threshold: selection.threshold,
|
|
629
|
+
transaction,
|
|
630
|
+
type: 'transaction',
|
|
631
|
+
updatedAt: 1,
|
|
632
|
+
weight: selection.weight,
|
|
633
|
+
})
|
|
634
|
+
const serialized = MultisigOperation.serializeTransaction(operation, {
|
|
635
|
+
approvals: selection.selectedApprovals,
|
|
636
|
+
})
|
|
637
|
+
const value = TxEnvelopeTempo.deserialize(serialized)
|
|
638
|
+
results.push({
|
|
639
|
+
account: value.signature?.account,
|
|
640
|
+
configVersion:
|
|
641
|
+
value.signature?.type === 'multisig'
|
|
642
|
+
? value.signature.config.version
|
|
643
|
+
: undefined,
|
|
644
|
+
hash: Hash.keccak256(serialized),
|
|
645
|
+
signatureCount:
|
|
646
|
+
value.signature?.type === 'multisig'
|
|
647
|
+
? value.signature.signatures.length
|
|
648
|
+
: 0,
|
|
649
|
+
type: serialized.slice(0, 4),
|
|
650
|
+
})
|
|
651
|
+
}
|
|
652
|
+
|
|
653
|
+
expect(results).toMatchInlineSnapshot(`
|
|
654
|
+
[
|
|
655
|
+
{
|
|
656
|
+
"account": "0xf81b7763d3a6876195d780865bd783dbd97dd36e",
|
|
657
|
+
"configVersion": 1n,
|
|
658
|
+
"hash": "0x8309740edaf304284186f7bcfe4527745cd4eb48e7441795ebdd970798091f8f",
|
|
659
|
+
"signatureCount": 2,
|
|
660
|
+
"type": "0x76",
|
|
661
|
+
},
|
|
662
|
+
{
|
|
663
|
+
"account": "0xf81b7763d3a6876195d780865bd783dbd97dd36e",
|
|
664
|
+
"configVersion": 0n,
|
|
665
|
+
"hash": "0x636af84d8445d87cbcd33039d5a3a2274a5911036b1b7ceb740ac41e2f638611",
|
|
666
|
+
"signatureCount": 2,
|
|
667
|
+
"type": "0x76",
|
|
668
|
+
},
|
|
669
|
+
]
|
|
670
|
+
`)
|
|
671
|
+
})
|
|
672
|
+
|
|
673
|
+
test('fee-payer transactions', async () => {
|
|
674
|
+
const envelope = TxEnvelopeTempo.from({
|
|
675
|
+
calls: [{ data: '0x1234', to: owner_1 }],
|
|
676
|
+
chainId: 4217,
|
|
677
|
+
})
|
|
678
|
+
const transactions = [
|
|
679
|
+
TxEnvelopeTempo.serialize(envelope, {
|
|
680
|
+
format: 'feePayer',
|
|
681
|
+
sender: account,
|
|
682
|
+
}),
|
|
683
|
+
TxEnvelopeTempo.serialize(
|
|
684
|
+
{
|
|
685
|
+
...envelope,
|
|
686
|
+
feePayerSignature: { r: 5n, s: 6n, yParity: 0 },
|
|
687
|
+
},
|
|
688
|
+
{ format: 'feePayer' },
|
|
689
|
+
),
|
|
690
|
+
] as const
|
|
691
|
+
const results = []
|
|
692
|
+
for (const transaction of transactions) {
|
|
693
|
+
const hash = MultisigOperation.getHash({
|
|
694
|
+
account,
|
|
695
|
+
configVersion: 1n,
|
|
696
|
+
transaction,
|
|
697
|
+
type: 'transaction',
|
|
698
|
+
})
|
|
699
|
+
const selection = await MultisigOperation.selectApprovals({
|
|
700
|
+
account,
|
|
701
|
+
approvals: [
|
|
702
|
+
signApproval(owners[0]!, hash),
|
|
703
|
+
signApproval(owners[1]!, hash),
|
|
704
|
+
],
|
|
705
|
+
config,
|
|
706
|
+
hash,
|
|
707
|
+
})
|
|
708
|
+
const serialized = MultisigOperation.serializeTransaction(
|
|
709
|
+
MultisigOperation.from({
|
|
710
|
+
account,
|
|
711
|
+
approvals: selection.approvals,
|
|
712
|
+
config,
|
|
713
|
+
configVersion: 1n,
|
|
714
|
+
createdAt: 1,
|
|
715
|
+
hash,
|
|
716
|
+
init: false,
|
|
717
|
+
signatureCount: selection.signatureCount,
|
|
718
|
+
status: 'pending',
|
|
719
|
+
threshold: selection.threshold,
|
|
720
|
+
transaction,
|
|
721
|
+
type: 'transaction',
|
|
722
|
+
updatedAt: 1,
|
|
723
|
+
weight: selection.weight,
|
|
724
|
+
}),
|
|
725
|
+
{ approvals: selection.selectedApprovals },
|
|
726
|
+
)
|
|
727
|
+
const value = TxEnvelopeTempo.deserialize(serialized)
|
|
728
|
+
results.push({
|
|
729
|
+
feePayerSignature: value.feePayerSignature,
|
|
730
|
+
from: value.from,
|
|
731
|
+
signatureCount:
|
|
732
|
+
value.signature?.type === 'multisig'
|
|
733
|
+
? value.signature.signatures.length
|
|
734
|
+
: 0,
|
|
735
|
+
type: serialized.slice(0, 4),
|
|
736
|
+
})
|
|
737
|
+
}
|
|
738
|
+
|
|
739
|
+
expect(results).toMatchInlineSnapshot(`
|
|
740
|
+
[
|
|
741
|
+
{
|
|
742
|
+
"feePayerSignature": null,
|
|
743
|
+
"from": "0xf81b7763d3a6876195d780865bd783dbd97dd36e",
|
|
744
|
+
"signatureCount": 2,
|
|
745
|
+
"type": "0x78",
|
|
746
|
+
},
|
|
747
|
+
{
|
|
748
|
+
"feePayerSignature": {
|
|
749
|
+
"r": 5n,
|
|
750
|
+
"s": 6n,
|
|
751
|
+
"yParity": 0,
|
|
752
|
+
},
|
|
753
|
+
"from": "0xf81b7763d3a6876195d780865bd783dbd97dd36e",
|
|
754
|
+
"signatureCount": 2,
|
|
755
|
+
"type": "0x78",
|
|
756
|
+
},
|
|
757
|
+
]
|
|
758
|
+
`)
|
|
759
|
+
})
|
|
760
|
+
|
|
761
|
+
test('rejects an approval not retained by the operation', async () => {
|
|
762
|
+
const hash = MultisigOperation.getHash({
|
|
763
|
+
account,
|
|
764
|
+
configVersion: 1n,
|
|
765
|
+
transaction,
|
|
766
|
+
type: 'transaction',
|
|
767
|
+
})
|
|
768
|
+
const approval_1 = signApproval(owners[0]!, hash)
|
|
769
|
+
const approval_2 = signApproval(owners[1]!, hash)
|
|
770
|
+
const selection = await MultisigOperation.selectApprovals({
|
|
771
|
+
account,
|
|
772
|
+
approvals: [approval_1],
|
|
773
|
+
config,
|
|
774
|
+
hash,
|
|
775
|
+
})
|
|
776
|
+
const operation = MultisigOperation.from({
|
|
777
|
+
account,
|
|
778
|
+
approvals: selection.approvals,
|
|
779
|
+
config,
|
|
780
|
+
configVersion: 1n,
|
|
781
|
+
createdAt: 1,
|
|
782
|
+
hash,
|
|
783
|
+
init: false,
|
|
784
|
+
signatureCount: selection.signatureCount,
|
|
785
|
+
status: 'pending',
|
|
786
|
+
threshold: selection.threshold,
|
|
787
|
+
transaction,
|
|
788
|
+
type: 'transaction',
|
|
789
|
+
updatedAt: 1,
|
|
790
|
+
weight: selection.weight,
|
|
791
|
+
})
|
|
792
|
+
|
|
793
|
+
expect(() =>
|
|
794
|
+
MultisigOperation.serializeTransaction(operation, {
|
|
795
|
+
approvals: [approval_2],
|
|
796
|
+
}),
|
|
797
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
798
|
+
`[MultisigOperation.InvalidOperationError: Invalid multisig operation: transaction signature is not a retained approval.]`,
|
|
799
|
+
)
|
|
800
|
+
})
|
|
801
|
+
})
|
|
802
|
+
|
|
104
803
|
describe('from', () => {
|
|
105
804
|
test('transaction states', () => {
|
|
106
805
|
const pending = MultisigOperation.from(transactionPending)
|
|
@@ -142,6 +841,7 @@ describe('from', () => {
|
|
|
142
841
|
],
|
|
143
842
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
144
843
|
"threshold": 2,
|
|
844
|
+
"version": 1n,
|
|
145
845
|
},
|
|
146
846
|
"configVersion": 1n,
|
|
147
847
|
"createdAt": 1,
|
|
@@ -174,6 +874,7 @@ describe('from', () => {
|
|
|
174
874
|
],
|
|
175
875
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
176
876
|
"threshold": 2,
|
|
877
|
+
"version": 1n,
|
|
177
878
|
},
|
|
178
879
|
"configVersion": 1n,
|
|
179
880
|
"createdAt": 1,
|
|
@@ -208,6 +909,7 @@ describe('from', () => {
|
|
|
208
909
|
],
|
|
209
910
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
210
911
|
"threshold": 2,
|
|
912
|
+
"version": 1n,
|
|
211
913
|
},
|
|
212
914
|
"configVersion": 1n,
|
|
213
915
|
"createdAt": 1,
|
|
@@ -238,10 +940,10 @@ describe('from', () => {
|
|
|
238
940
|
...transactionPending,
|
|
239
941
|
hash: MultisigConfig.getSignPayload({
|
|
240
942
|
account,
|
|
943
|
+
config: initializedConfig,
|
|
241
944
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
242
945
|
TxEnvelopeTempo.deserialize(transaction),
|
|
243
946
|
),
|
|
244
|
-
version: 1n,
|
|
245
947
|
}),
|
|
246
948
|
transaction,
|
|
247
949
|
})
|
|
@@ -270,6 +972,7 @@ describe('from', () => {
|
|
|
270
972
|
],
|
|
271
973
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
272
974
|
"threshold": 2,
|
|
975
|
+
"version": 1n,
|
|
273
976
|
},
|
|
274
977
|
"configVersion": 1n,
|
|
275
978
|
"createdAt": 1,
|
|
@@ -300,10 +1003,10 @@ describe('from', () => {
|
|
|
300
1003
|
...transactionPending,
|
|
301
1004
|
hash: MultisigConfig.getSignPayload({
|
|
302
1005
|
account,
|
|
1006
|
+
config: initializedConfig,
|
|
303
1007
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
304
1008
|
TxEnvelopeTempo.deserialize(transaction),
|
|
305
1009
|
),
|
|
306
|
-
version: 1n,
|
|
307
1010
|
}),
|
|
308
1011
|
transaction,
|
|
309
1012
|
})
|
|
@@ -317,10 +1020,10 @@ describe('from', () => {
|
|
|
317
1020
|
configVersion: 0n,
|
|
318
1021
|
hash: MultisigConfig.getSignPayload({
|
|
319
1022
|
account,
|
|
1023
|
+
config,
|
|
320
1024
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
321
1025
|
TxEnvelopeTempo.deserialize(transaction),
|
|
322
1026
|
),
|
|
323
|
-
version: 0n,
|
|
324
1027
|
}),
|
|
325
1028
|
init: true,
|
|
326
1029
|
})
|
|
@@ -349,6 +1052,7 @@ describe('from', () => {
|
|
|
349
1052
|
],
|
|
350
1053
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
351
1054
|
"threshold": 2,
|
|
1055
|
+
"version": 0n,
|
|
352
1056
|
},
|
|
353
1057
|
"configVersion": 0n,
|
|
354
1058
|
"createdAt": 1,
|
|
@@ -372,10 +1076,10 @@ describe('from', () => {
|
|
|
372
1076
|
configVersion: 0n,
|
|
373
1077
|
hash: MultisigConfig.getSignPayload({
|
|
374
1078
|
account,
|
|
1079
|
+
config,
|
|
375
1080
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
376
1081
|
TxEnvelopeTempo.deserialize(transaction),
|
|
377
1082
|
),
|
|
378
|
-
version: 0n,
|
|
379
1083
|
}),
|
|
380
1084
|
})
|
|
381
1085
|
|
|
@@ -400,6 +1104,7 @@ describe('from', () => {
|
|
|
400
1104
|
KeyAuthorization.from(authorization, {
|
|
401
1105
|
signature: {
|
|
402
1106
|
account,
|
|
1107
|
+
config: initializedConfig,
|
|
403
1108
|
signatures: [
|
|
404
1109
|
SignatureEnvelope.deserialize(approval_1),
|
|
405
1110
|
SignatureEnvelope.deserialize(approval_2),
|
|
@@ -433,6 +1138,7 @@ describe('from', () => {
|
|
|
433
1138
|
],
|
|
434
1139
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
435
1140
|
"threshold": 2,
|
|
1141
|
+
"version": 1n,
|
|
436
1142
|
},
|
|
437
1143
|
"configVersion": 1n,
|
|
438
1144
|
"createdAt": 1,
|
|
@@ -465,12 +1171,13 @@ describe('from', () => {
|
|
|
465
1171
|
],
|
|
466
1172
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
467
1173
|
"threshold": 2,
|
|
1174
|
+
"version": 1n,
|
|
468
1175
|
},
|
|
469
1176
|
"configVersion": 1n,
|
|
470
1177
|
"createdAt": 1,
|
|
471
1178
|
"hash": "0xf6995eb69c12c03d3d13b357abf7cac22b4effe52fba8ff02e5aef26161f77a0",
|
|
472
1179
|
"init": false,
|
|
473
|
-
"keyAuthorization": "
|
|
1180
|
+
"keyAuthorization": "0xf901b3f782107980943333333333333333333333333333333333333333846b49d2008080808094f81b7763d3a6876195d780865bd783dbd97dd36eb9017805f9017494f81b7763d3a6876195d780865bd783dbd97dd36ef852a000000000000000000000000000000000000000000000000000000000000000000102eed69407e1ed8ea0e9601e5546b0a03aed683df360140701d694288f0cd85005f34168f731a468aef268c2f9456f01f90108b88201000000000000000000000000000000000000000000000000000000000000000500000000000000000000000000000000000000000000000000000000000000065ecbe4d1a6330a44c8f7ef951d4bf165e6c6b721efada985fb41661bc6e7fd6c8734640c4998ff7e374b06ce1a64a2ecd82ab036384fb83d9a79b127a27d503200b88201000000000000000000000000000000000000000000000000000000000000000300000000000000000000000000000000000000000000000000000000000000047cf27b188d034f7e8a52380304b51ac3c08969e277f21b35a60b48fc4766997807775510db8ed040293d9ac69f7430dbba7dade63ce982299e04b79d227873d100",
|
|
474
1181
|
"signatureCount": 2,
|
|
475
1182
|
"status": "success",
|
|
476
1183
|
"threshold": 2,
|
|
@@ -488,7 +1195,7 @@ describe('from', () => {
|
|
|
488
1195
|
KeyAuthorization.from(authorization, {
|
|
489
1196
|
signature: {
|
|
490
1197
|
account,
|
|
491
|
-
|
|
1198
|
+
config,
|
|
492
1199
|
signatures: [
|
|
493
1200
|
SignatureEnvelope.deserialize(approval_1),
|
|
494
1201
|
SignatureEnvelope.deserialize(approval_2),
|
|
@@ -503,8 +1210,8 @@ describe('from', () => {
|
|
|
503
1210
|
configVersion: 0n,
|
|
504
1211
|
hash: MultisigConfig.getSignPayload({
|
|
505
1212
|
account,
|
|
1213
|
+
config,
|
|
506
1214
|
payload: KeyAuthorization.getSignPayload(authorization),
|
|
507
|
-
version: 0n,
|
|
508
1215
|
}),
|
|
509
1216
|
init: true,
|
|
510
1217
|
keyAuthorization: keyAuthorization_,
|
|
@@ -538,6 +1245,7 @@ describe('from', () => {
|
|
|
538
1245
|
],
|
|
539
1246
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
540
1247
|
"threshold": 2,
|
|
1248
|
+
"version": 0n,
|
|
541
1249
|
},
|
|
542
1250
|
"configVersion": 0n,
|
|
543
1251
|
"createdAt": 1,
|
|
@@ -561,6 +1269,9 @@ describe('RPC conversion', () => {
|
|
|
561
1269
|
const transactionRpc = MultisigOperation.toRpc(transactionPending)
|
|
562
1270
|
const keyAuthorizationRpc = MultisigOperation.toRpc(keyAuthorizationPending)
|
|
563
1271
|
|
|
1272
|
+
expect(() =>
|
|
1273
|
+
JSON.stringify({ keyAuthorizationRpc, transactionRpc }),
|
|
1274
|
+
).not.toThrow()
|
|
564
1275
|
expect({ keyAuthorizationRpc, transactionRpc }).toMatchInlineSnapshot(`
|
|
565
1276
|
{
|
|
566
1277
|
"keyAuthorizationRpc": {
|
|
@@ -581,6 +1292,7 @@ describe('RPC conversion', () => {
|
|
|
581
1292
|
],
|
|
582
1293
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
583
1294
|
"threshold": 2,
|
|
1295
|
+
"version": "0x1",
|
|
584
1296
|
},
|
|
585
1297
|
"configVersion": "0x1",
|
|
586
1298
|
"createdAt": 1,
|
|
@@ -612,6 +1324,7 @@ describe('RPC conversion', () => {
|
|
|
612
1324
|
],
|
|
613
1325
|
"salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
|
|
614
1326
|
"threshold": 2,
|
|
1327
|
+
"version": "0x1",
|
|
615
1328
|
},
|
|
616
1329
|
"configVersion": "0x1",
|
|
617
1330
|
"createdAt": 1,
|
|
@@ -749,10 +1462,10 @@ describe('validation', () => {
|
|
|
749
1462
|
account: '0x0000000000000000000000000000000000000000',
|
|
750
1463
|
hash: MultisigConfig.getSignPayload({
|
|
751
1464
|
account: '0x0000000000000000000000000000000000000000',
|
|
1465
|
+
config: initializedConfig,
|
|
752
1466
|
payload: TxEnvelopeTempo.getSignPayload(
|
|
753
1467
|
TxEnvelopeTempo.deserialize(transaction),
|
|
754
1468
|
),
|
|
755
|
-
version: 1n,
|
|
756
1469
|
}),
|
|
757
1470
|
},
|
|
758
1471
|
},
|
|
@@ -822,7 +1535,7 @@ describe('validation', () => {
|
|
|
822
1535
|
approvals: [
|
|
823
1536
|
SignatureEnvelope.serialize({
|
|
824
1537
|
account,
|
|
825
|
-
|
|
1538
|
+
config,
|
|
826
1539
|
signatures: [ownerSignature_1],
|
|
827
1540
|
type: 'multisig',
|
|
828
1541
|
}),
|
|
@@ -859,6 +1572,7 @@ describe('validation', () => {
|
|
|
859
1572
|
KeyAuthorization.from(authorization, {
|
|
860
1573
|
signature: {
|
|
861
1574
|
account,
|
|
1575
|
+
config: initializedConfig,
|
|
862
1576
|
signatures: [
|
|
863
1577
|
SignatureEnvelope.deserialize(approval_1),
|
|
864
1578
|
SignatureEnvelope.deserialize(approval_3),
|
|
@@ -882,12 +1596,12 @@ describe('validation', () => {
|
|
|
882
1596
|
const signatures = [
|
|
883
1597
|
...SignatureEnvelope.sortMultisigApprovals({
|
|
884
1598
|
account,
|
|
1599
|
+
config: initializedConfig,
|
|
885
1600
|
payload: KeyAuthorization.getSignPayload(authorization),
|
|
886
1601
|
signatures: [
|
|
887
1602
|
SignatureEnvelope.deserialize(approval_1),
|
|
888
1603
|
SignatureEnvelope.deserialize(approval_2),
|
|
889
1604
|
],
|
|
890
|
-
version: 1n,
|
|
891
1605
|
}),
|
|
892
1606
|
].reverse()
|
|
893
1607
|
const operation = {
|
|
@@ -895,7 +1609,12 @@ describe('validation', () => {
|
|
|
895
1609
|
approvals: [approval_1, approval_2],
|
|
896
1610
|
keyAuthorization: KeyAuthorization.serialize(
|
|
897
1611
|
KeyAuthorization.from(authorization, {
|
|
898
|
-
signature: {
|
|
1612
|
+
signature: {
|
|
1613
|
+
account,
|
|
1614
|
+
config: initializedConfig,
|
|
1615
|
+
signatures,
|
|
1616
|
+
type: 'multisig',
|
|
1617
|
+
},
|
|
899
1618
|
}),
|
|
900
1619
|
),
|
|
901
1620
|
signatureCount: 2,
|
|
@@ -917,6 +1636,7 @@ describe('validation', () => {
|
|
|
917
1636
|
KeyAuthorization.from(authorization, {
|
|
918
1637
|
signature: {
|
|
919
1638
|
account,
|
|
1639
|
+
config: initializedConfig,
|
|
920
1640
|
signatures: [
|
|
921
1641
|
SignatureEnvelope.deserialize(approval_1),
|
|
922
1642
|
SignatureEnvelope.deserialize(approval_1),
|
|
@@ -937,37 +1657,49 @@ describe('validation', () => {
|
|
|
937
1657
|
|
|
938
1658
|
test('rejects reordered nested key authorization approvals', () => {
|
|
939
1659
|
const authorization = KeyAuthorization.deserialize(keyAuthorization)
|
|
1660
|
+
const nestedConfig = MultisigConfig.from({
|
|
1661
|
+
owners: [{ owner: owner_2, weight: 1 }],
|
|
1662
|
+
threshold: 1,
|
|
1663
|
+
version: 1n,
|
|
1664
|
+
})
|
|
940
1665
|
const childSignatures = SignatureEnvelope.sortMultisigApprovals({
|
|
941
1666
|
account: owner_1,
|
|
1667
|
+
config: nestedConfig,
|
|
942
1668
|
payload: keyAuthorizationHash,
|
|
943
1669
|
signatures: [
|
|
944
1670
|
SignatureEnvelope.deserialize(approval_1),
|
|
945
1671
|
SignatureEnvelope.deserialize(approval_2),
|
|
946
1672
|
],
|
|
947
|
-
version: 1n,
|
|
948
1673
|
})
|
|
949
1674
|
const retainedNested = SignatureEnvelope.from({
|
|
950
1675
|
account: owner_1,
|
|
1676
|
+
config: nestedConfig,
|
|
951
1677
|
signatures: childSignatures,
|
|
952
1678
|
type: 'multisig',
|
|
953
1679
|
})
|
|
954
1680
|
const selectedNested = SignatureEnvelope.from({
|
|
955
1681
|
account: owner_1,
|
|
1682
|
+
config: nestedConfig,
|
|
956
1683
|
signatures: [...childSignatures].reverse(),
|
|
957
1684
|
type: 'multisig',
|
|
958
1685
|
})
|
|
959
1686
|
const selected = SignatureEnvelope.sortMultisigApprovals({
|
|
960
1687
|
account,
|
|
1688
|
+
config: initializedConfig,
|
|
961
1689
|
payload: KeyAuthorization.getSignPayload(authorization),
|
|
962
1690
|
signatures: [selectedNested, SignatureEnvelope.deserialize(approval_2)],
|
|
963
|
-
version: 1n,
|
|
964
1691
|
})
|
|
965
1692
|
const operation = {
|
|
966
1693
|
...keyAuthorizationPending,
|
|
967
1694
|
approvals: [SignatureEnvelope.serialize(retainedNested), approval_2],
|
|
968
1695
|
keyAuthorization: KeyAuthorization.serialize(
|
|
969
1696
|
KeyAuthorization.from(authorization, {
|
|
970
|
-
signature: {
|
|
1697
|
+
signature: {
|
|
1698
|
+
account,
|
|
1699
|
+
config: initializedConfig,
|
|
1700
|
+
signatures: selected,
|
|
1701
|
+
type: 'multisig',
|
|
1702
|
+
},
|
|
971
1703
|
}),
|
|
972
1704
|
),
|
|
973
1705
|
signatureCount: 2,
|
|
@@ -1006,12 +1738,12 @@ describe('validation', () => {
|
|
|
1006
1738
|
})
|
|
1007
1739
|
const signatures = SignatureEnvelope.sortMultisigApprovals({
|
|
1008
1740
|
account,
|
|
1741
|
+
config,
|
|
1009
1742
|
payload: KeyAuthorization.getSignPayload(authorization),
|
|
1010
1743
|
signatures: [
|
|
1011
1744
|
SignatureEnvelope.deserialize(approval_1),
|
|
1012
1745
|
SignatureEnvelope.deserialize(approval_2),
|
|
1013
1746
|
],
|
|
1014
|
-
version: 0n,
|
|
1015
1747
|
})
|
|
1016
1748
|
const approvals = signatures.map((signature) =>
|
|
1017
1749
|
SignatureEnvelope.serialize(signature),
|
|
@@ -1025,15 +1757,15 @@ describe('validation', () => {
|
|
|
1025
1757
|
createdAt: 1,
|
|
1026
1758
|
hash: MultisigConfig.getSignPayload({
|
|
1027
1759
|
account,
|
|
1760
|
+
config,
|
|
1028
1761
|
payload: KeyAuthorization.getSignPayload(authorization),
|
|
1029
|
-
version: 0n,
|
|
1030
1762
|
}),
|
|
1031
1763
|
init: true,
|
|
1032
1764
|
keyAuthorization: KeyAuthorization.serialize(
|
|
1033
1765
|
KeyAuthorization.from(authorization, {
|
|
1034
1766
|
signature: {
|
|
1035
1767
|
account,
|
|
1036
|
-
|
|
1768
|
+
config,
|
|
1037
1769
|
signatures,
|
|
1038
1770
|
type: 'multisig',
|
|
1039
1771
|
},
|