ox 0.14.32 → 0.14.34

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.
Files changed (48) hide show
  1. package/CHANGELOG.md +24 -0
  2. package/_cjs/tempo/KeyAuthorization.js +6 -6
  3. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  4. package/_cjs/tempo/MultisigConfig.js +4 -4
  5. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  6. package/_cjs/tempo/SignatureEnvelope.js +23 -15
  7. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  8. package/_cjs/tempo/ZoneId.js +38 -6
  9. package/_cjs/tempo/ZoneId.js.map +1 -1
  10. package/_cjs/tempo/index.js.map +1 -1
  11. package/_cjs/version.js +1 -1
  12. package/_esm/tempo/KeyAuthorization.js +7 -13
  13. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  14. package/_esm/tempo/MultisigConfig.js +17 -16
  15. package/_esm/tempo/MultisigConfig.js.map +1 -1
  16. package/_esm/tempo/SignatureEnvelope.js +42 -28
  17. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  18. package/_esm/tempo/ZoneId.js +46 -16
  19. package/_esm/tempo/ZoneId.js.map +1 -1
  20. package/_esm/tempo/index.js +7 -8
  21. package/_esm/tempo/index.js.map +1 -1
  22. package/_esm/version.js +1 -1
  23. package/_types/tempo/KeyAuthorization.d.ts +16 -12
  24. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  25. package/_types/tempo/MultisigConfig.d.ts +20 -17
  26. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  27. package/_types/tempo/SignatureEnvelope.d.ts +26 -18
  28. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  29. package/_types/tempo/ZoneId.d.ts +35 -17
  30. package/_types/tempo/ZoneId.d.ts.map +1 -1
  31. package/_types/tempo/index.d.ts +7 -8
  32. package/_types/tempo/index.d.ts.map +1 -1
  33. package/_types/version.d.ts +1 -1
  34. package/package.json +6 -1
  35. package/tempo/KeyAuthorization.test-d.ts +41 -6
  36. package/tempo/KeyAuthorization.test.ts +106 -60
  37. package/tempo/KeyAuthorization.ts +36 -38
  38. package/tempo/MultisigConfig.test.ts +28 -8
  39. package/tempo/MultisigConfig.ts +28 -19
  40. package/tempo/SignatureEnvelope.test.ts +64 -19
  41. package/tempo/SignatureEnvelope.ts +52 -36
  42. package/tempo/ZoneId.test-d/package.json +6 -0
  43. package/tempo/ZoneId.test-d.ts +16 -0
  44. package/tempo/ZoneId.test.ts +48 -13
  45. package/tempo/ZoneId.ts +55 -19
  46. package/tempo/index.ts +7 -8
  47. package/tempo/multisig.e2e.test.ts +402 -145
  48. package/version.ts +1 -1
@@ -23,6 +23,12 @@ const multisig = {
23
23
  type: 'multisig',
24
24
  } as const satisfies SignatureEnvelope.Multisig
25
25
 
26
+ const keychain = {
27
+ inner: signature,
28
+ type: 'keychain',
29
+ userAddress: authorization.address,
30
+ } as const satisfies SignatureEnvelope.Keychain
31
+
26
32
  test('accepts primitive signatures', () => {
27
33
  const signed = KeyAuthorization.from(authorization, { signature })
28
34
 
@@ -31,11 +37,12 @@ test('accepts primitive signatures', () => {
31
37
  >()
32
38
  })
33
39
 
34
- test('rejects multisig signatures', () => {
35
- KeyAuthorization.from(authorization, {
36
- // @ts-expect-error Key authorizations accept only primitive signatures.
37
- signature: multisig,
38
- })
40
+ test('accepts multisig signatures', () => {
41
+ const signed = KeyAuthorization.from(authorization, { signature: multisig })
42
+
43
+ expectTypeOf(signed.signature).toMatchTypeOf<
44
+ KeyAuthorization.Signed['signature']
45
+ >()
39
46
 
40
47
  const multisigRpc = {
41
48
  account: multisig.account,
@@ -54,9 +61,37 @@ test('rejects multisig signatures', () => {
54
61
  expiry: null,
55
62
  keyId: authorization.address,
56
63
  keyType: authorization.type,
57
- // @ts-expect-error Key authorizations accept only primitive RPC signatures.
58
64
  signature: multisigRpc,
59
65
  }
60
66
 
61
67
  expectTypeOf(rpc).toEqualTypeOf<KeyAuthorization.Rpc>()
62
68
  })
69
+
70
+ test('rejects keychain signatures', () => {
71
+ KeyAuthorization.from(authorization, {
72
+ // @ts-expect-error Key authorizations do not accept keychain signatures.
73
+ signature: keychain,
74
+ })
75
+
76
+ const keychainRpc = {
77
+ signature: {
78
+ r: '0x01',
79
+ s: '0x02',
80
+ type: 'secp256k1',
81
+ yParity: '0x0',
82
+ },
83
+ type: 'keychain',
84
+ userAddress: authorization.address,
85
+ } as const satisfies SignatureEnvelope.KeychainRpc
86
+
87
+ const rpc: KeyAuthorization.Rpc = {
88
+ chainId: '0x1',
89
+ expiry: null,
90
+ keyId: authorization.address,
91
+ keyType: authorization.type,
92
+ // @ts-expect-error Key authorizations do not accept keychain RPC signatures.
93
+ signature: keychainRpc,
94
+ }
95
+
96
+ expectTypeOf(rpc).toEqualTypeOf<KeyAuthorization.Rpc>()
97
+ })
@@ -53,11 +53,17 @@ const signature_webauthn = SignatureEnvelope.from({
53
53
  },
54
54
  })
55
55
 
56
- const signature_multisig = SignatureEnvelope.from({
56
+ const signature_multisig = {
57
57
  account: address,
58
58
  signatures: [SignatureEnvelope.from(signature_secp256k1)],
59
59
  type: 'multisig',
60
- })
60
+ } as const satisfies SignatureEnvelope.Multisig
61
+
62
+ const signature_keychain = {
63
+ inner: SignatureEnvelope.from(signature_secp256k1),
64
+ type: 'keychain',
65
+ userAddress: address,
66
+ } as const satisfies SignatureEnvelope.Keychain
61
67
 
62
68
  describe('from', () => {
63
69
  test('default', () => {
@@ -448,7 +454,34 @@ describe('from', () => {
448
454
  `)
449
455
  })
450
456
 
451
- test('rejects a multisig signature', () => {
457
+ test('with signature (multisig)', () => {
458
+ const authorization = KeyAuthorization.from({
459
+ address,
460
+ chainId: 1n,
461
+ type: 'secp256k1',
462
+ })
463
+
464
+ const signed = KeyAuthorization.from(authorization, {
465
+ signature: signature_multisig,
466
+ })
467
+ expect(signed.signature).toEqual(signature_multisig)
468
+ expect(
469
+ KeyAuthorization.from(authorization, {
470
+ signature: SignatureEnvelope.serialize(signature_multisig),
471
+ }).signature,
472
+ ).toEqual(signature_multisig)
473
+ expect(
474
+ KeyAuthorization.from({
475
+ ...authorization,
476
+ signature: signature_multisig,
477
+ }).signature,
478
+ ).toEqual(signature_multisig)
479
+ expect(
480
+ KeyAuthorization.deserialize(KeyAuthorization.serialize(signed)),
481
+ ).toEqual(signed)
482
+ })
483
+
484
+ test('rejects a keychain signature', () => {
452
485
  const authorization = KeyAuthorization.from({
453
486
  address,
454
487
  chainId: 1n,
@@ -457,50 +490,55 @@ describe('from', () => {
457
490
 
458
491
  expect(() =>
459
492
  KeyAuthorization.from(authorization, {
460
- signature: signature_multisig as never,
493
+ signature: signature_keychain as never,
461
494
  }),
462
495
  ).toThrowErrorMatchingInlineSnapshot(
463
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
496
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
464
497
  )
465
498
 
466
499
  expect(() =>
467
500
  KeyAuthorization.from(authorization, {
468
- signature: SignatureEnvelope.serialize(signature_multisig),
501
+ signature: SignatureEnvelope.serialize(signature_keychain),
469
502
  }),
470
503
  ).toThrowErrorMatchingInlineSnapshot(
471
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
504
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
472
505
  )
473
506
 
474
507
  expect(() =>
475
508
  KeyAuthorization.from({
476
509
  ...authorization,
477
- signature: signature_multisig,
510
+ signature: signature_keychain,
478
511
  } as never),
479
512
  ).toThrowErrorMatchingInlineSnapshot(
480
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
513
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
481
514
  )
482
515
  })
483
516
  })
484
517
 
485
518
  describe('fromRpc', () => {
486
- test('rejects a multisig signature', () => {
519
+ test('multisig', () => {
520
+ const authorization = KeyAuthorization.fromRpc({
521
+ chainId: '0x1',
522
+ expiry: null,
523
+ keyId: address,
524
+ keyType: 'secp256k1',
525
+ signature: SignatureEnvelope.toRpc(signature_multisig),
526
+ })
527
+
528
+ expect(authorization.signature).toEqual(signature_multisig)
529
+ })
530
+
531
+ test('rejects a keychain signature', () => {
487
532
  expect(() =>
488
533
  KeyAuthorization.fromRpc({
489
534
  chainId: '0x1',
490
535
  expiry: null,
491
536
  keyId: address,
492
537
  keyType: 'secp256k1',
493
- signature: {
494
- account: address,
495
- signatures: [
496
- SignatureEnvelope.toRpc(
497
- SignatureEnvelope.from(signature_secp256k1),
498
- ),
499
- ],
500
- },
538
+ signature: SignatureEnvelope.toRpc(signature_keychain),
501
539
  } as never),
502
540
  ).toThrowErrorMatchingInlineSnapshot(
503
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
541
+ `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`keychain\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.]`,
504
542
  )
505
543
  })
506
544
 
@@ -721,15 +759,13 @@ describe('fromRpc', () => {
721
759
  })
722
760
 
723
761
  describe('fromTuple', () => {
724
- test('rejects a multisig signature', () => {
725
- expect(() =>
726
- KeyAuthorization.fromTuple([
727
- ['0x1', '0x', address],
728
- SignatureEnvelope.serialize(signature_multisig),
729
- ]),
730
- ).toThrowErrorMatchingInlineSnapshot(
731
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
732
- )
762
+ test('multisig', () => {
763
+ const authorization = KeyAuthorization.fromTuple([
764
+ ['0x1', '0x', address],
765
+ SignatureEnvelope.serialize(signature_multisig),
766
+ ])
767
+
768
+ expect(authorization.signature).toEqual(signature_multisig)
733
769
  })
734
770
 
735
771
  test('default', () => {
@@ -1069,16 +1105,14 @@ describe('getSignPayload', () => {
1069
1105
  })
1070
1106
 
1071
1107
  describe('deserialize', () => {
1072
- test('rejects a multisig signature', () => {
1108
+ test('multisig', () => {
1073
1109
  const serialized = Rlp.fromHex([
1074
1110
  ['0x1', '0x', address],
1075
1111
  SignatureEnvelope.serialize(signature_multisig),
1076
1112
  ])
1077
1113
 
1078
- expect(() =>
1079
- KeyAuthorization.deserialize(serialized),
1080
- ).toThrowErrorMatchingInlineSnapshot(
1081
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
1114
+ expect(KeyAuthorization.deserialize(serialized).signature).toEqual(
1115
+ signature_multisig,
1082
1116
  )
1083
1117
  })
1084
1118
 
@@ -1252,16 +1286,16 @@ describe('serialize', () => {
1252
1286
  })
1253
1287
 
1254
1288
  describe('toRpc', () => {
1255
- test('rejects a multisig signature', () => {
1256
- expect(() =>
1257
- KeyAuthorization.toRpc({
1258
- address,
1259
- chainId: 1n,
1260
- signature: signature_multisig,
1261
- type: 'secp256k1',
1262
- } as never),
1263
- ).toThrowErrorMatchingInlineSnapshot(
1264
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
1289
+ test('multisig', () => {
1290
+ const authorization = KeyAuthorization.toRpc({
1291
+ address,
1292
+ chainId: 1n,
1293
+ signature: signature_multisig,
1294
+ type: 'secp256k1',
1295
+ })
1296
+
1297
+ expect(authorization.signature).toEqual(
1298
+ SignatureEnvelope.toRpc(signature_multisig),
1265
1299
  )
1266
1300
  })
1267
1301
 
@@ -1522,17 +1556,15 @@ describe('toRpc', () => {
1522
1556
  })
1523
1557
 
1524
1558
  describe('toTuple', () => {
1525
- test('rejects a multisig signature', () => {
1526
- expect(() =>
1527
- KeyAuthorization.toTuple({
1528
- address,
1529
- chainId: 1n,
1530
- signature: signature_multisig,
1531
- type: 'secp256k1',
1532
- } as never),
1533
- ).toThrowErrorMatchingInlineSnapshot(
1534
- `[KeyAuthorization.InvalidSignatureTypeError: Signature type \`multisig\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.]`,
1535
- )
1559
+ test('multisig', () => {
1560
+ const tuple = KeyAuthorization.toTuple({
1561
+ address,
1562
+ chainId: 1n,
1563
+ signature: signature_multisig,
1564
+ type: 'secp256k1',
1565
+ })
1566
+
1567
+ expect(tuple[1]).toBe(SignatureEnvelope.serialize(signature_multisig))
1536
1568
  })
1537
1569
 
1538
1570
  test('default', () => {
@@ -2483,12 +2515,12 @@ describe('admin keys (TIP-1049)', () => {
2483
2515
  expect(restored.account).toBeUndefined()
2484
2516
  })
2485
2517
 
2486
- test('fromTuple: drops orphan account without isAdmin', () => {
2518
+ test('fromTuple: preserves a non-admin account binding', () => {
2487
2519
  const restored = KeyAuthorization.fromTuple([
2488
2520
  ['0x01', '0x', address, '0x', [], [], '0x', '0x', account],
2489
2521
  ])
2490
- expect(restored.isAdmin).toBeUndefined()
2491
- expect(restored.account).toBeUndefined()
2522
+ expect(restored.isAdmin).toBe(false)
2523
+ expect(restored.account).toBe(account)
2492
2524
  })
2493
2525
 
2494
2526
  test('fromTuple: extracts isAdmin + account together', () => {
@@ -2523,6 +2555,20 @@ describe('admin keys (TIP-1049)', () => {
2523
2555
  expect(restored.account).toBe(account)
2524
2556
  })
2525
2557
 
2558
+ test('serialize/deserialize: roundtrip with non-admin account binding', () => {
2559
+ const authorization = KeyAuthorization.from({
2560
+ address,
2561
+ account,
2562
+ chainId: 1n,
2563
+ isAdmin: false,
2564
+ type: 'secp256k1',
2565
+ })
2566
+ const serialized = KeyAuthorization.serialize(authorization)
2567
+ const restored = KeyAuthorization.deserialize(serialized)
2568
+ expect(restored.isAdmin).toBe(false)
2569
+ expect(restored.account).toBe(account)
2570
+ })
2571
+
2526
2572
  test('toRpc/fromRpc: roundtrip with isAdmin + account', () => {
2527
2573
  const authorization = KeyAuthorization.from(
2528
2574
  {
@@ -2553,15 +2599,15 @@ describe('admin keys (TIP-1049)', () => {
2553
2599
  expect(restored.account).toBeUndefined()
2554
2600
  })
2555
2601
 
2556
- test('fromRpc: drops orphan account without isAdmin', () => {
2602
+ test('fromRpc: preserves a non-admin account binding', () => {
2557
2603
  const authorization = KeyAuthorization.from(
2558
2604
  { address, chainId: 1n, type: 'secp256k1' },
2559
2605
  { signature: SignatureEnvelope.from(signature_secp256k1) },
2560
2606
  )
2561
2607
  const rpc = KeyAuthorization.toRpc(authorization)
2562
2608
  const restored = KeyAuthorization.fromRpc({ ...rpc, account })
2563
- expect(restored.isAdmin).toBeUndefined()
2564
- expect(restored.account).toBeUndefined()
2609
+ expect(restored.isAdmin).toBe(false)
2610
+ expect(restored.account).toBe(account)
2565
2611
  })
2566
2612
 
2567
2613
  test('hash: changes when admin pair is added', () => {
@@ -81,13 +81,22 @@ export type KeyAuthorization<
81
81
  | {}
82
82
  > &
83
83
  (signed extends true
84
- ? { signature: SignatureEnvelope.Primitive<bigintType, numberType> }
84
+ ? { signature: Signature<bigintType, numberType> }
85
85
  : {
86
- signature?:
87
- | SignatureEnvelope.Primitive<bigintType, numberType>
88
- | undefined
86
+ signature?: Signature<bigintType, numberType> | undefined
89
87
  })
90
88
 
89
+ /** Signature that can authorize an access key. */
90
+ export type Signature<bigintType = bigint, numberType = number> = OneOf<
91
+ | SignatureEnvelope.Primitive<bigintType, numberType>
92
+ | SignatureEnvelope.Multisig<bigintType, numberType>
93
+ >
94
+
95
+ /** RPC-formatted signature that can authorize an access key. */
96
+ export type SignatureRpc = OneOf<
97
+ SignatureEnvelope.PrimitiveRpc | SignatureEnvelope.MultisigRpc
98
+ >
99
+
91
100
  /** Input type for a Key Authorization. */
92
101
  export type Input = KeyAuthorization<
93
102
  false,
@@ -114,8 +123,8 @@ export type Rpc = {
114
123
  keyType: SignatureEnvelope.Type
115
124
  /** Token spending limits. */
116
125
  limits?: readonly RpcTokenLimit[] | undefined
117
- /** Primitive signature authorizing this key. */
118
- signature: SignatureEnvelope.PrimitiveRpc
126
+ /** Signature authorizing this key. */
127
+ signature: SignatureRpc
119
128
  /** Optional 32-byte witness (hex). */
120
129
  witness?: Hex.Hex | null | undefined
121
130
  }
@@ -146,10 +155,11 @@ export type Signed<
146
155
  addressType = Address.Address,
147
156
  > = KeyAuthorization<true, bigintType, numberType, addressType>
148
157
 
149
- type PrimitiveSignatureValue =
150
- | UnionPartialBy<SignatureEnvelope.Primitive, 'prehash' | 'type'>
158
+ type SignatureValue =
159
+ | UnionPartialBy<Signature, 'prehash' | 'type'>
151
160
  | SignatureEnvelope.Secp256k1Flat
152
161
  | SignatureEnvelope.Serialized
162
+ | SignatureEnvelope.from.MultisigFromInitialConfig
153
163
 
154
164
  type BaseTuple = readonly [
155
165
  chainId: Hex.Hex,
@@ -408,7 +418,7 @@ export type TokenLimit<
408
418
  */
409
419
  export function from<
410
420
  const authorization extends Input | Rpc,
411
- const signature extends PrimitiveSignatureValue | undefined = undefined,
421
+ const signature extends SignatureValue | undefined = undefined,
412
422
  >(
413
423
  authorization: authorization | KeyAuthorization,
414
424
  options: from.Options<signature> = {},
@@ -467,29 +477,25 @@ export function from<
467
477
 
468
478
  export declare namespace from {
469
479
  type Options<
470
- signature extends PrimitiveSignatureValue | undefined =
471
- | PrimitiveSignatureValue
472
- | undefined,
480
+ signature extends SignatureValue | undefined = SignatureValue | undefined,
473
481
  > = {
474
- /** The primitive signature to attach to the Key Authorization. */
475
- signature?: signature | SignatureEnvelope.Primitive | undefined
482
+ /** The signature to attach to the Key Authorization. */
483
+ signature?: signature | Signature | undefined
476
484
  }
477
485
 
478
486
  type ReturnType<
479
487
  authorization extends KeyAuthorization | Input | Rpc = KeyAuthorization,
480
- signature extends PrimitiveSignatureValue | undefined =
481
- | PrimitiveSignatureValue
482
- | undefined,
488
+ signature extends SignatureValue | undefined = SignatureValue | undefined,
483
489
  > = Compute<
484
490
  authorization extends Rpc
485
491
  ? Signed
486
492
  : TempoAddress.ResolveAddresses<
487
493
  authorization &
488
- (signature extends PrimitiveSignatureValue
494
+ (signature extends SignatureValue
489
495
  ? {
490
496
  signature: Extract<
491
497
  SignatureEnvelope.from.ReturnValue<signature>,
492
- SignatureEnvelope.Primitive
498
+ Signature
493
499
  >
494
500
  }
495
501
  : {})
@@ -551,11 +557,8 @@ export function fromRpc(authorization: Rpc): Signed {
551
557
  })
552
558
  : undefined
553
559
 
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 } : {}
560
+ const accountBinding =
561
+ account !== undefined ? { account, isAdmin: isAdmin ?? false } : {}
559
562
 
560
563
  return {
561
564
  address: keyId,
@@ -572,7 +575,7 @@ export function fromRpc(authorization: Rpc): Signed {
572
575
  signature,
573
576
  type: keyType,
574
577
  ...(witness !== undefined ? { witness } : {}),
575
- ...adminPair,
578
+ ...accountBinding,
576
579
  }
577
580
  }
578
581
 
@@ -694,11 +697,8 @@ export function fromTuple<const tuple extends Tuple>(
694
697
  const account = isAbsent(rawAccount)
695
698
  ? undefined
696
699
  : (rawAccount as Address.Address)
697
- // TIP-1049 admin fields are paired: only emit both when both are present on
698
- // the wire. Wire shapes carrying only one are tolerated for forward-compat
699
- // but the orphan field is dropped (since the public API requires both).
700
- const adminPair =
701
- account !== undefined && isAdmin ? { account, isAdmin: true as const } : {}
700
+ const accountBinding =
701
+ account !== undefined ? { account, isAdmin: isAdmin ?? false } : {}
702
702
  const args: KeyAuthorization = {
703
703
  address: keyId,
704
704
  chainId: chainId === '0x' ? 0n : Hex.toBigInt(chainId),
@@ -707,7 +707,7 @@ export function fromTuple<const tuple extends Tuple>(
707
707
  ...(limits !== undefined ? { limits } : {}),
708
708
  ...(scopes !== undefined ? { scopes } : {}),
709
709
  ...(witness !== undefined ? { witness } : {}),
710
- ...adminPair,
710
+ ...accountBinding,
711
711
  }
712
712
  if (signatureSerialized) {
713
713
  const signature = SignatureEnvelope.deserialize(signatureSerialized)
@@ -963,9 +963,7 @@ export function toRpc(authorization: Signed): Rpc {
963
963
  limit: Hex.fromNumber(limit),
964
964
  ...(period ? { period: numberToHex(period) } : {}),
965
965
  })),
966
- signature: SignatureEnvelope.toRpc(
967
- signature,
968
- ) as SignatureEnvelope.PrimitiveRpc,
966
+ signature: SignatureEnvelope.toRpc(signature) as SignatureRpc,
969
967
  ...(allowedCalls ? { allowedCalls } : {}),
970
968
  ...(witness !== undefined ? { witness } : {}),
971
969
  ...(isAdmin ? { isAdmin: true } : {}),
@@ -1162,8 +1160,8 @@ function assertWitness(witness: Hex.Hex): void {
1162
1160
 
1163
1161
  function assertSignature<bigintType, numberType>(
1164
1162
  signature: SignatureEnvelope.SignatureEnvelope<bigintType, numberType>,
1165
- ): asserts signature is SignatureEnvelope.Primitive<bigintType, numberType> {
1166
- if (signature.type === 'keychain' || signature.type === 'multisig')
1163
+ ): asserts signature is Signature<bigintType, numberType> {
1164
+ if (signature.type === 'keychain')
1167
1165
  throw new InvalidSignatureTypeError(signature.type)
1168
1166
  }
1169
1167
 
@@ -1191,12 +1189,12 @@ export class InvalidAdminMarkerError extends Error {
1191
1189
  }
1192
1190
  }
1193
1191
 
1194
- /** Thrown when a key authorization contains a non-primitive signature. */
1192
+ /** Thrown when a key authorization contains a keychain signature. */
1195
1193
  export class InvalidSignatureTypeError extends Error {
1196
1194
  override readonly name = 'KeyAuthorization.InvalidSignatureTypeError'
1197
1195
  constructor(type: SignatureEnvelope.SignatureEnvelope['type']) {
1198
1196
  super(
1199
- `Signature type \`${type}\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, or \`webAuthn\`.`,
1197
+ `Signature type \`${type}\` is invalid for key authorizations; expected \`secp256k1\`, \`p256\`, \`webAuthn\`, or \`multisig\`.`,
1200
1198
  )
1201
1199
  }
1202
1200
  }
@@ -86,24 +86,44 @@ describe('getSignPayload', () => {
86
86
  test('matches independent ground truth', () => {
87
87
  expect(
88
88
  MultisigConfig.getSignPayload({
89
- payload: `0x${'de'.repeat(32)}`,
90
- genesisConfig: singleOwnerConfig,
89
+ payload: `0x${'42'.repeat(32)}`,
90
+ initialConfig: singleOwnerConfig,
91
91
  }),
92
92
  ).toMatchInlineSnapshot(
93
- `"0x7df8cb9fef4fc2aeb271b617d1a4c6178b720ae1d7564f48a363069b7d77a079"`,
93
+ `"0xbf944a7a752b2cfab0418d5fb4591c5a7ff62976488edce11794d7f35fb34f41"`,
94
94
  )
95
95
  })
96
96
 
97
- test('behavior: `genesisConfig` and `{ account }` produce identical digests', () => {
97
+ test('behavior: `initialConfig` and `{ account }` produce identical digests', () => {
98
98
  const account = MultisigConfig.getAddress(singleOwnerConfig)
99
99
  const payload = `0x${'42'.repeat(32)}` as const
100
100
  expect(
101
101
  MultisigConfig.getSignPayload({
102
102
  payload,
103
- genesisConfig: singleOwnerConfig,
103
+ initialConfig: singleOwnerConfig,
104
104
  }),
105
105
  ).toBe(MultisigConfig.getSignPayload({ payload, account }))
106
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
+ )
126
+ })
107
127
  })
108
128
 
109
129
  describe('toTuple / fromTuple', () => {
@@ -149,8 +169,8 @@ describe('assert / validate', () => {
149
169
  expect(MultisigConfig.validate({ threshold: 1, owners: [] })).toBe(false)
150
170
  })
151
171
 
152
- test('accepts 255 owners', () => {
153
- const owners = Array.from({ length: 255 }, (_, i) => ({
172
+ test('accepts 50 owners', () => {
173
+ const owners = Array.from({ length: 50 }, (_, i) => ({
154
174
  owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
155
175
  weight: 1,
156
176
  }))
@@ -163,7 +183,7 @@ describe('assert / validate', () => {
163
183
  })
164
184
 
165
185
  test('too many owners', () => {
166
- const owners = Array.from({ length: 256 }, (_, i) => ({
186
+ const owners = Array.from({ length: 51 }, (_, i) => ({
167
187
  owner: `0x${(i + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
168
188
  weight: 1,
169
189
  }))