ox 0.14.44 → 0.14.45

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 (66) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/_cjs/tempo/KeyAuthorization.js +43 -10
  3. package/_cjs/tempo/KeyAuthorization.js.map +1 -1
  4. package/_cjs/tempo/MultisigConfig.js +33 -11
  5. package/_cjs/tempo/MultisigConfig.js.map +1 -1
  6. package/_cjs/tempo/MultisigOperation.js +18 -94
  7. package/_cjs/tempo/MultisigOperation.js.map +1 -1
  8. package/_cjs/tempo/MultisigSimulation.js +18 -46
  9. package/_cjs/tempo/MultisigSimulation.js.map +1 -1
  10. package/_cjs/tempo/SignatureEnvelope.js +72 -109
  11. package/_cjs/tempo/SignatureEnvelope.js.map +1 -1
  12. package/_cjs/tempo/TransactionRequest.js +5 -1
  13. package/_cjs/tempo/TransactionRequest.js.map +1 -1
  14. package/_cjs/tempo/index.js.map +1 -1
  15. package/_cjs/version.js +1 -1
  16. package/_esm/tempo/KeyAuthorization.js +42 -9
  17. package/_esm/tempo/KeyAuthorization.js.map +1 -1
  18. package/_esm/tempo/MultisigConfig.js +41 -22
  19. package/_esm/tempo/MultisigConfig.js.map +1 -1
  20. package/_esm/tempo/MultisigOperation.js +20 -100
  21. package/_esm/tempo/MultisigOperation.js.map +1 -1
  22. package/_esm/tempo/MultisigSimulation.js +18 -46
  23. package/_esm/tempo/MultisigSimulation.js.map +1 -1
  24. package/_esm/tempo/SignatureEnvelope.js +107 -169
  25. package/_esm/tempo/SignatureEnvelope.js.map +1 -1
  26. package/_esm/tempo/TransactionRequest.js +5 -1
  27. package/_esm/tempo/TransactionRequest.js.map +1 -1
  28. package/_esm/tempo/index.js +3 -1
  29. package/_esm/tempo/index.js.map +1 -1
  30. package/_esm/version.js +1 -1
  31. package/_types/tempo/KeyAuthorization.d.ts +48 -38
  32. package/_types/tempo/KeyAuthorization.d.ts.map +1 -1
  33. package/_types/tempo/MultisigConfig.d.ts +15 -14
  34. package/_types/tempo/MultisigConfig.d.ts.map +1 -1
  35. package/_types/tempo/MultisigOperation.d.ts +3 -2
  36. package/_types/tempo/MultisigOperation.d.ts.map +1 -1
  37. package/_types/tempo/MultisigSimulation.d.ts +2 -49
  38. package/_types/tempo/MultisigSimulation.d.ts.map +1 -1
  39. package/_types/tempo/SignatureEnvelope.d.ts +69 -128
  40. package/_types/tempo/SignatureEnvelope.d.ts.map +1 -1
  41. package/_types/tempo/TransactionRequest.d.ts +5 -2
  42. package/_types/tempo/TransactionRequest.d.ts.map +1 -1
  43. package/_types/tempo/index.d.ts +3 -1
  44. package/_types/tempo/index.d.ts.map +1 -1
  45. package/_types/version.d.ts +1 -1
  46. package/package.json +1 -1
  47. package/tempo/AuthorizationTempo.test.ts +8 -4
  48. package/tempo/KeyAuthorization.test-d.ts +68 -33
  49. package/tempo/KeyAuthorization.test.ts +89 -8
  50. package/tempo/KeyAuthorization.ts +124 -61
  51. package/tempo/MultisigConfig.test.ts +34 -26
  52. package/tempo/MultisigConfig.ts +54 -24
  53. package/tempo/MultisigOperation.test.ts +79 -550
  54. package/tempo/MultisigOperation.ts +31 -146
  55. package/tempo/MultisigSimulation.test-d.ts +2 -2
  56. package/tempo/MultisigSimulation.test.ts +21 -110
  57. package/tempo/MultisigSimulation.ts +25 -99
  58. package/tempo/SignatureEnvelope.test-d.ts +29 -67
  59. package/tempo/SignatureEnvelope.test.ts +301 -568
  60. package/tempo/SignatureEnvelope.ts +156 -274
  61. package/tempo/TransactionRequest.test-d.ts +6 -5
  62. package/tempo/TransactionRequest.test.ts +17 -80
  63. package/tempo/TransactionRequest.ts +23 -3
  64. package/tempo/index.ts +3 -1
  65. package/tempo/multisig.e2e.test.ts +322 -861
  66. package/version.ts +1 -1
@@ -1,6 +1,5 @@
1
1
  import {
2
2
  Address,
3
- Hash,
4
3
  Hex,
5
4
  P256,
6
5
  PublicKey,
@@ -68,9 +67,6 @@ const signature_keychain_webauthn = SignatureEnvelope.from({
68
67
  version: 'v2',
69
68
  })
70
69
 
71
- const signature_multisig_rpc =
72
- '0xf89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
73
-
74
70
  describe('assert', () => {
75
71
  describe('secp256k1', () => {
76
72
  test('behavior: validates valid signature', () => {
@@ -1104,86 +1100,30 @@ describe('from', () => {
1104
1100
  })
1105
1101
 
1106
1102
  describe('multisig', () => {
1107
- const config = MultisigConfig.from({
1108
- owners: [
1109
- {
1110
- owner: '0x1111111111111111111111111111111111111111',
1111
- weight: 1,
1112
- },
1113
- ],
1114
- threshold: 1,
1115
- })
1116
-
1117
- test('behavior: derives an initial account from config', () => {
1103
+ test('normalizes the current config', () => {
1118
1104
  const envelope = SignatureEnvelope.from({
1119
- config,
1120
- signatures: [SignatureEnvelope.from(signature_secp256k1)],
1121
- })
1122
-
1123
- expect(envelope).toMatchInlineSnapshot(
1124
- {
1125
- signatures: [
1126
- {
1127
- signature: {
1128
- r: expect.anything(),
1129
- s: expect.anything(),
1130
- yParity: expect.any(Number),
1131
- },
1132
- },
1105
+ account: '0x2222222222222222222222222222222222222222',
1106
+ config: {
1107
+ threshold: 1,
1108
+ owners: [
1109
+ { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
1133
1110
  ],
1134
1111
  },
1135
- `
1112
+ signatures: [SignatureEnvelope.from(signature_secp256k1)],
1113
+ })
1114
+ expect(envelope.config).toMatchInlineSnapshot(`
1136
1115
  {
1137
- "account": "0xf4b916c5aea0fb199bd942389be00db0690c961f",
1138
- "config": {
1139
- "owners": [
1140
- {
1141
- "owner": "0x1111111111111111111111111111111111111111",
1142
- "weight": 1,
1143
- },
1144
- ],
1145
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
1146
- "threshold": 1,
1147
- "version": 0n,
1148
- },
1149
- "signatures": [
1116
+ "owners": [
1150
1117
  {
1151
- "signature": {
1152
- "r": Anything,
1153
- "s": Anything,
1154
- "yParity": Any<Number>,
1155
- },
1156
- "type": "secp256k1",
1118
+ "owner": "0x1111111111111111111111111111111111111111",
1119
+ "weight": 1,
1157
1120
  },
1158
1121
  ],
1159
- "type": "multisig",
1122
+ "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
1123
+ "threshold": 1,
1124
+ "version": 0n,
1160
1125
  }
1161
- `,
1162
- )
1163
- })
1164
-
1165
- test('behavior: accepts an explicit account for a current config', () => {
1166
- const envelope = SignatureEnvelope.from({
1167
- account: '0x2222222222222222222222222222222222222222',
1168
- config: { ...config, version: 1 },
1169
- signatures: [SignatureEnvelope.from(signature_secp256k1)],
1170
- })
1171
-
1172
- expect(envelope.account).toBe(
1173
- '0x2222222222222222222222222222222222222222',
1174
- )
1175
- expect(envelope.config.version).toBe(1n)
1176
- })
1177
-
1178
- test('error: requires an account for a current config', () => {
1179
- expect(() =>
1180
- SignatureEnvelope.from({
1181
- config: { ...config, version: 1n },
1182
- signatures: [SignatureEnvelope.from(signature_secp256k1)],
1183
- } as never),
1184
- ).toThrowErrorMatchingInlineSnapshot(
1185
- `[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
1186
- )
1126
+ `)
1187
1127
  })
1188
1128
  })
1189
1129
  })
@@ -1787,6 +1727,8 @@ describe('serialize', () => {
1787
1727
  })
1788
1728
 
1789
1729
  describe('sortMultisigApprovals', () => {
1730
+ // Build owner key pairs first so we can construct a real genesis config
1731
+ // whose owner set matches the keys that produce the approvals below.
1790
1732
  const ownerKeys = Array.from({ length: 3 }, () => {
1791
1733
  const privateKey = Secp256k1.randomPrivateKey()
1792
1734
  const address = Address.fromPublicKey(
@@ -1798,16 +1740,19 @@ describe('sortMultisigApprovals', () => {
1798
1740
  Hex.toBigInt(a.address) < Hex.toBigInt(b.address) ? -1 : 1,
1799
1741
  )
1800
1742
 
1801
- const config = MultisigConfig.from({
1802
- owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
1743
+ const genesisConfig = MultisigConfig.from({
1803
1744
  threshold: 2,
1745
+ owners: ascendingOwners.map((o) => ({ owner: o.address, weight: 1 })),
1804
1746
  })
1805
- const account = MultisigConfig.getAddress(config)
1806
1747
  const payload = `0x${'42'.repeat(32)}` as const
1748
+ const account = MultisigConfig.getAddress(genesisConfig, {
1749
+ factory: '0x7171717171717171717171717171717171717171',
1750
+ })
1751
+ const version = 3n
1807
1752
  const digest = MultisigConfig.getSignPayload({
1808
- account,
1809
- config,
1810
1753
  payload,
1754
+ account,
1755
+ config: { version },
1811
1756
  })
1812
1757
 
1813
1758
  const owners = ownerKeys.map((owner) => ({
@@ -1824,7 +1769,7 @@ describe('sortMultisigApprovals', () => {
1824
1769
  test('behavior: orders approvals ascending by recovered owner address', () => {
1825
1770
  const ordered = SignatureEnvelope.sortMultisigApprovals({
1826
1771
  account,
1827
- config,
1772
+ config: { version },
1828
1773
  payload,
1829
1774
  // Provide approvals in reverse of the canonical order.
1830
1775
  signatures: [...ascending].reverse().map((owner) => owner.signature),
@@ -1837,7 +1782,7 @@ describe('sortMultisigApprovals', () => {
1837
1782
  expect(
1838
1783
  SignatureEnvelope.sortMultisigApprovals({
1839
1784
  account,
1840
- config,
1785
+ config: { version },
1841
1786
  payload,
1842
1787
  signatures,
1843
1788
  }),
@@ -1847,7 +1792,7 @@ describe('sortMultisigApprovals', () => {
1847
1792
  test('behavior: recovered order matches the config owner order', () => {
1848
1793
  const ordered = SignatureEnvelope.sortMultisigApprovals({
1849
1794
  account,
1850
- config,
1795
+ config: { version },
1851
1796
  payload,
1852
1797
  signatures: owners.map((owner) => owner.signature),
1853
1798
  })
@@ -2471,72 +2416,6 @@ describe('fromRpc', () => {
2471
2416
  expect(envelope.keyId).toBe('0xbe95c3f554e9fc85ec51be69a3d807a0d55bcf2c')
2472
2417
  })
2473
2418
  })
2474
-
2475
- describe('multisig', () => {
2476
- test('behavior: converts an untagged serialized multisig signature', () => {
2477
- expect(
2478
- SignatureEnvelope.fromRpc(signature_multisig_rpc),
2479
- ).toMatchInlineSnapshot(`
2480
- {
2481
- "account": "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
2482
- "config": {
2483
- "owners": [
2484
- {
2485
- "owner": "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
2486
- "weight": 1,
2487
- },
2488
- ],
2489
- "salt": "0x0000000000000000000000000000000000000000000000000000000000000000",
2490
- "threshold": 1,
2491
- "version": 0n,
2492
- },
2493
- "signatures": [
2494
- {
2495
- "signature": {
2496
- "r": 60871800714128149016591846789173983752390955456021923556141688418770269698437n,
2497
- "s": 24367763726302312398084372528434045669788111037623467391175388828437596076276n,
2498
- "yParity": 0,
2499
- },
2500
- "type": "secp256k1",
2501
- },
2502
- ],
2503
- "type": "multisig",
2504
- }
2505
- `)
2506
- })
2507
-
2508
- test('error: rejects a payload with the transaction type byte', () => {
2509
- expect(() =>
2510
- SignatureEnvelope.fromRpc(`0x05${signature_multisig_rpc.slice(2)}`),
2511
- ).toThrowErrorMatchingInlineSnapshot(`
2512
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: invalid multisig wire shape: expected exactly three fields
2513
-
2514
- Serialized: 0x0505f89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b]
2515
- `)
2516
- })
2517
-
2518
- test('error: rejects trailing bytes', () => {
2519
- expect(() =>
2520
- SignatureEnvelope.fromRpc(`${signature_multisig_rpc}00`),
2521
- ).toThrowErrorMatchingInlineSnapshot(`
2522
- [SignatureEnvelope.InvalidSerializedError: Unable to deserialize signature envelope: invalid multisig RPC signature
2523
-
2524
- Serialized: 0xf89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b00]
2525
- `)
2526
- })
2527
-
2528
- test('error: rejects a structured multisig signature', () => {
2529
- expect(() =>
2530
- SignatureEnvelope.fromRpc({
2531
- account: '0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b',
2532
- config: {},
2533
- signatures: [],
2534
- } as never),
2535
- ).toThrowErrorMatchingInlineSnapshot(
2536
- `[SignatureEnvelope.CoercionError: Unable to coerce value (\`{"account":"0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b","config":{},"signatures":[]}\`) to a valid signature envelope.]`,
2537
- )
2538
- })
2539
- })
2540
2419
  })
2541
2420
 
2542
2421
  describe('toRpc', () => {
@@ -2634,10 +2513,11 @@ describe('toRpc', () => {
2634
2513
  const rpc = SignatureEnvelope.toRpc(
2635
2514
  signature_keychain_p256,
2636
2515
  ) as SignatureEnvelope.KeychainRpc
2637
- if (typeof rpc.signature === 'string') throw new Error('unreachable')
2638
2516
 
2639
2517
  expect(rpc.type).toBe('keychain')
2640
2518
  expect(rpc.userAddress).toBe(signature_keychain_p256.userAddress)
2519
+ if (typeof rpc.signature === 'string')
2520
+ throw new Error('Expected structured primitive signature')
2641
2521
  expect(rpc.signature.type).toBe('p256')
2642
2522
  expect(typeof rpc.signature.pubKeyX).toBe('string')
2643
2523
  expect(typeof rpc.signature.pubKeyY).toBe('string')
@@ -2647,10 +2527,11 @@ describe('toRpc', () => {
2647
2527
  const rpc = SignatureEnvelope.toRpc(
2648
2528
  signature_keychain_webauthn,
2649
2529
  ) as SignatureEnvelope.KeychainRpc
2650
- if (typeof rpc.signature === 'string') throw new Error('unreachable')
2651
2530
 
2652
2531
  expect(rpc.type).toBe('keychain')
2653
2532
  expect(rpc.userAddress).toBe(signature_keychain_webauthn.userAddress)
2533
+ if (typeof rpc.signature === 'string')
2534
+ throw new Error('Expected structured primitive signature')
2654
2535
  expect(rpc.signature.type).toBe('webAuthn')
2655
2536
  expect(typeof rpc.signature.pubKeyX).toBe('string')
2656
2537
  expect(typeof rpc.signature.webauthnData).toBe('string')
@@ -2691,18 +2572,6 @@ describe('toRpc', () => {
2691
2572
  expect(rpc.keyId).toBeUndefined()
2692
2573
  })
2693
2574
  })
2694
-
2695
- describe('multisig', () => {
2696
- test('behavior: removes the transaction type byte', () => {
2697
- const envelope = SignatureEnvelope.deserialize(
2698
- `0x05${signature_multisig_rpc.slice(2)}`,
2699
- )
2700
-
2701
- expect(SignatureEnvelope.toRpc(envelope)).toMatchInlineSnapshot(
2702
- `"${signature_multisig_rpc}"`,
2703
- )
2704
- })
2705
- })
2706
2575
  })
2707
2576
 
2708
2577
  describe('roundtrip: toRpc <-> fromRpc', () => {
@@ -2936,12 +2805,278 @@ describe('CoercionError', () => {
2936
2805
  })
2937
2806
 
2938
2807
  describe('multisig', () => {
2808
+ const account = '0x2222222222222222222222222222222222222222'
2809
+ const config = MultisigConfig.from({
2810
+ threshold: 1,
2811
+ owners: [
2812
+ { owner: '0x1111111111111111111111111111111111111111', weight: 1 },
2813
+ ],
2814
+ })
2815
+ const primitive = SignatureEnvelope.from(signature_secp256k1)
2816
+ const envelope = SignatureEnvelope.from({
2817
+ account,
2818
+ config,
2819
+ signatures: [primitive],
2820
+ })
2821
+
2822
+ test('serialize carries account, config, and primitive approvals', () => {
2823
+ const serialized = SignatureEnvelope.serialize(envelope)
2824
+ expect(Hex.slice(serialized, 0, 1)).toMatchInlineSnapshot('"0x05"')
2825
+ expect(Rlp.toHex(Hex.slice(serialized, 1))).toEqual([
2826
+ account,
2827
+ [
2828
+ MultisigConfig.zeroSalt,
2829
+ '0x',
2830
+ '0x01',
2831
+ [['0x1111111111111111111111111111111111111111', '0x01']],
2832
+ ],
2833
+ [SignatureEnvelope.serialize(primitive)],
2834
+ ])
2835
+ expect(SignatureEnvelope.deserialize(serialized)).toEqual(envelope)
2836
+ })
2837
+
2838
+ test('RPC contains RLP bytes without the type byte', () => {
2839
+ const rpc = SignatureEnvelope.toRpc(envelope)
2840
+ expect(rpc).toBe(Hex.slice(SignatureEnvelope.serialize(envelope), 1))
2841
+ expect(SignatureEnvelope.fromRpc(rpc)).toEqual(envelope)
2842
+ })
2843
+
2844
+ test('round-trips updated configs and maximum versions', () => {
2845
+ for (const version of [1n, 0xffffffffffffffffn]) {
2846
+ const updated = SignatureEnvelope.from({
2847
+ ...envelope,
2848
+ config: { ...config, config: { version } },
2849
+ })
2850
+ expect(
2851
+ SignatureEnvelope.deserialize(SignatureEnvelope.serialize(updated)),
2852
+ ).toEqual(updated)
2853
+ expect(
2854
+ SignatureEnvelope.fromRpc(SignatureEnvelope.toRpc(updated)),
2855
+ ).toEqual(updated)
2856
+ }
2857
+ })
2858
+
2859
+ test('extractAddress returns the named account', () => {
2860
+ expect(
2861
+ SignatureEnvelope.extractAddress({
2862
+ signature: envelope,
2863
+ payload: '0x00',
2864
+ }),
2865
+ ).toMatchInlineSnapshot('"0x2222222222222222222222222222222222222222"')
2866
+ })
2867
+
2868
+ test('rejects old wire and RPC shapes', () => {
2869
+ for (const first of [account, MultisigConfig.toTuple(config)] as const)
2870
+ expect(() =>
2871
+ SignatureEnvelope.deserialize(
2872
+ Hex.concat(
2873
+ '0x05',
2874
+ Rlp.fromHex([first, [SignatureEnvelope.serialize(primitive)]]),
2875
+ ),
2876
+ ),
2877
+ ).toThrowError(SignatureEnvelope.InvalidSerializedError)
2878
+ expect(() =>
2879
+ SignatureEnvelope.fromRpc({ account, signatures: [] } as never),
2880
+ ).toThrowError(SignatureEnvelope.CoercionError)
2881
+ })
2882
+
2883
+ test('rejects malformed configs', () => {
2884
+ const valid = MultisigConfig.toTuple(config)
2885
+ for (const tuple of [
2886
+ ['0x00', ...valid.slice(1)],
2887
+ [valid[0], '0x00', valid[2], valid[3]],
2888
+ [valid[0], '0x010000000000000000', valid[2], valid[3]],
2889
+ [valid[0], '0x', '0x0001', valid[3]],
2890
+ [valid[0], '0x', '0x01', [[config.owners[0]!.owner, '0x0001']]],
2891
+ [valid[0], '0x', '0x01', []],
2892
+ ])
2893
+ expect(() =>
2894
+ SignatureEnvelope.deserialize(
2895
+ Hex.concat(
2896
+ '0x05',
2897
+ Rlp.fromHex([
2898
+ account,
2899
+ tuple as never,
2900
+ [SignatureEnvelope.serialize(primitive)],
2901
+ ]),
2902
+ ),
2903
+ ),
2904
+ ).toThrowError(MultisigConfig.InvalidConfigError)
2905
+ })
2906
+
2907
+ test('rejects invalid accounts, missing configs, and invalid approval counts', () => {
2908
+ for (const value of [
2909
+ { ...envelope, account: '0x11' },
2910
+ { ...envelope, account: '0x0000000000000000000000000000000000000000' },
2911
+ { ...envelope, config: undefined },
2912
+ { ...envelope, signatures: [] },
2913
+ { ...envelope, signatures: Array(9).fill(primitive) },
2914
+ ])
2915
+ expect(() => SignatureEnvelope.assert(value as never)).toThrowError()
2916
+ expect(() =>
2917
+ SignatureEnvelope.assert({
2918
+ ...envelope,
2919
+ signatures: Array(8).fill(primitive),
2920
+ }),
2921
+ ).not.toThrow()
2922
+ })
2923
+
2924
+ test('rejects self-ownership in the initial configuration', () => {
2925
+ const self = {
2926
+ ...envelope,
2927
+ config: { ...config, owners: [{ owner: account, weight: 1 }] },
2928
+ } as const
2929
+ expect(() => SignatureEnvelope.assert(self)).toThrowError(
2930
+ SignatureEnvelope.InvalidMultisigApprovalError,
2931
+ )
2932
+ expect(() =>
2933
+ SignatureEnvelope.assert({
2934
+ ...self,
2935
+ config: { ...self.config, version: 1n },
2936
+ }),
2937
+ ).not.toThrow()
2938
+ })
2939
+
2940
+ test('rejects recursive owner approvals before decoding them', () => {
2941
+ for (const signature of [envelope, signature_keychain_secp256k1]) {
2942
+ expect(() =>
2943
+ SignatureEnvelope.serialize({
2944
+ ...envelope,
2945
+ signatures: [signature],
2946
+ } as never),
2947
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
2948
+ const wire = Hex.concat(
2949
+ '0x05',
2950
+ Rlp.fromHex([
2951
+ account,
2952
+ MultisigConfig.toTuple(config),
2953
+ [SignatureEnvelope.serialize(signature)],
2954
+ ]),
2955
+ )
2956
+ expect(() => SignatureEnvelope.deserialize(wire)).toThrowError(
2957
+ SignatureEnvelope.InvalidSerializedError,
2958
+ )
2959
+ }
2960
+ })
2961
+
2962
+ test('rejects oversized owner approvals', () => {
2963
+ const oversized = {
2964
+ ...signature_webauthn,
2965
+ metadata: {
2966
+ ...signature_webauthn.metadata,
2967
+ clientDataJSON: 'x'.repeat(2048),
2968
+ },
2969
+ }
2970
+ expect(() =>
2971
+ SignatureEnvelope.assert({ ...envelope, signatures: [oversized] }),
2972
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
2973
+ })
2974
+ test('requires keychain V2 for multisig delegates', () => {
2975
+ const keychain = SignatureEnvelope.from({
2976
+ userAddress: account,
2977
+ version: 'v2',
2978
+ inner: envelope,
2979
+ })
2980
+ const serialized = SignatureEnvelope.serialize(keychain)
2981
+ expect(SignatureEnvelope.deserialize(serialized)).toEqual(keychain)
2982
+ expect(() =>
2983
+ SignatureEnvelope.serialize({ ...keychain, version: 'v1' }),
2984
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
2985
+ expect(() =>
2986
+ SignatureEnvelope.deserialize(
2987
+ Hex.concat('0x03', Hex.slice(serialized, 1)),
2988
+ ),
2989
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
2990
+ expect(() =>
2991
+ SignatureEnvelope.fromRpc({
2992
+ type: 'keychain',
2993
+ userAddress: account,
2994
+ version: 'v1',
2995
+ signature: SignatureEnvelope.toRpc(envelope),
2996
+ }),
2997
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
2998
+ })
2999
+ test('rejects V1 wrappers around recursively nested multisig delegates', () => {
3000
+ let inner: SignatureEnvelope.SignatureEnvelope = envelope
3001
+ for (let depth = 0; depth < 3; depth++) {
3002
+ inner = { inner, type: 'keychain', userAddress: account, version: 'v2' }
3003
+ const valid: SignatureEnvelope.Keychain = {
3004
+ inner,
3005
+ type: 'keychain',
3006
+ userAddress: account,
3007
+ version: 'v2',
3008
+ }
3009
+ const invalid = { ...valid, version: 'v1' } as const
3010
+ expect(SignatureEnvelope.validate(valid)).toBe(true)
3011
+ expect(SignatureEnvelope.validate(invalid)).toBe(false)
3012
+ expect(() => SignatureEnvelope.serialize(invalid)).toThrowError(
3013
+ SignatureEnvelope.InvalidMultisigApprovalError,
3014
+ )
3015
+ expect(() => SignatureEnvelope.toRpc(invalid)).toThrowError(
3016
+ SignatureEnvelope.InvalidMultisigApprovalError,
3017
+ )
3018
+ expect(() =>
3019
+ SignatureEnvelope.fromRpc({
3020
+ type: 'keychain',
3021
+ userAddress: account,
3022
+ version: 'v1',
3023
+ signature: SignatureEnvelope.toRpc(inner),
3024
+ }),
3025
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
3026
+ expect(() =>
3027
+ SignatureEnvelope.deserialize(
3028
+ Hex.concat('0x03', account, SignatureEnvelope.serialize(inner)),
3029
+ ),
3030
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
3031
+ expect(() =>
3032
+ SignatureEnvelope.serialize({ ...valid, inner: invalid }),
3033
+ ).toThrowError(SignatureEnvelope.InvalidMultisigApprovalError)
3034
+ }
3035
+ expect(
3036
+ SignatureEnvelope.validate({
3037
+ inner: {
3038
+ inner: primitive,
3039
+ type: 'keychain',
3040
+ userAddress: account,
3041
+ version: 'v2',
3042
+ },
3043
+ type: 'keychain',
3044
+ userAddress: account,
3045
+ version: 'v1',
3046
+ }),
3047
+ ).toBe(true)
3048
+ })
3049
+ test('RPC rejects 64-byte values that resemble primitive signatures', () => {
3050
+ expect(() =>
3051
+ SignatureEnvelope.fromRpc(
3052
+ Hex.concat(Hex.fromNumber(1, { size: 63 }), '0x1b'),
3053
+ ),
3054
+ ).toThrowError()
3055
+ })
3056
+ test('round-trips mixed primitive approvals', () => {
3057
+ const signature = { r: p256Signature.r, s: p256Signature.s }
3058
+ const mixed = SignatureEnvelope.from({
3059
+ ...envelope,
3060
+ signatures: [
3061
+ primitive,
3062
+ { ...signature_p256, signature },
3063
+ { ...signature_webauthn, signature },
3064
+ ],
3065
+ })
3066
+ expect(
3067
+ SignatureEnvelope.deserialize(SignatureEnvelope.serialize(mixed)),
3068
+ ).toEqual(mixed)
3069
+ expect(SignatureEnvelope.fromRpc(SignatureEnvelope.toRpc(mixed))).toEqual(
3070
+ mixed,
3071
+ )
3072
+ })
3073
+ })
3074
+
3075
+ describe('multisig vectors', () => {
2939
3076
  const initial =
2940
3077
  '0x05f89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b' as const
2941
3078
  const current =
2942
3079
  '0x05f89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b' as const
2943
- const nested =
2944
- '0x05f8f0945f0287385ec182f906c238bec5f87692d48b23f2f83ba033333333333333333333333333333333333333333333333333333333333333338001d7d69470845f2897b639686cb383799d605c52315d785501f89cb89a05f8979470845f2897b639686cb383799d605c52315d7855f83ba022222222222222222222222222222222222222222222222222222222222222228001d7d6946813eb9362372eef6200f3b1dbc3f819671cba6901f843b841032aa6f3ea7b0b7069720d0f3891983c493d149326c5c957d864ed7371b8475e3e95325079b24491f4b6d68920e4b3bacd7c6094df6ed88b8674864ab559c8fb1b' as const
2945
3080
 
2946
3081
  test('example: decodes and re-encodes the frozen initial signature', () => {
2947
3082
  const envelope = SignatureEnvelope.deserialize(initial)
@@ -3009,56 +3144,6 @@ describe('multisig', () => {
3009
3144
  expect(SignatureEnvelope.serialize(envelope)).toBe(current)
3010
3145
  })
3011
3146
 
3012
- test('example: decodes and re-encodes the frozen nested signature', () => {
3013
- const envelope = SignatureEnvelope.deserialize(nested)
3014
-
3015
- expect(envelope).toMatchInlineSnapshot(`
3016
- {
3017
- "account": "0x5f0287385ec182f906c238bec5f87692d48b23f2",
3018
- "config": {
3019
- "owners": [
3020
- {
3021
- "owner": "0x70845f2897b639686cb383799d605c52315d7855",
3022
- "weight": 1,
3023
- },
3024
- ],
3025
- "salt": "0x3333333333333333333333333333333333333333333333333333333333333333",
3026
- "threshold": 1,
3027
- "version": 0n,
3028
- },
3029
- "signatures": [
3030
- {
3031
- "account": "0x70845f2897b639686cb383799d605c52315d7855",
3032
- "config": {
3033
- "owners": [
3034
- {
3035
- "owner": "0x6813eb9362372eef6200f3b1dbc3f819671cba69",
3036
- "weight": 1,
3037
- },
3038
- ],
3039
- "salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
3040
- "threshold": 1,
3041
- "version": 0n,
3042
- },
3043
- "signatures": [
3044
- {
3045
- "signature": {
3046
- "r": 1432298388324792555676061659976215111517531547814803085200940835913455912798n,
3047
- "s": 28307004081743690677157914067925034813425317008577473150162358055484664695035n,
3048
- "yParity": 0,
3049
- },
3050
- "type": "secp256k1",
3051
- },
3052
- ],
3053
- "type": "multisig",
3054
- },
3055
- ],
3056
- "type": "multisig",
3057
- }
3058
- `)
3059
- expect(SignatureEnvelope.serialize(envelope)).toBe(nested)
3060
- })
3061
-
3062
3147
  test('behavior: preserves exact 65-byte secp256k1 precedence', () => {
3063
3148
  const serialized = ('0x05' +
3064
3149
  '00'.repeat(31) +
@@ -3069,356 +3154,4 @@ describe('multisig', () => {
3069
3154
  expect(Hex.size(serialized)).toBe(65)
3070
3155
  expect(SignatureEnvelope.deserialize(serialized).type).toBe('secp256k1')
3071
3156
  })
3072
-
3073
- test('behavior: serializes account, config, and approvals in wire order', () => {
3074
- const envelope = SignatureEnvelope.deserialize(initial)
3075
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3076
- const serialized = SignatureEnvelope.serialize(envelope)
3077
- const decoded = Rlp.toHex(Hex.slice(serialized, 1))
3078
-
3079
- expect(decoded).toMatchInlineSnapshot(`
3080
- [
3081
- "0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b",
3082
- [
3083
- "0x0000000000000000000000000000000000000000000000000000000000000000",
3084
- "0x",
3085
- "0x01",
3086
- [
3087
- [
3088
- "0x7e5f4552091a69125d5dfcb7b8c2659029395bdf",
3089
- "0x01",
3090
- ],
3091
- ],
3092
- ],
3093
- [
3094
- "0x869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
3095
- ],
3096
- ]
3097
- `)
3098
- })
3099
-
3100
- test('behavior: round trips initial and current RPC signatures', () => {
3101
- const initialEnvelope = SignatureEnvelope.deserialize(initial)
3102
- const currentEnvelope = SignatureEnvelope.deserialize(current)
3103
- const rpc = {
3104
- current: SignatureEnvelope.toRpc(currentEnvelope),
3105
- initial: SignatureEnvelope.toRpc(initialEnvelope),
3106
- }
3107
-
3108
- expect(rpc).toMatchInlineSnapshot(`
3109
- {
3110
- "current": "0xf89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000000101d7d6942b5ad5c4795c026514f8317c7a215e218dccd6cf01f843b8414a0e5b5b4a90f08e6e8d676a73a22dc2cf022ffdcc9299512b5d9a6daf66e0504a395a2ef6f6c0f173910b875c45d01aa66d928e56ba6f49bbc8186f80268bf51b",
3111
- "initial": "0xf89794c4a590afa7337e5cd5eb3aa60cacf91c5400044bf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d6947e5f4552091a69125d5dfcb7b8c2659029395bdf01f843b841869437e01f64bebeb78a8a6b30bfd3a993819c8cad82c807515d9b9e9b36f98535dfaa5eebc597715d05f6ce4927747f14fa4cd2acc717fdcd3877146437f8f41b",
3112
- }
3113
- `)
3114
- expect(SignatureEnvelope.fromRpc(rpc.initial)).toStrictEqual(
3115
- initialEnvelope,
3116
- )
3117
- expect(SignatureEnvelope.fromRpc(rpc.current)).toStrictEqual(
3118
- currentEnvelope,
3119
- )
3120
- })
3121
-
3122
- test('behavior: derives the account only for an initial config', () => {
3123
- const initialEnvelope = SignatureEnvelope.deserialize(initial)
3124
- if (initialEnvelope.type !== 'multisig') throw new Error('unreachable')
3125
- if (initialEnvelope.config.version !== 0n) throw new Error('unreachable')
3126
- const derived = SignatureEnvelope.from({
3127
- config: initialEnvelope.config as MultisigConfig.Config<0n>,
3128
- signatures: initialEnvelope.signatures,
3129
- })
3130
- const currentEnvelope = SignatureEnvelope.deserialize(current)
3131
- if (currentEnvelope.type !== 'multisig') throw new Error('unreachable')
3132
-
3133
- expect(derived.account).toBe(initialEnvelope.account)
3134
- expect(() =>
3135
- SignatureEnvelope.from({
3136
- config: currentEnvelope.config,
3137
- signatures: currentEnvelope.signatures,
3138
- } as never),
3139
- ).toThrowErrorMatchingInlineSnapshot(
3140
- `[MultisigConfig.InvalidConfigError: Invalid native multisig config: account address requires version zero.]`,
3141
- )
3142
- })
3143
-
3144
- test('behavior: extracts the multisig account', () => {
3145
- const envelope = SignatureEnvelope.deserialize(initial)
3146
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3147
-
3148
- expect(
3149
- SignatureEnvelope.extractAddress({
3150
- payload: '0xdeadbeef',
3151
- signature: envelope,
3152
- }),
3153
- ).toBe(envelope.account)
3154
- })
3155
-
3156
- test('example: matches the frozen eight-approval boundary vector', () => {
3157
- const config = MultisigConfig.from({
3158
- owners: Array.from({ length: 8 }, (_, index) => ({
3159
- owner:
3160
- `0x${(index + 1).toString(16).padStart(40, '0')}` as `0x${string}`,
3161
- weight: 1,
3162
- })),
3163
- salt: `0x${'66'.repeat(32)}`,
3164
- threshold: 8,
3165
- })
3166
- const approval = SignatureEnvelope.from({
3167
- r: 0x840cfc572845f5786e702984c2a582528cad4b49b2a10b9db1be7fca90058565n,
3168
- s: 0x25e7109ceb98168d95b09b18bbf6b685130e0562f233877d492b94eee0c5b6d1n,
3169
- yParity: 0,
3170
- })
3171
- const envelope = SignatureEnvelope.from({
3172
- config,
3173
- signatures: Array.from(
3174
- { length: MultisigConfig.maxSignatures },
3175
- () => approval,
3176
- ),
3177
- })
3178
- const serialized = SignatureEnvelope.serialize(envelope)
3179
-
3180
- expect({
3181
- hash: Hash.keccak256(serialized),
3182
- length: Hex.size(serialized),
3183
- signatureCount: envelope.signatures.length,
3184
- }).toMatchInlineSnapshot(`
3185
- {
3186
- "hash": "0x507fa2d2bc6cf5e7753d4d7cb92fe3d199f64c6eaa9a0bd1d14bfb366028a379",
3187
- "length": 787,
3188
- "signatureCount": 8,
3189
- }
3190
- `)
3191
- })
3192
-
3193
- test('behavior: accepts initial and current nested signatures', () => {
3194
- const initialEnvelope = SignatureEnvelope.deserialize(initial)
3195
- const currentEnvelope = SignatureEnvelope.deserialize(current)
3196
- if (
3197
- initialEnvelope.type !== 'multisig' ||
3198
- currentEnvelope.type !== 'multisig'
3199
- )
3200
- throw new Error('unreachable')
3201
-
3202
- for (const signature of [initialEnvelope, currentEnvelope]) {
3203
- const config = MultisigConfig.from({
3204
- owners: [{ owner: signature.account, weight: 1 }],
3205
- salt: ('0x' + 'ff'.repeat(32)) as Hex.Hex,
3206
- threshold: 1,
3207
- version: 1n,
3208
- })
3209
- const envelope = SignatureEnvelope.from({
3210
- account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
3211
- config,
3212
- signatures: [signature],
3213
- })
3214
-
3215
- expect(
3216
- SignatureEnvelope.deserialize(SignatureEnvelope.serialize(envelope)),
3217
- ).toStrictEqual(envelope)
3218
- }
3219
- })
3220
-
3221
- test('error: rejects an initial account mismatch', () => {
3222
- const envelope = SignatureEnvelope.deserialize(initial)
3223
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3224
-
3225
- expect(() =>
3226
- SignatureEnvelope.assert({
3227
- ...envelope,
3228
- account: '0x1111111111111111111111111111111111111111',
3229
- }),
3230
- ).toThrowErrorMatchingInlineSnapshot(
3231
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: initial multisig config does not derive account.]`,
3232
- )
3233
- })
3234
-
3235
- test('error: rejects a self-owned current signature', () => {
3236
- const envelope = SignatureEnvelope.deserialize(current)
3237
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3238
- const account = envelope.config.owners[0]!.owner
3239
-
3240
- expect(() =>
3241
- SignatureEnvelope.assert({ ...envelope, account }),
3242
- ).toThrowErrorMatchingInlineSnapshot(
3243
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig account cannot be an owner.]`,
3244
- )
3245
- })
3246
-
3247
- test('error: rejects empty and excess approvals', () => {
3248
- const envelope = SignatureEnvelope.deserialize(current)
3249
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3250
-
3251
- expect(() =>
3252
- SignatureEnvelope.assert({ ...envelope, signatures: [] }),
3253
- ).toThrowErrorMatchingInlineSnapshot(
3254
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures cannot be empty.]`,
3255
- )
3256
- expect(() =>
3257
- SignatureEnvelope.assert({
3258
- ...envelope,
3259
- signatures: Array.from(
3260
- { length: MultisigConfig.maxSignatures + 1 },
3261
- () => envelope.signatures[0]!,
3262
- ),
3263
- }),
3264
- ).toThrowErrorMatchingInlineSnapshot(
3265
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig signatures exceed 8.]`,
3266
- )
3267
- })
3268
-
3269
- test('error: rejects keychain owner approvals', () => {
3270
- const envelope = SignatureEnvelope.deserialize(current)
3271
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3272
-
3273
- expect(() =>
3274
- SignatureEnvelope.assert({
3275
- ...envelope,
3276
- signatures: [signature_keychain_secp256k1],
3277
- } as never),
3278
- ).toThrowErrorMatchingInlineSnapshot(
3279
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: keychain owner approvals are not allowed.]`,
3280
- )
3281
- })
3282
-
3283
- test('error: rejects oversized primitive approvals', () => {
3284
- const envelope = SignatureEnvelope.deserialize(current)
3285
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3286
- const oversized = {
3287
- ...signature_webauthn,
3288
- metadata: {
3289
- ...signature_webauthn.metadata,
3290
- clientDataJSON: JSON.stringify({ value: 'x'.repeat(2048) }),
3291
- },
3292
- }
3293
-
3294
- expect(() =>
3295
- SignatureEnvelope.assert({
3296
- ...envelope,
3297
- signatures: [oversized],
3298
- }),
3299
- ).toThrowErrorMatchingInlineSnapshot(
3300
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig owner signature exceeds 2049 bytes.]`,
3301
- )
3302
- })
3303
-
3304
- test('error: rejects nesting deeper than two nodes', () => {
3305
- const envelope = SignatureEnvelope.deserialize(initial)
3306
- if (envelope.type !== 'multisig') throw new Error('unreachable')
3307
- const depth2 = SignatureEnvelope.from({
3308
- ...envelope,
3309
- signatures: [envelope],
3310
- })
3311
-
3312
- expect(() =>
3313
- SignatureEnvelope.assert({
3314
- ...envelope,
3315
- signatures: [depth2],
3316
- }),
3317
- ).toThrowErrorMatchingInlineSnapshot(
3318
- `[SignatureEnvelope.InvalidMultisigApprovalError: Invalid native multisig owner approval: multisig nesting depth exceeds 2.]`,
3319
- )
3320
- })
3321
-
3322
- test.each([
3323
- {
3324
- name: 'two fields',
3325
- value: Rlp.fromHex([
3326
- '0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b',
3327
- [Hex.slice(initial, -65)],
3328
- ]),
3329
- },
3330
- {
3331
- name: 'trailing field',
3332
- value: Rlp.fromHex([
3333
- '0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b',
3334
- MultisigConfig.toTuple(
3335
- MultisigConfig.from({
3336
- owners: [
3337
- {
3338
- owner: '0x7e5f4552091a69125d5dfcb7b8c2659029395bdf',
3339
- weight: 1,
3340
- },
3341
- ],
3342
- threshold: 1,
3343
- }),
3344
- ),
3345
- [Hex.slice(initial, -65)],
3346
- '0x',
3347
- ]),
3348
- },
3349
- ])('error: rejects malformed $name wire shape', ({ value }) => {
3350
- expect(() =>
3351
- SignatureEnvelope.deserialize(Hex.concat('0x05', value)),
3352
- ).toThrowError(SignatureEnvelope.InvalidSerializedError)
3353
- })
3354
-
3355
- test.each([
3356
- {
3357
- field: 'salt',
3358
- value: [
3359
- ('0x' + '00'.repeat(31)) as Hex.Hex,
3360
- '0x',
3361
- '0x01',
3362
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
3363
- ],
3364
- },
3365
- {
3366
- field: 'oversized version',
3367
- value: [
3368
- ('0x' + '00'.repeat(32)) as Hex.Hex,
3369
- ('0x' + '01'.repeat(9)) as Hex.Hex,
3370
- '0x01',
3371
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
3372
- ],
3373
- },
3374
- {
3375
- field: 'zero version',
3376
- value: [
3377
- ('0x' + '00'.repeat(32)) as Hex.Hex,
3378
- '0x00',
3379
- '0x01',
3380
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
3381
- ],
3382
- },
3383
- {
3384
- field: 'zero-prefixed version',
3385
- value: [
3386
- ('0x' + '00'.repeat(32)) as Hex.Hex,
3387
- '0x0001',
3388
- '0x01',
3389
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
3390
- ],
3391
- },
3392
- {
3393
- field: 'threshold',
3394
- value: [
3395
- ('0x' + '00'.repeat(32)) as Hex.Hex,
3396
- '0x',
3397
- '0x0001',
3398
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x01']],
3399
- ],
3400
- },
3401
- {
3402
- field: 'weight',
3403
- value: [
3404
- ('0x' + '00'.repeat(32)) as Hex.Hex,
3405
- '0x',
3406
- '0x01',
3407
- [['0x7e5f4552091a69125d5dfcb7b8c2659029395bdf', '0x0001']],
3408
- ],
3409
- },
3410
- ])('error: rejects a noncanonical $field', ({ value }) => {
3411
- const malformed = Hex.concat(
3412
- '0x05',
3413
- Rlp.fromHex([
3414
- '0xc4a590afa7337e5cd5eb3aa60cacf91c5400044b',
3415
- value as never,
3416
- [Hex.slice(initial, -65)],
3417
- ]),
3418
- )
3419
-
3420
- expect(() => SignatureEnvelope.deserialize(malformed)).toThrowError(
3421
- SignatureEnvelope.InvalidSerializedError,
3422
- )
3423
- })
3424
3157
  })