ox 0.14.29 → 0.14.31
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 +30 -0
- package/_cjs/core/AbiParameters.js +2 -1
- package/_cjs/core/AbiParameters.js.map +1 -1
- package/_cjs/core/internal/abiParameters.js +10 -2
- package/_cjs/core/internal/abiParameters.js.map +1 -1
- package/_cjs/tempo/KeyAuthorization.js +39 -10
- package/_cjs/tempo/KeyAuthorization.js.map +1 -1
- package/_cjs/tempo/MultisigConfig.js +26 -26
- package/_cjs/tempo/MultisigConfig.js.map +1 -1
- package/_cjs/tempo/SignatureEnvelope.js +189 -55
- package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
- package/_cjs/version.js +1 -1
- package/_esm/core/AbiParameters.js +4 -1
- package/_esm/core/AbiParameters.js.map +1 -1
- package/_esm/core/internal/abiParameters.js +17 -2
- package/_esm/core/internal/abiParameters.js.map +1 -1
- package/_esm/tempo/KeyAuthorization.js +41 -9
- package/_esm/tempo/KeyAuthorization.js.map +1 -1
- package/_esm/tempo/MultisigConfig.js +53 -81
- package/_esm/tempo/MultisigConfig.js.map +1 -1
- package/_esm/tempo/SignatureEnvelope.js +202 -75
- package/_esm/tempo/SignatureEnvelope.js.map +1 -1
- package/_esm/tempo/index.js +2 -2
- package/_esm/version.js +1 -1
- package/_types/core/AbiParameters.d.ts.map +1 -1
- package/_types/core/internal/abiParameters.d.ts.map +1 -1
- package/_types/core/internal/rpcSchemas/eth.d.ts +16 -0
- package/_types/core/internal/rpcSchemas/eth.d.ts.map +1 -1
- package/_types/tempo/KeyAuthorization.d.ts +22 -16
- package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
- package/_types/tempo/MultisigConfig.d.ts +43 -83
- package/_types/tempo/MultisigConfig.d.ts.map +1 -1
- package/_types/tempo/SignatureEnvelope.d.ts +62 -40
- 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/core/AbiParameters.ts +3 -1
- package/core/internal/abiParameters.ts +17 -2
- package/core/internal/rpcSchemas/eth.ts +16 -0
- package/package.json +11 -1
- package/tempo/KeyAuthorization.test-d/package.json +6 -0
- package/tempo/KeyAuthorization.test-d.ts +62 -0
- package/tempo/KeyAuthorization.test.ts +132 -0
- package/tempo/KeyAuthorization.ts +76 -29
- package/tempo/MultisigConfig.test.ts +86 -37
- package/tempo/MultisigConfig.ts +75 -132
- package/tempo/SignatureEnvelope.test-d/package.json +6 -0
- package/tempo/SignatureEnvelope.test-d.ts +97 -0
- package/tempo/SignatureEnvelope.test.ts +322 -30
- package/tempo/SignatureEnvelope.ts +314 -118
- package/tempo/e2e.test.ts +22 -207
- package/tempo/index.ts +2 -2
- package/tempo/multisig.e2e.test.ts +582 -0
- package/version.ts +1 -1
|
@@ -3,7 +3,7 @@ import type * as Address from '../core/Address.js'
|
|
|
3
3
|
import type * as Errors from '../core/Errors.js'
|
|
4
4
|
import * as Hash from '../core/Hash.js'
|
|
5
5
|
import * as Hex from '../core/Hex.js'
|
|
6
|
-
import type { Compute, OneOf } from '../core/internal/types.js'
|
|
6
|
+
import type { Compute, OneOf, UnionPartialBy } from '../core/internal/types.js'
|
|
7
7
|
import * as Rlp from '../core/Rlp.js'
|
|
8
8
|
import * as SignatureEnvelope from './SignatureEnvelope.js'
|
|
9
9
|
import * as TempoAddress from './TempoAddress.js'
|
|
@@ -81,10 +81,10 @@ export type KeyAuthorization<
|
|
|
81
81
|
| {}
|
|
82
82
|
> &
|
|
83
83
|
(signed extends true
|
|
84
|
-
? { signature: SignatureEnvelope.
|
|
84
|
+
? { signature: SignatureEnvelope.Primitive<bigintType, numberType> }
|
|
85
85
|
: {
|
|
86
86
|
signature?:
|
|
87
|
-
| SignatureEnvelope.
|
|
87
|
+
| SignatureEnvelope.Primitive<bigintType, numberType>
|
|
88
88
|
| undefined
|
|
89
89
|
})
|
|
90
90
|
|
|
@@ -114,8 +114,8 @@ export type Rpc = {
|
|
|
114
114
|
keyType: SignatureEnvelope.Type
|
|
115
115
|
/** Token spending limits. */
|
|
116
116
|
limits?: readonly RpcTokenLimit[] | undefined
|
|
117
|
-
/**
|
|
118
|
-
signature: SignatureEnvelope.
|
|
117
|
+
/** Primitive signature authorizing this key. */
|
|
118
|
+
signature: SignatureEnvelope.PrimitiveRpc
|
|
119
119
|
/** Optional 32-byte witness (hex). */
|
|
120
120
|
witness?: Hex.Hex | null | undefined
|
|
121
121
|
}
|
|
@@ -146,6 +146,11 @@ export type Signed<
|
|
|
146
146
|
addressType = Address.Address,
|
|
147
147
|
> = KeyAuthorization<true, bigintType, numberType, addressType>
|
|
148
148
|
|
|
149
|
+
type PrimitiveSignatureValue =
|
|
150
|
+
| UnionPartialBy<SignatureEnvelope.Primitive, 'prehash' | 'type'>
|
|
151
|
+
| SignatureEnvelope.Secp256k1Flat
|
|
152
|
+
| SignatureEnvelope.Serialized
|
|
153
|
+
|
|
149
154
|
type BaseTuple = readonly [
|
|
150
155
|
chainId: Hex.Hex,
|
|
151
156
|
keyType: Hex.Hex,
|
|
@@ -403,7 +408,7 @@ export type TokenLimit<
|
|
|
403
408
|
*/
|
|
404
409
|
export function from<
|
|
405
410
|
const authorization extends Input | Rpc,
|
|
406
|
-
const signature extends
|
|
411
|
+
const signature extends PrimitiveSignatureValue | undefined = undefined,
|
|
407
412
|
>(
|
|
408
413
|
authorization: authorization | KeyAuthorization,
|
|
409
414
|
options: from.Options<signature> = {},
|
|
@@ -418,6 +423,7 @@ export function from<
|
|
|
418
423
|
}[]
|
|
419
424
|
}
|
|
420
425
|
if (auth.witness !== undefined) assertWitness(auth.witness)
|
|
426
|
+
if (auth.signature) assertSignature(auth.signature)
|
|
421
427
|
const resolved = {
|
|
422
428
|
...auth,
|
|
423
429
|
address: TempoAddress.resolve(auth.address as TempoAddress.Address),
|
|
@@ -446,41 +452,51 @@ export function from<
|
|
|
446
452
|
}
|
|
447
453
|
: {}),
|
|
448
454
|
}
|
|
449
|
-
if (options.signature)
|
|
455
|
+
if (options.signature) {
|
|
456
|
+
const signature = SignatureEnvelope.from(
|
|
457
|
+
options.signature,
|
|
458
|
+
) as SignatureEnvelope.SignatureEnvelope
|
|
459
|
+
assertSignature(signature)
|
|
450
460
|
return {
|
|
451
461
|
...resolved,
|
|
452
|
-
signature
|
|
462
|
+
signature,
|
|
453
463
|
} as never
|
|
464
|
+
}
|
|
454
465
|
return resolved as never
|
|
455
466
|
}
|
|
456
467
|
|
|
457
468
|
export declare namespace from {
|
|
458
469
|
type Options<
|
|
459
|
-
signature extends
|
|
460
|
-
|
|
|
470
|
+
signature extends PrimitiveSignatureValue | undefined =
|
|
471
|
+
| PrimitiveSignatureValue
|
|
461
472
|
| undefined,
|
|
462
473
|
> = {
|
|
463
|
-
/** The
|
|
464
|
-
signature?: signature | SignatureEnvelope.
|
|
474
|
+
/** The primitive signature to attach to the Key Authorization. */
|
|
475
|
+
signature?: signature | SignatureEnvelope.Primitive | undefined
|
|
465
476
|
}
|
|
466
477
|
|
|
467
478
|
type ReturnType<
|
|
468
479
|
authorization extends KeyAuthorization | Input | Rpc = KeyAuthorization,
|
|
469
|
-
signature extends
|
|
470
|
-
|
|
|
480
|
+
signature extends PrimitiveSignatureValue | undefined =
|
|
481
|
+
| PrimitiveSignatureValue
|
|
471
482
|
| undefined,
|
|
472
483
|
> = Compute<
|
|
473
484
|
authorization extends Rpc
|
|
474
485
|
? Signed
|
|
475
486
|
: TempoAddress.ResolveAddresses<
|
|
476
487
|
authorization &
|
|
477
|
-
(signature extends
|
|
478
|
-
? {
|
|
488
|
+
(signature extends PrimitiveSignatureValue
|
|
489
|
+
? {
|
|
490
|
+
signature: Extract<
|
|
491
|
+
SignatureEnvelope.from.ReturnValue<signature>,
|
|
492
|
+
SignatureEnvelope.Primitive
|
|
493
|
+
>
|
|
494
|
+
}
|
|
479
495
|
: {})
|
|
480
496
|
>
|
|
481
497
|
>
|
|
482
498
|
|
|
483
|
-
type ErrorType = Errors.GlobalErrorType
|
|
499
|
+
type ErrorType = InvalidSignatureTypeError | Errors.GlobalErrorType
|
|
484
500
|
}
|
|
485
501
|
|
|
486
502
|
/**
|
|
@@ -515,6 +531,7 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
515
531
|
const isAdmin = authorization.isAdmin ?? undefined
|
|
516
532
|
const account = authorization.account ?? undefined
|
|
517
533
|
const signature = SignatureEnvelope.fromRpc(authorization.signature)
|
|
534
|
+
assertSignature(signature)
|
|
518
535
|
if (witness !== undefined) assertWitness(witness)
|
|
519
536
|
|
|
520
537
|
// Unflatten nested allowedCalls into flat scopes
|
|
@@ -534,6 +551,12 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
534
551
|
})
|
|
535
552
|
: undefined
|
|
536
553
|
|
|
554
|
+
// TIP-1049 admin fields are paired: emit both or neither; orphan wire
|
|
555
|
+
// fields are dropped. Separate conditional spreads break `Signed`
|
|
556
|
+
// assignability without `exactOptionalPropertyTypes` (#290).
|
|
557
|
+
const adminPair =
|
|
558
|
+
account !== undefined && isAdmin ? { account, isAdmin: true as const } : {}
|
|
559
|
+
|
|
537
560
|
return {
|
|
538
561
|
address: keyId,
|
|
539
562
|
chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
|
|
@@ -549,13 +572,12 @@ export function fromRpc(authorization: Rpc): Signed {
|
|
|
549
572
|
signature,
|
|
550
573
|
type: keyType,
|
|
551
574
|
...(witness !== undefined ? { witness } : {}),
|
|
552
|
-
...
|
|
553
|
-
...(account !== undefined ? { account } : {}),
|
|
575
|
+
...adminPair,
|
|
554
576
|
}
|
|
555
577
|
}
|
|
556
578
|
|
|
557
579
|
export declare namespace fromRpc {
|
|
558
|
-
type ErrorType = Errors.GlobalErrorType
|
|
580
|
+
type ErrorType = InvalidSignatureTypeError | Errors.GlobalErrorType
|
|
559
581
|
}
|
|
560
582
|
|
|
561
583
|
/**
|
|
@@ -687,8 +709,11 @@ export function fromTuple<const tuple extends Tuple>(
|
|
|
687
709
|
...(witness !== undefined ? { witness } : {}),
|
|
688
710
|
...adminPair,
|
|
689
711
|
}
|
|
690
|
-
if (signatureSerialized)
|
|
691
|
-
|
|
712
|
+
if (signatureSerialized) {
|
|
713
|
+
const signature = SignatureEnvelope.deserialize(signatureSerialized)
|
|
714
|
+
assertSignature(signature)
|
|
715
|
+
args.signature = signature
|
|
716
|
+
}
|
|
692
717
|
return from(args) as never
|
|
693
718
|
}
|
|
694
719
|
|
|
@@ -697,7 +722,7 @@ export declare namespace fromTuple {
|
|
|
697
722
|
KeyAuthorization<authorization extends Tuple<true> ? true : false>
|
|
698
723
|
>
|
|
699
724
|
|
|
700
|
-
type ErrorType = Errors.GlobalErrorType
|
|
725
|
+
type ErrorType = InvalidSignatureTypeError | Errors.GlobalErrorType
|
|
701
726
|
}
|
|
702
727
|
|
|
703
728
|
/**
|
|
@@ -901,6 +926,7 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
901
926
|
isAdmin,
|
|
902
927
|
account,
|
|
903
928
|
} = authorization
|
|
929
|
+
assertSignature(signature)
|
|
904
930
|
if (witness !== undefined) assertWitness(witness)
|
|
905
931
|
|
|
906
932
|
// Group flat scopes by address into nested allowedCalls wire format
|
|
@@ -937,7 +963,9 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
937
963
|
limit: Hex.fromNumber(limit),
|
|
938
964
|
...(period ? { period: numberToHex(period) } : {}),
|
|
939
965
|
})),
|
|
940
|
-
signature: SignatureEnvelope.toRpc(
|
|
966
|
+
signature: SignatureEnvelope.toRpc(
|
|
967
|
+
signature,
|
|
968
|
+
) as SignatureEnvelope.PrimitiveRpc,
|
|
941
969
|
...(allowedCalls ? { allowedCalls } : {}),
|
|
942
970
|
...(witness !== undefined ? { witness } : {}),
|
|
943
971
|
...(isAdmin ? { isAdmin: true } : {}),
|
|
@@ -946,7 +974,7 @@ export function toRpc(authorization: Signed): Rpc {
|
|
|
946
974
|
}
|
|
947
975
|
|
|
948
976
|
export declare namespace toRpc {
|
|
949
|
-
type ErrorType = Errors.GlobalErrorType
|
|
977
|
+
type ErrorType = InvalidSignatureTypeError | Errors.GlobalErrorType
|
|
950
978
|
}
|
|
951
979
|
|
|
952
980
|
/**
|
|
@@ -994,9 +1022,11 @@ export function toTuple<const authorization extends KeyAuthorization>(
|
|
|
994
1022
|
account,
|
|
995
1023
|
} = authorization
|
|
996
1024
|
if (witness !== undefined) assertWitness(witness)
|
|
997
|
-
const signature =
|
|
998
|
-
|
|
999
|
-
|
|
1025
|
+
const signature = (() => {
|
|
1026
|
+
if (!authorization.signature) return undefined
|
|
1027
|
+
assertSignature(authorization.signature)
|
|
1028
|
+
return SignatureEnvelope.serialize(authorization.signature)
|
|
1029
|
+
})()
|
|
1000
1030
|
const type = (() => {
|
|
1001
1031
|
switch (authorization.type) {
|
|
1002
1032
|
case 'secp256k1':
|
|
@@ -1092,7 +1122,7 @@ export declare namespace toTuple {
|
|
|
1092
1122
|
type ReturnType<authorization extends KeyAuthorization = KeyAuthorization> =
|
|
1093
1123
|
Compute<Tuple<authorization extends KeyAuthorization<true> ? true : false>>
|
|
1094
1124
|
|
|
1095
|
-
type ErrorType = Errors.GlobalErrorType
|
|
1125
|
+
type ErrorType = InvalidSignatureTypeError | Errors.GlobalErrorType
|
|
1096
1126
|
}
|
|
1097
1127
|
|
|
1098
1128
|
function bigintToHex(value: bigint): Hex.Hex {
|
|
@@ -1130,6 +1160,13 @@ function assertWitness(witness: Hex.Hex): void {
|
|
|
1130
1160
|
if (Hex.size(witness) !== 32) throw new InvalidWitnessSizeError(witness)
|
|
1131
1161
|
}
|
|
1132
1162
|
|
|
1163
|
+
function assertSignature<bigintType, numberType>(
|
|
1164
|
+
signature: SignatureEnvelope.SignatureEnvelope<bigintType, numberType>,
|
|
1165
|
+
): asserts signature is SignatureEnvelope.Primitive<bigintType, numberType> {
|
|
1166
|
+
if (signature.type === 'keychain' || signature.type === 'multisig')
|
|
1167
|
+
throw new InvalidSignatureTypeError(signature.type)
|
|
1168
|
+
}
|
|
1169
|
+
|
|
1133
1170
|
function isAbsent(value: unknown): boolean {
|
|
1134
1171
|
return value === undefined || value === '0x'
|
|
1135
1172
|
}
|
|
@@ -1153,3 +1190,13 @@ export class InvalidAdminMarkerError extends Error {
|
|
|
1153
1190
|
)
|
|
1154
1191
|
}
|
|
1155
1192
|
}
|
|
1193
|
+
|
|
1194
|
+
/** Thrown when a key authorization contains a non-primitive signature. */
|
|
1195
|
+
export class InvalidSignatureTypeError extends Error {
|
|
1196
|
+
override readonly name = 'KeyAuthorization.InvalidSignatureTypeError'
|
|
1197
|
+
constructor(type: SignatureEnvelope.SignatureEnvelope['type']) {
|
|
1198
|
+
super(
|
|
1199
|
+
`Signature type \`${type}\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.`,
|
|
1200
|
+
)
|
|
1201
|
+
}
|
|
1202
|
+
}
|
|
@@ -30,31 +30,51 @@ describe('from', () => {
|
|
|
30
30
|
})
|
|
31
31
|
})
|
|
32
32
|
|
|
33
|
-
describe('
|
|
33
|
+
describe('getAddress', () => {
|
|
34
34
|
test('matches independent ground truth', () => {
|
|
35
|
-
expect(MultisigConfig.
|
|
36
|
-
`"
|
|
35
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
|
|
36
|
+
`"0x8820d1497eeaf4f68e00b2cfc00a2f3b1dbb00da"`,
|
|
37
37
|
)
|
|
38
38
|
})
|
|
39
39
|
|
|
40
|
+
test('matches independent ground truth (salt + weights)', () => {
|
|
41
|
+
expect(
|
|
42
|
+
MultisigConfig.getAddress({
|
|
43
|
+
salt: `0x${'42'.repeat(32)}`,
|
|
44
|
+
threshold: 2,
|
|
45
|
+
owners: [
|
|
46
|
+
{ owner: owner1, weight: 1 },
|
|
47
|
+
{ owner: owner2, weight: 2 },
|
|
48
|
+
],
|
|
49
|
+
}),
|
|
50
|
+
).toMatchInlineSnapshot(`"0x0773e28146400643e42cb28f6659b74e7c0b451d"`)
|
|
51
|
+
})
|
|
52
|
+
|
|
40
53
|
test('is stable across calls', () => {
|
|
41
|
-
expect(MultisigConfig.
|
|
42
|
-
MultisigConfig.
|
|
54
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).toBe(
|
|
55
|
+
MultisigConfig.getAddress(singleOwnerConfig),
|
|
43
56
|
)
|
|
44
57
|
})
|
|
45
58
|
|
|
46
59
|
test('differs for a different salt', () => {
|
|
47
|
-
expect(MultisigConfig.
|
|
48
|
-
MultisigConfig.
|
|
60
|
+
expect(MultisigConfig.getAddress(singleOwnerConfig)).not.toBe(
|
|
61
|
+
MultisigConfig.getAddress({
|
|
49
62
|
...singleOwnerConfig,
|
|
50
63
|
salt: `0x${'42'.repeat(32)}`,
|
|
51
64
|
}),
|
|
52
65
|
)
|
|
53
66
|
})
|
|
54
67
|
|
|
68
|
+
test('address is chain-independent', () => {
|
|
69
|
+
// Derivation does not include chain ID; identical config → identical address.
|
|
70
|
+
const a = MultisigConfig.getAddress(singleOwnerConfig)
|
|
71
|
+
const b = MultisigConfig.getAddress(MultisigConfig.from(singleOwnerConfig))
|
|
72
|
+
expect(a).toBe(b)
|
|
73
|
+
})
|
|
74
|
+
|
|
55
75
|
test('throws on invalid config', () => {
|
|
56
76
|
expect(() =>
|
|
57
|
-
MultisigConfig.
|
|
77
|
+
MultisigConfig.getAddress({
|
|
58
78
|
threshold: 5,
|
|
59
79
|
owners: singleOwnerConfig.owners,
|
|
60
80
|
}),
|
|
@@ -62,50 +82,27 @@ describe('genesisConfigId', () => {
|
|
|
62
82
|
})
|
|
63
83
|
})
|
|
64
84
|
|
|
65
|
-
describe('getAddress', () => {
|
|
66
|
-
test('matches independent ground truth', () => {
|
|
67
|
-
expect(MultisigConfig.getAddress(singleOwnerConfig)).toMatchInlineSnapshot(
|
|
68
|
-
`"0x6ca655065b1de473d903eebd50e5cb4996e10468"`,
|
|
69
|
-
)
|
|
70
|
-
})
|
|
71
|
-
|
|
72
|
-
test('derives from positional config or `{ genesisConfigId }` identically', () => {
|
|
73
|
-
const genesisConfigId = MultisigConfig.toId(singleOwnerConfig)
|
|
74
|
-
expect(MultisigConfig.getAddress({ genesisConfigId })).toBe(
|
|
75
|
-
MultisigConfig.getAddress(singleOwnerConfig),
|
|
76
|
-
)
|
|
77
|
-
})
|
|
78
|
-
|
|
79
|
-
test('config ID and address are chain-independent', () => {
|
|
80
|
-
// Derivation does not include chain ID; identical config → identical id/address.
|
|
81
|
-
const a = MultisigConfig.toId(singleOwnerConfig)
|
|
82
|
-
const b = MultisigConfig.toId(MultisigConfig.from(singleOwnerConfig))
|
|
83
|
-
expect(a).toBe(b)
|
|
84
|
-
})
|
|
85
|
-
})
|
|
86
|
-
|
|
87
85
|
describe('getSignPayload', () => {
|
|
88
86
|
test('matches independent ground truth', () => {
|
|
89
87
|
expect(
|
|
90
88
|
MultisigConfig.getSignPayload({
|
|
91
|
-
payload: `0x${'
|
|
89
|
+
payload: `0x${'de'.repeat(32)}`,
|
|
92
90
|
genesisConfig: singleOwnerConfig,
|
|
93
91
|
}),
|
|
94
92
|
).toMatchInlineSnapshot(
|
|
95
|
-
`"
|
|
93
|
+
`"0x7df8cb9fef4fc2aeb271b617d1a4c6178b720ae1d7564f48a363069b7d77a079"`,
|
|
96
94
|
)
|
|
97
95
|
})
|
|
98
96
|
|
|
99
|
-
test('behavior: `genesisConfig` and `{account
|
|
100
|
-
const
|
|
101
|
-
const account = MultisigConfig.getAddress({ genesisConfigId })
|
|
97
|
+
test('behavior: `genesisConfig` and `{ account }` produce identical digests', () => {
|
|
98
|
+
const account = MultisigConfig.getAddress(singleOwnerConfig)
|
|
102
99
|
const payload = `0x${'42'.repeat(32)}` as const
|
|
103
100
|
expect(
|
|
104
101
|
MultisigConfig.getSignPayload({
|
|
105
102
|
payload,
|
|
106
103
|
genesisConfig: singleOwnerConfig,
|
|
107
104
|
}),
|
|
108
|
-
).toBe(MultisigConfig.getSignPayload({ payload, account
|
|
105
|
+
).toBe(MultisigConfig.getSignPayload({ payload, account }))
|
|
109
106
|
})
|
|
110
107
|
})
|
|
111
108
|
|
|
@@ -152,8 +149,21 @@ describe('assert / validate', () => {
|
|
|
152
149
|
expect(MultisigConfig.validate({ threshold: 1, owners: [] })).toBe(false)
|
|
153
150
|
})
|
|
154
151
|
|
|
152
|
+
test('accepts 255 owners', () => {
|
|
153
|
+
const owners = Array.from({ length: 255 }, (_, i) => ({
|
|
154
|
+
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
155
|
+
weight: 1,
|
|
156
|
+
}))
|
|
157
|
+
expect(
|
|
158
|
+
MultisigConfig.validate({
|
|
159
|
+
threshold: MultisigConfig.maxThreshold,
|
|
160
|
+
owners,
|
|
161
|
+
}),
|
|
162
|
+
).toBe(true)
|
|
163
|
+
})
|
|
164
|
+
|
|
155
165
|
test('too many owners', () => {
|
|
156
|
-
const owners = Array.from({ length:
|
|
166
|
+
const owners = Array.from({ length: 256 }, (_, i) => ({
|
|
157
167
|
owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
|
|
158
168
|
weight: 1,
|
|
159
169
|
}))
|
|
@@ -169,6 +179,24 @@ describe('assert / validate', () => {
|
|
|
169
179
|
).toBe(false)
|
|
170
180
|
})
|
|
171
181
|
|
|
182
|
+
test('threshold exceeds protocol maximum', () => {
|
|
183
|
+
expect(
|
|
184
|
+
MultisigConfig.validate({
|
|
185
|
+
threshold: MultisigConfig.maxThreshold + 1,
|
|
186
|
+
owners: [{ owner: owner1, weight: MultisigConfig.maxThreshold + 1 }],
|
|
187
|
+
}),
|
|
188
|
+
).toBe(false)
|
|
189
|
+
})
|
|
190
|
+
|
|
191
|
+
test('fractional threshold', () => {
|
|
192
|
+
expect(
|
|
193
|
+
MultisigConfig.validate({
|
|
194
|
+
threshold: 1.5,
|
|
195
|
+
owners: [{ owner: owner1, weight: 2 }],
|
|
196
|
+
}),
|
|
197
|
+
).toBe(false)
|
|
198
|
+
})
|
|
199
|
+
|
|
172
200
|
test('threshold exceeds total weight', () => {
|
|
173
201
|
expect(
|
|
174
202
|
MultisigConfig.validate({
|
|
@@ -178,6 +206,18 @@ describe('assert / validate', () => {
|
|
|
178
206
|
).toBe(false)
|
|
179
207
|
})
|
|
180
208
|
|
|
209
|
+
test('total weight exceeds u8 max', () => {
|
|
210
|
+
expect(
|
|
211
|
+
MultisigConfig.validate({
|
|
212
|
+
threshold: MultisigConfig.maxThreshold,
|
|
213
|
+
owners: [
|
|
214
|
+
{ owner: owner1, weight: 128 },
|
|
215
|
+
{ owner: owner2, weight: 128 },
|
|
216
|
+
],
|
|
217
|
+
}),
|
|
218
|
+
).toBe(false)
|
|
219
|
+
})
|
|
220
|
+
|
|
181
221
|
test('zero owner weight', () => {
|
|
182
222
|
expect(
|
|
183
223
|
MultisigConfig.validate({
|
|
@@ -187,6 +227,15 @@ describe('assert / validate', () => {
|
|
|
187
227
|
).toBe(false)
|
|
188
228
|
})
|
|
189
229
|
|
|
230
|
+
test('fractional owner weight', () => {
|
|
231
|
+
expect(
|
|
232
|
+
MultisigConfig.validate({
|
|
233
|
+
threshold: 1,
|
|
234
|
+
owners: [{ owner: owner1, weight: 1.5 }],
|
|
235
|
+
}),
|
|
236
|
+
).toBe(false)
|
|
237
|
+
})
|
|
238
|
+
|
|
190
239
|
test('zero owner address', () => {
|
|
191
240
|
expect(
|
|
192
241
|
MultisigConfig.validate({
|