ox 0.14.39 → 0.14.41
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 +12 -0
- package/_cjs/tempo/MultisigOperation.js +30 -45
- package/_cjs/tempo/MultisigOperation.js.map +1 -1
- package/_cjs/tempo/MultisigWitness.js +92 -0
- package/_cjs/tempo/MultisigWitness.js.map +1 -0
- package/_cjs/tempo/SignatureEnvelope.js +5 -2
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/tempo/TransactionRequest.js +7 -1
- package/_cjs/tempo/TransactionRequest.js.map +1 -1
- package/_cjs/tempo/index.js +2 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/tempo/MultisigOperation.js +31 -48
- package/_esm/tempo/MultisigOperation.js.map +1 -1
- package/_esm/tempo/MultisigWitness.js +123 -0
- package/_esm/tempo/MultisigWitness.js.map +1 -0
- package/_esm/tempo/SignatureEnvelope.js +5 -2
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/TransactionRequest.js +7 -1
- package/_esm/tempo/TransactionRequest.js.map +1 -1
- package/_esm/tempo/index.js +9 -0
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/version.js +1 -1
- package/_types/tempo/MultisigOperation.d.ts +4 -25
- package/_types/tempo/MultisigOperation.d.ts.map +1 -1
- package/_types/tempo/MultisigWitness.d.ts +106 -0
- package/_types/tempo/MultisigWitness.d.ts.map +1 -0
- package/_types/tempo/SignatureEnvelope.d.ts +3 -3
- package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
- package/_types/tempo/TransactionRequest.d.ts +6 -3
- package/_types/tempo/TransactionRequest.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +9 -0
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/version.d.ts +1 -1
- package/package.json +16 -1
- package/tempo/KeyAuthorization.test-d.ts +1 -1
- package/tempo/MultisigOperation.test-d.ts +0 -6
- package/tempo/MultisigOperation.test.ts +196 -145
- package/tempo/MultisigOperation.ts +40 -83
- package/tempo/MultisigWitness/package.json +6 -0
- package/tempo/MultisigWitness.test-d/package.json +6 -0
- package/tempo/MultisigWitness.test-d.ts +42 -0
- package/tempo/MultisigWitness.test.ts +289 -0
- package/tempo/MultisigWitness.ts +210 -0
- package/tempo/RpcSchemaTempo.test-d.ts +25 -0
- package/tempo/SignatureEnvelope.test-d.ts +4 -4
- package/tempo/SignatureEnvelope.test.ts +2 -2
- package/tempo/SignatureEnvelope.ts +9 -5
- package/tempo/TransactionRequest.test-d/package.json +6 -0
- package/tempo/TransactionRequest.test-d.ts +24 -0
- package/tempo/TransactionRequest.test.ts +102 -1
- package/tempo/TransactionRequest.ts +12 -2
- package/tempo/index.ts +9 -0
- package/version.ts +1 -1
|
@@ -15,14 +15,10 @@ export type Base<quantity = bigint> = {
|
|
|
15
15
|
approvals: readonly Hex.Hex[]
|
|
16
16
|
/** Root configuration used to verify approvals. */
|
|
17
17
|
config: MultisigConfig.Config<quantity>
|
|
18
|
-
/** Root configuration version. */
|
|
19
|
-
configVersion: quantity
|
|
20
18
|
/** Unix creation time in milliseconds. */
|
|
21
19
|
createdAt: number
|
|
22
20
|
/** Deterministic multisig operation hash. */
|
|
23
21
|
hash: Hex.Hex
|
|
24
|
-
/** Whether the operation initializes the root multisig account. */
|
|
25
|
-
init: boolean
|
|
26
22
|
/** Number of approvals selected for quorum evaluation. */
|
|
27
23
|
signatureCount: number
|
|
28
24
|
/** Required root owner weight. */
|
|
@@ -73,9 +69,6 @@ export type KeyAuthorizationRpc = KeyAuthorizationOperation<Hex.Hex>
|
|
|
73
69
|
/** JSON-RPC multisig operation. */
|
|
74
70
|
export type Rpc = Operation<Hex.Hex>
|
|
75
71
|
|
|
76
|
-
/** Maximum supported multisig configuration version. */
|
|
77
|
-
const maxConfigVersion = 2n ** 64n - 1n
|
|
78
|
-
|
|
79
72
|
/**
|
|
80
73
|
* Derives the deterministic hash for a multisig operation.
|
|
81
74
|
*
|
|
@@ -86,7 +79,7 @@ const maxConfigVersion = 2n ** 64n - 1n
|
|
|
86
79
|
*
|
|
87
80
|
* const hash = MultisigOperation.getHash({
|
|
88
81
|
* account,
|
|
89
|
-
*
|
|
82
|
+
* config,
|
|
90
83
|
* transaction,
|
|
91
84
|
* type: 'transaction',
|
|
92
85
|
* })
|
|
@@ -96,7 +89,7 @@ const maxConfigVersion = 2n ** 64n - 1n
|
|
|
96
89
|
* @returns The operation hash signed by each owner.
|
|
97
90
|
*/
|
|
98
91
|
export function getHash(options: getHash.Options): Hex.Hex {
|
|
99
|
-
const { account,
|
|
92
|
+
const { account, config } = options
|
|
100
93
|
const payload =
|
|
101
94
|
options.type === 'transaction'
|
|
102
95
|
? TxEnvelopeTempo.getSignPayload(
|
|
@@ -109,7 +102,7 @@ export function getHash(options: getHash.Options): Hex.Hex {
|
|
|
109
102
|
)
|
|
110
103
|
return MultisigConfig.getSignPayload({
|
|
111
104
|
account,
|
|
112
|
-
config
|
|
105
|
+
config,
|
|
113
106
|
payload,
|
|
114
107
|
})
|
|
115
108
|
}
|
|
@@ -119,8 +112,8 @@ export declare namespace getHash {
|
|
|
119
112
|
export type Options = {
|
|
120
113
|
/** Root multisig account. */
|
|
121
114
|
account: Address.Address
|
|
122
|
-
/**
|
|
123
|
-
|
|
115
|
+
/** Complete root multisig configuration witness. */
|
|
116
|
+
config: MultisigConfig.Config
|
|
124
117
|
} & (
|
|
125
118
|
| {
|
|
126
119
|
/** Canonical serialized key authorization. */
|
|
@@ -163,7 +156,6 @@ export declare namespace getHash {
|
|
|
163
156
|
* approvals,
|
|
164
157
|
* config,
|
|
165
158
|
* hash,
|
|
166
|
-
* resolveConfig,
|
|
167
159
|
* })
|
|
168
160
|
* ```
|
|
169
161
|
*
|
|
@@ -173,18 +165,25 @@ export declare namespace getHash {
|
|
|
173
165
|
export async function selectApprovals(
|
|
174
166
|
options: selectApprovals.Options,
|
|
175
167
|
): Promise<selectApprovals.ReturnValue> {
|
|
176
|
-
const { account, approvals, hash
|
|
168
|
+
const { account, approvals, hash } = options
|
|
177
169
|
if (!Address.validate(account) || Hex.toBigInt(account) === 0n)
|
|
178
170
|
throw new InvalidApprovalError({ reason: 'account is invalid' })
|
|
179
171
|
if (!Hash.validate(hash))
|
|
180
172
|
throw new InvalidApprovalError({ reason: 'hash is invalid' })
|
|
173
|
+
const config = MultisigConfig.from(options.config)
|
|
174
|
+
if (
|
|
175
|
+
config.version === 0n &&
|
|
176
|
+
!Address.isEqual(MultisigConfig.getAddress(config), account)
|
|
177
|
+
)
|
|
178
|
+
throw new InvalidApprovalError({
|
|
179
|
+
reason: 'initial config does not derive the root multisig account',
|
|
180
|
+
})
|
|
181
181
|
return selectApprovals_internal(
|
|
182
182
|
{
|
|
183
183
|
account,
|
|
184
184
|
approvals,
|
|
185
|
-
config
|
|
185
|
+
config,
|
|
186
186
|
hash,
|
|
187
|
-
resolveConfig,
|
|
188
187
|
},
|
|
189
188
|
[account.toLowerCase()],
|
|
190
189
|
)
|
|
@@ -201,27 +200,6 @@ export declare namespace selectApprovals {
|
|
|
201
200
|
config: MultisigConfig.Config
|
|
202
201
|
/** Deterministic operation hash approved by root owners. */
|
|
203
202
|
hash: Hex.Hex
|
|
204
|
-
/** Resolves the current configuration of a nested multisig owner. */
|
|
205
|
-
resolveConfig?: ResolveConfig | undefined
|
|
206
|
-
}
|
|
207
|
-
|
|
208
|
-
/** Resolves an initialized nested multisig configuration. */
|
|
209
|
-
export type ResolveConfig = (
|
|
210
|
-
options: ResolveConfigOptions,
|
|
211
|
-
) => ResolvedConfig | Promise<ResolvedConfig>
|
|
212
|
-
|
|
213
|
-
/** Nested multisig configuration lookup parameters. */
|
|
214
|
-
export type ResolveConfigOptions = {
|
|
215
|
-
/** Nested multisig account. */
|
|
216
|
-
account: Address.Address
|
|
217
|
-
}
|
|
218
|
-
|
|
219
|
-
/** Resolved nested multisig configuration. */
|
|
220
|
-
export type ResolvedConfig = {
|
|
221
|
-
/** Current nested multisig configuration. */
|
|
222
|
-
config: MultisigConfig.Config
|
|
223
|
-
/** Current nested multisig configuration version. */
|
|
224
|
-
version: bigint
|
|
225
203
|
}
|
|
226
204
|
|
|
227
205
|
/** Result of validating and selecting approvals. */
|
|
@@ -338,10 +316,7 @@ export function from<const operation extends Operation>(
|
|
|
338
316
|
operation: operation,
|
|
339
317
|
): from.ReturnValue<operation> {
|
|
340
318
|
try {
|
|
341
|
-
const config = MultisigConfig.from(
|
|
342
|
-
...operation.config,
|
|
343
|
-
version: operation.configVersion,
|
|
344
|
-
})
|
|
319
|
+
const config = MultisigConfig.from(operation.config)
|
|
345
320
|
if (
|
|
346
321
|
typeof config.threshold !== 'number' ||
|
|
347
322
|
config.owners.some((owner) => typeof owner.weight !== 'number')
|
|
@@ -391,22 +366,19 @@ export function fromRpc<const operation extends Rpc>(
|
|
|
391
366
|
operation: operation,
|
|
392
367
|
): fromRpc.ReturnValue<operation> {
|
|
393
368
|
try {
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
!Hex.validate(operation.configVersion)
|
|
397
|
-
)
|
|
369
|
+
const version = operation.config?.version
|
|
370
|
+
if (typeof version !== 'string' || !Hex.validate(version))
|
|
398
371
|
throw new InvalidOperationError({
|
|
399
|
-
reason: '
|
|
372
|
+
reason: 'config.version must be a hexadecimal quantity',
|
|
400
373
|
})
|
|
401
|
-
const
|
|
402
|
-
if (Hex.fromNumber(
|
|
374
|
+
const version_ = Hex.toBigInt(version)
|
|
375
|
+
if (Hex.fromNumber(version_) !== version)
|
|
403
376
|
throw new InvalidOperationError({
|
|
404
|
-
reason: '
|
|
377
|
+
reason: 'config.version must use canonical quantity encoding',
|
|
405
378
|
})
|
|
406
379
|
return from({
|
|
407
380
|
...operation,
|
|
408
381
|
config: MultisigConfig.fromRpc(operation.config),
|
|
409
|
-
configVersion,
|
|
410
382
|
} as Operation) as never
|
|
411
383
|
} catch (cause) {
|
|
412
384
|
if (cause instanceof InvalidOperationError) throw cause
|
|
@@ -446,7 +418,6 @@ export function toRpc<const operation extends Operation>(
|
|
|
446
418
|
return {
|
|
447
419
|
...value,
|
|
448
420
|
config: MultisigConfig.toRpc(value.config),
|
|
449
|
-
configVersion: Hex.fromNumber(value.configVersion),
|
|
450
421
|
} as never
|
|
451
422
|
}
|
|
452
423
|
|
|
@@ -458,7 +429,7 @@ export declare namespace toRpc {
|
|
|
458
429
|
: KeyAuthorizationRpc
|
|
459
430
|
|
|
460
431
|
/** Error type for `toRpc`. */
|
|
461
|
-
export type ErrorType = from.ErrorType |
|
|
432
|
+
export type ErrorType = from.ErrorType | MultisigConfig.toRpc.ErrorType
|
|
462
433
|
}
|
|
463
434
|
|
|
464
435
|
/**
|
|
@@ -524,15 +495,18 @@ async function selectApprovals_internal(
|
|
|
524
495
|
throw new InvalidApprovalError({
|
|
525
496
|
reason: `nested multisig owner ${group.address} is invalid`,
|
|
526
497
|
})
|
|
527
|
-
|
|
498
|
+
const config = MultisigConfig.from(nested[0]!.config)
|
|
499
|
+
if (nested.some((signature) => !sameConfig(signature.config, config)))
|
|
528
500
|
throw new InvalidApprovalError({
|
|
529
|
-
reason: `nested multisig owner ${group.address}
|
|
501
|
+
reason: `nested multisig owner ${group.address} has conflicting config witnesses`,
|
|
502
|
+
})
|
|
503
|
+
if (
|
|
504
|
+
config.version === 0n &&
|
|
505
|
+
!Address.isEqual(MultisigConfig.getAddress(config), group.address)
|
|
506
|
+
)
|
|
507
|
+
throw new InvalidApprovalError({
|
|
508
|
+
reason: `initial config does not derive nested multisig owner ${group.address}`,
|
|
530
509
|
})
|
|
531
|
-
const resolved = await options.resolveConfig({ account: group.address })
|
|
532
|
-
const config = MultisigConfig.from({
|
|
533
|
-
...resolved.config,
|
|
534
|
-
version: resolved.version,
|
|
535
|
-
})
|
|
536
510
|
const selected = await selectApprovals_internal(
|
|
537
511
|
{
|
|
538
512
|
account: group.address,
|
|
@@ -547,7 +521,6 @@ async function selectApprovals_internal(
|
|
|
547
521
|
config,
|
|
548
522
|
payload: options.hash,
|
|
549
523
|
}),
|
|
550
|
-
resolveConfig: options.resolveConfig,
|
|
551
524
|
},
|
|
552
525
|
[...path, group.address.toLowerCase()],
|
|
553
526
|
)
|
|
@@ -689,16 +662,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
689
662
|
throw new InvalidOperationError({ reason: 'account cannot be zero' })
|
|
690
663
|
if (!Hash.validate(operation.hash))
|
|
691
664
|
throw new InvalidOperationError({ reason: 'hash is invalid' })
|
|
692
|
-
if (typeof operation.init !== 'boolean')
|
|
693
|
-
throw new InvalidOperationError({ reason: 'init must be a boolean' })
|
|
694
|
-
if (
|
|
695
|
-
typeof operation.configVersion !== 'bigint' ||
|
|
696
|
-
operation.configVersion < 0n ||
|
|
697
|
-
operation.configVersion > maxConfigVersion
|
|
698
|
-
)
|
|
699
|
-
throw new InvalidOperationError({
|
|
700
|
-
reason: 'configVersion must be an unsigned 64-bit integer',
|
|
701
|
-
})
|
|
702
665
|
assertInteger(operation.createdAt, 'createdAt')
|
|
703
666
|
assertInteger(operation.updatedAt, 'updatedAt')
|
|
704
667
|
if (operation.updatedAt < operation.createdAt)
|
|
@@ -778,19 +741,13 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
|
|
|
778
741
|
reason:
|
|
779
742
|
'weight is not reachable by signatureCount retained owner approvals',
|
|
780
743
|
})
|
|
781
|
-
if (
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
operation.account.toLowerCase()
|
|
789
|
-
)
|
|
790
|
-
throw new InvalidOperationError({
|
|
791
|
-
reason: 'bootstrap config does not derive the operation account',
|
|
792
|
-
})
|
|
793
|
-
}
|
|
744
|
+
if (
|
|
745
|
+
config.version === 0n &&
|
|
746
|
+
!Address.isEqual(MultisigConfig.getAddress(config), operation.account)
|
|
747
|
+
)
|
|
748
|
+
throw new InvalidOperationError({
|
|
749
|
+
reason: 'initial config does not derive the operation account',
|
|
750
|
+
})
|
|
794
751
|
}
|
|
795
752
|
|
|
796
753
|
/**
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { expectTypeOf, test } from 'vitest'
|
|
2
|
+
import * as MultisigWitness from './MultisigWitness.js'
|
|
3
|
+
|
|
4
|
+
const rpc = {
|
|
5
|
+
account: '0x2222222222222222222222222222222222222222',
|
|
6
|
+
approvals: [
|
|
7
|
+
{
|
|
8
|
+
owner: '0x1111111111111111111111111111111111111111',
|
|
9
|
+
type: 'primitive',
|
|
10
|
+
},
|
|
11
|
+
],
|
|
12
|
+
config: {
|
|
13
|
+
owners: [
|
|
14
|
+
{
|
|
15
|
+
owner: '0x1111111111111111111111111111111111111111',
|
|
16
|
+
weight: 1,
|
|
17
|
+
},
|
|
18
|
+
],
|
|
19
|
+
salt: '0x0000000000000000000000000000000000000000000000000000000000000000',
|
|
20
|
+
threshold: 1,
|
|
21
|
+
version: 0,
|
|
22
|
+
},
|
|
23
|
+
} as const satisfies MultisigWitness.Rpc
|
|
24
|
+
|
|
25
|
+
test('fromRpc returns a domain witness', () => {
|
|
26
|
+
const witness = MultisigWitness.fromRpc(rpc)
|
|
27
|
+
|
|
28
|
+
expectTypeOf(witness).toEqualTypeOf<MultisigWitness.MultisigWitness>()
|
|
29
|
+
expectTypeOf(witness.config.version).toEqualTypeOf<bigint>()
|
|
30
|
+
})
|
|
31
|
+
|
|
32
|
+
test('RPC witnesses use numeric configuration versions', () => {
|
|
33
|
+
expectTypeOf<
|
|
34
|
+
MultisigWitness.Rpc['config']['version']
|
|
35
|
+
>().toEqualTypeOf<number>()
|
|
36
|
+
})
|
|
37
|
+
|
|
38
|
+
test('toRpc returns an RPC witness', () => {
|
|
39
|
+
expectTypeOf(
|
|
40
|
+
MultisigWitness.toRpc(MultisigWitness.fromRpc(rpc)),
|
|
41
|
+
).toEqualTypeOf<MultisigWitness.Rpc>()
|
|
42
|
+
})
|
|
@@ -0,0 +1,289 @@
|
|
|
1
|
+
import { MultisigWitness } from 'ox/tempo'
|
|
2
|
+
import { describe, expect, test } from 'vitest'
|
|
3
|
+
|
|
4
|
+
const rpc = {
|
|
5
|
+
account: '0xcccccccccccccccccccccccccccccccccccccccc',
|
|
6
|
+
approvals: [
|
|
7
|
+
{
|
|
8
|
+
keyData: '0x0102030405',
|
|
9
|
+
keyType: 'webAuthn',
|
|
10
|
+
owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
11
|
+
type: 'primitive',
|
|
12
|
+
},
|
|
13
|
+
{
|
|
14
|
+
type: 'multisig',
|
|
15
|
+
witness: {
|
|
16
|
+
account: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
17
|
+
approvals: [
|
|
18
|
+
{
|
|
19
|
+
keyData: '0x010203040506',
|
|
20
|
+
keyType: 'secp256k1',
|
|
21
|
+
owner: '0xdddddddddddddddddddddddddddddddddddddddd',
|
|
22
|
+
},
|
|
23
|
+
{
|
|
24
|
+
owner: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
|
25
|
+
},
|
|
26
|
+
],
|
|
27
|
+
config: {
|
|
28
|
+
owners: [
|
|
29
|
+
{
|
|
30
|
+
owner: '0xdddddddddddddddddddddddddddddddddddddddd',
|
|
31
|
+
weight: 1,
|
|
32
|
+
},
|
|
33
|
+
{
|
|
34
|
+
owner: '0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee',
|
|
35
|
+
weight: 1,
|
|
36
|
+
},
|
|
37
|
+
],
|
|
38
|
+
salt: '0x2222222222222222222222222222222222222222222222222222222222222222',
|
|
39
|
+
threshold: 2,
|
|
40
|
+
version: 2,
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
},
|
|
44
|
+
],
|
|
45
|
+
config: {
|
|
46
|
+
owners: [
|
|
47
|
+
{
|
|
48
|
+
owner: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
|
|
49
|
+
weight: 1,
|
|
50
|
+
},
|
|
51
|
+
{
|
|
52
|
+
owner: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
53
|
+
weight: 1,
|
|
54
|
+
},
|
|
55
|
+
],
|
|
56
|
+
salt: '0x1111111111111111111111111111111111111111111111111111111111111111',
|
|
57
|
+
threshold: 2,
|
|
58
|
+
version: 1,
|
|
59
|
+
},
|
|
60
|
+
} as const satisfies MultisigWitness.Rpc
|
|
61
|
+
|
|
62
|
+
describe('fromRpc', () => {
|
|
63
|
+
test('behavior: converts numeric configuration versions to bigint', () => {
|
|
64
|
+
expect(MultisigWitness.fromRpc(rpc)).toMatchInlineSnapshot(`
|
|
65
|
+
{
|
|
66
|
+
"account": "0xcccccccccccccccccccccccccccccccccccccccc",
|
|
67
|
+
"approvals": [
|
|
68
|
+
{
|
|
69
|
+
"keyData": "0x0102030405",
|
|
70
|
+
"keyType": "webAuthn",
|
|
71
|
+
"owner": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
72
|
+
"type": "primitive",
|
|
73
|
+
},
|
|
74
|
+
{
|
|
75
|
+
"type": "multisig",
|
|
76
|
+
"witness": {
|
|
77
|
+
"account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
78
|
+
"approvals": [
|
|
79
|
+
{
|
|
80
|
+
"keyData": "0x010203040506",
|
|
81
|
+
"keyType": "secp256k1",
|
|
82
|
+
"owner": "0xdddddddddddddddddddddddddddddddddddddddd",
|
|
83
|
+
},
|
|
84
|
+
{
|
|
85
|
+
"owner": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
86
|
+
},
|
|
87
|
+
],
|
|
88
|
+
"config": {
|
|
89
|
+
"owners": [
|
|
90
|
+
{
|
|
91
|
+
"owner": "0xdddddddddddddddddddddddddddddddddddddddd",
|
|
92
|
+
"weight": 1,
|
|
93
|
+
},
|
|
94
|
+
{
|
|
95
|
+
"owner": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
96
|
+
"weight": 1,
|
|
97
|
+
},
|
|
98
|
+
],
|
|
99
|
+
"salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
|
|
100
|
+
"threshold": 2,
|
|
101
|
+
"version": 2n,
|
|
102
|
+
},
|
|
103
|
+
},
|
|
104
|
+
},
|
|
105
|
+
],
|
|
106
|
+
"config": {
|
|
107
|
+
"owners": [
|
|
108
|
+
{
|
|
109
|
+
"owner": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
110
|
+
"weight": 1,
|
|
111
|
+
},
|
|
112
|
+
{
|
|
113
|
+
"owner": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
114
|
+
"weight": 1,
|
|
115
|
+
},
|
|
116
|
+
],
|
|
117
|
+
"salt": "0x1111111111111111111111111111111111111111111111111111111111111111",
|
|
118
|
+
"threshold": 2,
|
|
119
|
+
"version": 1n,
|
|
120
|
+
},
|
|
121
|
+
}
|
|
122
|
+
`)
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
test('error: rejects unsafe numeric configuration versions', () => {
|
|
126
|
+
expect(() =>
|
|
127
|
+
MultisigWitness.fromRpc({
|
|
128
|
+
...rpc,
|
|
129
|
+
config: {
|
|
130
|
+
...rpc.config,
|
|
131
|
+
version: Number.MAX_SAFE_INTEGER + 1,
|
|
132
|
+
},
|
|
133
|
+
}),
|
|
134
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
135
|
+
`[MultisigConfig.InvalidConfigError: Invalid native multisig config: version must be an unsigned 64-bit integer.]`,
|
|
136
|
+
)
|
|
137
|
+
})
|
|
138
|
+
|
|
139
|
+
test('error: rejects excess root approvals', () => {
|
|
140
|
+
expect(() =>
|
|
141
|
+
MultisigWitness.fromRpc({
|
|
142
|
+
...rpc,
|
|
143
|
+
approvals: Array.from({ length: 9 }, () => rpc.approvals[0]),
|
|
144
|
+
}),
|
|
145
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
146
|
+
`[MultisigWitness.InvalidWitnessError: Invalid multisig witness: approval count exceeds 8.]`,
|
|
147
|
+
)
|
|
148
|
+
})
|
|
149
|
+
|
|
150
|
+
test('error: rejects excess nested approvals', () => {
|
|
151
|
+
expect(() =>
|
|
152
|
+
MultisigWitness.fromRpc({
|
|
153
|
+
...rpc,
|
|
154
|
+
approvals: [
|
|
155
|
+
{
|
|
156
|
+
...rpc.approvals[1],
|
|
157
|
+
witness: {
|
|
158
|
+
...rpc.approvals[1].witness,
|
|
159
|
+
approvals: Array.from(
|
|
160
|
+
{ length: 9 },
|
|
161
|
+
() => rpc.approvals[1].witness.approvals[0],
|
|
162
|
+
),
|
|
163
|
+
},
|
|
164
|
+
},
|
|
165
|
+
],
|
|
166
|
+
}),
|
|
167
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
168
|
+
`[MultisigWitness.InvalidWitnessError: Invalid multisig witness: approval count exceeds 8.]`,
|
|
169
|
+
)
|
|
170
|
+
})
|
|
171
|
+
})
|
|
172
|
+
|
|
173
|
+
describe('toRpc', () => {
|
|
174
|
+
test('behavior: converts bigint configuration versions to numbers', () => {
|
|
175
|
+
expect(
|
|
176
|
+
MultisigWitness.toRpc(MultisigWitness.fromRpc(rpc)),
|
|
177
|
+
).toMatchInlineSnapshot(`
|
|
178
|
+
{
|
|
179
|
+
"account": "0xcccccccccccccccccccccccccccccccccccccccc",
|
|
180
|
+
"approvals": [
|
|
181
|
+
{
|
|
182
|
+
"keyData": "0x0005",
|
|
183
|
+
"keyType": "webAuthn",
|
|
184
|
+
"owner": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
185
|
+
"type": "primitive",
|
|
186
|
+
},
|
|
187
|
+
{
|
|
188
|
+
"type": "multisig",
|
|
189
|
+
"witness": {
|
|
190
|
+
"account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
191
|
+
"approvals": [
|
|
192
|
+
{
|
|
193
|
+
"keyData": "0x0006",
|
|
194
|
+
"keyType": "secp256k1",
|
|
195
|
+
"owner": "0xdddddddddddddddddddddddddddddddddddddddd",
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
"owner": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
199
|
+
},
|
|
200
|
+
],
|
|
201
|
+
"config": {
|
|
202
|
+
"owners": [
|
|
203
|
+
{
|
|
204
|
+
"owner": "0xdddddddddddddddddddddddddddddddddddddddd",
|
|
205
|
+
"weight": 1,
|
|
206
|
+
},
|
|
207
|
+
{
|
|
208
|
+
"owner": "0xeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeeee",
|
|
209
|
+
"weight": 1,
|
|
210
|
+
},
|
|
211
|
+
],
|
|
212
|
+
"salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
|
|
213
|
+
"threshold": 2,
|
|
214
|
+
"version": 2,
|
|
215
|
+
},
|
|
216
|
+
},
|
|
217
|
+
},
|
|
218
|
+
],
|
|
219
|
+
"config": {
|
|
220
|
+
"owners": [
|
|
221
|
+
{
|
|
222
|
+
"owner": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
|
|
223
|
+
"weight": 1,
|
|
224
|
+
},
|
|
225
|
+
{
|
|
226
|
+
"owner": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
|
|
227
|
+
"weight": 1,
|
|
228
|
+
},
|
|
229
|
+
],
|
|
230
|
+
"salt": "0x1111111111111111111111111111111111111111111111111111111111111111",
|
|
231
|
+
"threshold": 2,
|
|
232
|
+
"version": 1,
|
|
233
|
+
},
|
|
234
|
+
}
|
|
235
|
+
`)
|
|
236
|
+
})
|
|
237
|
+
|
|
238
|
+
test('error: rejects versions that JSON cannot represent exactly', () => {
|
|
239
|
+
const witness = MultisigWitness.fromRpc(rpc)
|
|
240
|
+
expect(() =>
|
|
241
|
+
MultisigWitness.toRpc({
|
|
242
|
+
...witness,
|
|
243
|
+
config: {
|
|
244
|
+
...witness.config,
|
|
245
|
+
version: BigInt(Number.MAX_SAFE_INTEGER) + 1n,
|
|
246
|
+
},
|
|
247
|
+
}),
|
|
248
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
249
|
+
`[Hex.IntegerOutOfRangeError: Number \`9007199254740992\` is not in safe unsigned integer range (\`0\` to \`9007199254740991\`)]`,
|
|
250
|
+
)
|
|
251
|
+
})
|
|
252
|
+
|
|
253
|
+
test('error: rejects excess root approvals', () => {
|
|
254
|
+
const witness = MultisigWitness.fromRpc(rpc)
|
|
255
|
+
expect(() =>
|
|
256
|
+
MultisigWitness.toRpc({
|
|
257
|
+
...witness,
|
|
258
|
+
approvals: Array.from({ length: 9 }, () => witness.approvals[0]!),
|
|
259
|
+
}),
|
|
260
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
261
|
+
`[MultisigWitness.InvalidWitnessError: Invalid multisig witness: approval count exceeds 8.]`,
|
|
262
|
+
)
|
|
263
|
+
})
|
|
264
|
+
|
|
265
|
+
test('error: rejects excess nested approvals', () => {
|
|
266
|
+
const witness = MultisigWitness.fromRpc(rpc)
|
|
267
|
+
const nested = witness.approvals[1]!
|
|
268
|
+
if (nested.type !== 'multisig') throw new Error('unreachable')
|
|
269
|
+
expect(() =>
|
|
270
|
+
MultisigWitness.toRpc({
|
|
271
|
+
...witness,
|
|
272
|
+
approvals: [
|
|
273
|
+
{
|
|
274
|
+
...nested,
|
|
275
|
+
witness: {
|
|
276
|
+
...nested.witness,
|
|
277
|
+
approvals: Array.from(
|
|
278
|
+
{ length: 9 },
|
|
279
|
+
() => nested.witness.approvals[0]!,
|
|
280
|
+
),
|
|
281
|
+
},
|
|
282
|
+
},
|
|
283
|
+
],
|
|
284
|
+
}),
|
|
285
|
+
).toThrowErrorMatchingInlineSnapshot(
|
|
286
|
+
`[MultisigWitness.InvalidWitnessError: Invalid multisig witness: approval count exceeds 8.]`,
|
|
287
|
+
)
|
|
288
|
+
})
|
|
289
|
+
})
|