ox 1.7.4 → 1.7.5

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 (105) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/dist/core/AbiEvent.d.ts.map +1 -1
  3. package/dist/core/AbiEvent.js +10 -9
  4. package/dist/core/AbiEvent.js.map +1 -1
  5. package/dist/core/Blobs.d.ts.map +1 -1
  6. package/dist/core/Blobs.js +6 -2
  7. package/dist/core/Blobs.js.map +1 -1
  8. package/dist/core/Signature.d.ts +6 -5
  9. package/dist/core/Signature.d.ts.map +1 -1
  10. package/dist/core/Signature.js +17 -7
  11. package/dist/core/Signature.js.map +1 -1
  12. package/dist/core/Siwe.d.ts.map +1 -1
  13. package/dist/core/Siwe.js +21 -1
  14. package/dist/core/Siwe.js.map +1 -1
  15. package/dist/tempo/KeyAuthorization.d.ts +42 -38
  16. package/dist/tempo/KeyAuthorization.d.ts.map +1 -1
  17. package/dist/tempo/KeyAuthorization.js +24 -4
  18. package/dist/tempo/KeyAuthorization.js.map +1 -1
  19. package/dist/tempo/MultisigConfig.d.ts +18 -15
  20. package/dist/tempo/MultisigConfig.d.ts.map +1 -1
  21. package/dist/tempo/MultisigConfig.js +49 -20
  22. package/dist/tempo/MultisigConfig.js.map +1 -1
  23. package/dist/tempo/MultisigOperation.d.ts +3 -2
  24. package/dist/tempo/MultisigOperation.d.ts.map +1 -1
  25. package/dist/tempo/MultisigOperation.js +20 -100
  26. package/dist/tempo/MultisigOperation.js.map +1 -1
  27. package/dist/tempo/MultisigSimulation.d.ts +2 -49
  28. package/dist/tempo/MultisigSimulation.d.ts.map +1 -1
  29. package/dist/tempo/MultisigSimulation.js +18 -46
  30. package/dist/tempo/MultisigSimulation.js.map +1 -1
  31. package/dist/tempo/SignatureEnvelope.d.ts +72 -140
  32. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  33. package/dist/tempo/SignatureEnvelope.js +130 -181
  34. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  35. package/dist/tempo/TransactionRequest.d.ts +4 -2
  36. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  37. package/dist/tempo/TransactionRequest.js +6 -1
  38. package/dist/tempo/TransactionRequest.js.map +1 -1
  39. package/dist/tempo/index.d.ts +3 -1
  40. package/dist/tempo/index.d.ts.map +1 -1
  41. package/dist/tempo/index.js +3 -1
  42. package/dist/tempo/index.js.map +1 -1
  43. package/dist/zod/tempo/AuthorizationTempo.d.ts +350 -10
  44. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  45. package/dist/zod/tempo/KeyAuthorization.d.ts +242 -422
  46. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  47. package/dist/zod/tempo/KeyAuthorization.js +29 -31
  48. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  49. package/dist/zod/tempo/MultisigSimulation.d.ts +8 -76
  50. package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -1
  51. package/dist/zod/tempo/MultisigSimulation.js +3 -37
  52. package/dist/zod/tempo/MultisigSimulation.js.map +1 -1
  53. package/dist/zod/tempo/RpcSchemaTempo.d.ts +284 -228
  54. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  55. package/dist/zod/tempo/SignatureEnvelope.d.ts +105 -3
  56. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  57. package/dist/zod/tempo/SignatureEnvelope.js +21 -9
  58. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  59. package/dist/zod/tempo/Transaction.d.ts +676 -448
  60. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  61. package/dist/zod/tempo/TransactionRequest.d.ts +590 -473
  62. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  63. package/dist/zod/tempo/TransactionRequest.js +8 -0
  64. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  65. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +327 -213
  66. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  67. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +105 -3
  68. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  69. package/package.json +1 -1
  70. package/src/core/AbiEvent.ts +11 -9
  71. package/src/core/Blobs.ts +6 -2
  72. package/src/core/Signature.ts +41 -11
  73. package/src/core/Siwe.ts +20 -1
  74. package/src/core/_test/AbiEvent.test.ts +45 -0
  75. package/src/core/_test/Blobs.test.ts +22 -0
  76. package/src/core/_test/Signature.test.ts +166 -0
  77. package/src/core/_test/Siwe.test.ts +100 -1
  78. package/src/tempo/AuthorizationTempo.test.ts +8 -4
  79. package/src/tempo/KeyAuthorization.test-d.ts +16 -63
  80. package/src/tempo/KeyAuthorization.test.ts +69 -11
  81. package/src/tempo/KeyAuthorization.ts +107 -63
  82. package/src/tempo/MultisigConfig.test.ts +44 -20
  83. package/src/tempo/MultisigConfig.ts +65 -23
  84. package/src/tempo/MultisigOperation.test.ts +79 -550
  85. package/src/tempo/MultisigOperation.ts +31 -146
  86. package/src/tempo/MultisigSimulation.test-d.ts +2 -2
  87. package/src/tempo/MultisigSimulation.test.ts +21 -110
  88. package/src/tempo/MultisigSimulation.ts +25 -99
  89. package/src/tempo/SignatureEnvelope.test-d.ts +29 -47
  90. package/src/tempo/SignatureEnvelope.test.ts +328 -541
  91. package/src/tempo/SignatureEnvelope.ts +196 -291
  92. package/src/tempo/Transaction.test.ts +4 -4
  93. package/src/tempo/TransactionRequest.test.ts +17 -80
  94. package/src/tempo/TransactionRequest.ts +19 -2
  95. package/src/tempo/index.ts +3 -1
  96. package/src/tempo/multisig.e2e.test.ts +321 -860
  97. package/src/version.ts +1 -1
  98. package/src/zod/tempo/KeyAuthorization.ts +50 -39
  99. package/src/zod/tempo/MultisigSimulation.ts +3 -48
  100. package/src/zod/tempo/SignatureEnvelope.ts +46 -35
  101. package/src/zod/tempo/TransactionRequest.ts +14 -0
  102. package/src/zod/tempo/_test/KeyAuthorization.test.ts +69 -20
  103. package/src/zod/tempo/_test/MultisigSimulation.test.ts +6 -31
  104. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +118 -85
  105. package/src/zod/tempo/_test/TransactionRequest.test.ts +9 -19
@@ -142,7 +142,8 @@ export declare namespace getHash {
142
142
  *
143
143
  * The function retains one canonical approval per owner. It selects the
144
144
  * smallest deterministic quorum by owner weight, then orders the selected
145
- * approvals by owner address for serialization.
145
+ * approvals by owner address for serialization. The caller supplies the
146
+ * account and config; the chain validates their factory-derived identity.
146
147
  *
147
148
  * @example
148
149
  * ```ts twoslash
@@ -169,22 +170,12 @@ export async function selectApprovals(
169
170
  if (!Hash.validate(hash))
170
171
  throw new InvalidApprovalError({ reason: 'hash is invalid' })
171
172
  const config = MultisigConfig.from(options.config)
172
- if (
173
- config.version === 0n &&
174
- !Address.isEqual(MultisigConfig.getAddress(config), account)
175
- )
176
- throw new InvalidApprovalError({
177
- reason: 'initial config does not derive the root multisig account',
178
- })
179
- return selectApprovals_internal(
180
- {
181
- account,
182
- approvals,
183
- config,
184
- hash,
185
- },
186
- [account.toLowerCase()],
187
- )
173
+ return selectApprovals_internal({
174
+ account,
175
+ approvals,
176
+ config,
177
+ hash,
178
+ })
188
179
  }
189
180
 
190
181
  export declare namespace selectApprovals {
@@ -192,7 +183,7 @@ export declare namespace selectApprovals {
192
183
  export type Options = {
193
184
  /** Root multisig account. */
194
185
  account: Address.Address
195
- /** Serialized primitive or nested owner approvals. */
186
+ /** Serialized primitive owner approvals. */
196
187
  approvals: readonly SignatureEnvelope.Serialized[]
197
188
  /** Current root multisig configuration. */
198
189
  config: MultisigConfig.Config
@@ -271,7 +262,7 @@ export function serializeKeyAuthorization(
271
262
  config,
272
263
  payload: KeyAuthorization_.getSignPayload(authorization),
273
264
  signatures: options.approvals.map((approval) =>
274
- SignatureEnvelope.deserialize(approval),
265
+ assertApproval(options.account, approval, config),
275
266
  ),
276
267
  })
277
268
  return KeyAuthorization_.serialize(
@@ -339,7 +330,7 @@ export function serializeTransaction(
339
330
  value.transaction as TxEnvelopeTempo.Serialized,
340
331
  )
341
332
  const approvals = options.approvals.map((approval) =>
342
- SignatureEnvelope.from(approval),
333
+ assertApproval(value.account, approval, value.config),
343
334
  )
344
335
  assertRetainedApprovals(value, approvals)
345
336
  const signatures = SignatureEnvelope.sortMultisigApprovals({
@@ -524,7 +515,6 @@ export declare namespace toRpc {
524
515
  */
525
516
  async function selectApprovals_internal(
526
517
  options: selectApprovals.Options,
527
- path: readonly string[],
528
518
  ): Promise<selectApprovals.ReturnValue> {
529
519
  const owners = new Map(
530
520
  options.config.owners.map((owner) => [
@@ -535,17 +525,14 @@ async function selectApprovals_internal(
535
525
  const groups = new Map<string, ApprovalGroup>()
536
526
  for (const serialized of options.approvals) {
537
527
  const signature = SignatureEnvelope.from(serialized)
538
- if (signature.type === 'keychain')
528
+ if (signature.type === 'keychain' || signature.type === 'multisig')
539
529
  throw new InvalidApprovalError({
540
- reason: 'keychain signatures cannot approve a multisig operation',
530
+ reason: 'only primitive signatures can approve a multisig operation',
541
531
  })
542
- const address =
543
- signature.type === 'multisig'
544
- ? signature.account
545
- : SignatureEnvelope.extractAddress({
546
- payload: options.hash,
547
- signature,
548
- })
532
+ const address = SignatureEnvelope.extractAddress({
533
+ payload: options.hash,
534
+ signature,
535
+ })
549
536
  const owner = owners.get(address.toLowerCase())
550
537
  if (!owner)
551
538
  throw new InvalidApprovalError({
@@ -565,79 +552,6 @@ async function selectApprovals_internal(
565
552
  const valid: SelectedApproval[] = []
566
553
  const retained: RetainedApproval[] = []
567
554
  for (const group of groups.values()) {
568
- const nested = group.signatures.filter(
569
- (signature) => signature.type === 'multisig',
570
- )
571
- if (nested.length > 0) {
572
- if (nested.length !== group.signatures.length)
573
- throw new InvalidApprovalError({
574
- reason: `owner ${group.address} has conflicting signature types`,
575
- })
576
- if (
577
- path.length >= MultisigConfig.maxNestingDepth ||
578
- path.includes(group.address.toLowerCase())
579
- )
580
- throw new InvalidApprovalError({
581
- reason: `nested multisig owner ${group.address} is invalid`,
582
- })
583
- const config = MultisigConfig.from(nested[0]!.config)
584
- if (nested.some((signature) => !sameConfig(signature.config, config)))
585
- throw new InvalidApprovalError({
586
- reason: `nested multisig owner ${group.address} has conflicting config witnesses`,
587
- })
588
- if (
589
- config.version === 0n &&
590
- !Address.isEqual(MultisigConfig.getAddress(config), group.address)
591
- )
592
- throw new InvalidApprovalError({
593
- reason: `initial config does not derive nested multisig owner ${group.address}`,
594
- })
595
- const selected = await selectApprovals_internal(
596
- {
597
- account: group.address,
598
- approvals: nested.flatMap((signature) =>
599
- signature.signatures.map((approval) =>
600
- SignatureEnvelope.serialize(approval),
601
- ),
602
- ),
603
- config,
604
- hash: MultisigConfig.getSignPayload({
605
- account: group.address,
606
- config,
607
- payload: options.hash,
608
- }),
609
- },
610
- [...path, group.address.toLowerCase()],
611
- )
612
- retained.push({
613
- address: group.address,
614
- signature: SignatureEnvelope.serialize(
615
- SignatureEnvelope.from({
616
- account: group.address,
617
- config,
618
- signatures: selected.approvals.map((approval) =>
619
- SignatureEnvelope.from(approval),
620
- ),
621
- }),
622
- ),
623
- })
624
- if (selected.weight >= selected.threshold)
625
- valid.push({
626
- address: group.address,
627
- signature: SignatureEnvelope.serialize(
628
- SignatureEnvelope.from({
629
- account: group.address,
630
- config,
631
- signatures: selected.selectedApprovals.map((approval) =>
632
- SignatureEnvelope.from(approval),
633
- ),
634
- }),
635
- ),
636
- weight: group.weight,
637
- })
638
- continue
639
- }
640
-
641
555
  const signatures = group.signatures.map((signature) => {
642
556
  if (
643
557
  !SignatureEnvelope.verify(signature, {
@@ -706,7 +620,7 @@ type ApprovalGroup = {
706
620
  type RetainedApproval = {
707
621
  /** Configured owner address. */
708
622
  address: Address.Address
709
- /** Serialized primitive or normalized nested approval. */
623
+ /** Serialized primitive approval. */
710
624
  signature: SignatureEnvelope.Serialized
711
625
  }
712
626
 
@@ -826,13 +740,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
826
740
  reason:
827
741
  'weight is not reachable by signatureCount retained owner approvals',
828
742
  })
829
- if (
830
- config.version === 0n &&
831
- !Address.isEqual(MultisigConfig.getAddress(config), operation.account)
832
- )
833
- throw new InvalidOperationError({
834
- reason: 'initial config does not derive the operation account',
835
- })
836
743
  }
837
744
 
838
745
  /**
@@ -1020,8 +927,12 @@ function assertApproval(
1020
927
  account: Address.Address,
1021
928
  serialized: SignatureEnvelope.Serialized,
1022
929
  config: MultisigConfig.Config,
1023
- ): SignatureEnvelope.SignatureEnvelope {
930
+ ): SignatureEnvelope.Primitive {
1024
931
  const approval = SignatureEnvelope.deserialize(serialized)
932
+ if (approval.type === 'keychain' || approval.type === 'multisig')
933
+ throw new InvalidOperationError({
934
+ reason: 'only primitive owner approvals are allowed',
935
+ })
1025
936
  SignatureEnvelope.assert({
1026
937
  account,
1027
938
  config,
@@ -1049,8 +960,10 @@ function assertRetainedApprovals(
1049
960
  SignatureEnvelope.deserialize(approval),
1050
961
  )
1051
962
  for (const approval of selected) {
1052
- const index = retained.findIndex((candidate) =>
1053
- includesApproval(candidate, approval),
963
+ const index = retained.findIndex(
964
+ (candidate) =>
965
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
966
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1054
967
  )
1055
968
  if (index === -1)
1056
969
  throw new InvalidOperationError({
@@ -1074,8 +987,10 @@ function assertSelectedApprovals(
1074
987
  SignatureEnvelope.deserialize(approval),
1075
988
  )
1076
989
  for (const approval of selected) {
1077
- const index = retained.findIndex((candidate) =>
1078
- includesApproval(candidate, approval),
990
+ const index = retained.findIndex(
991
+ (candidate) =>
992
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
993
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1079
994
  )
1080
995
  if (index === -1)
1081
996
  throw new InvalidOperationError({
@@ -1106,36 +1021,6 @@ function assertSelectedApprovals(
1106
1021
  }
1107
1022
  }
1108
1023
 
1109
- /**
1110
- * Checks whether a selected approval is contained in a retained approval tree.
1111
- *
1112
- * @internal
1113
- */
1114
- function includesApproval(
1115
- retained: SignatureEnvelope.SignatureEnvelope,
1116
- selected: SignatureEnvelope.SignatureEnvelope,
1117
- ): boolean {
1118
- if (retained.type !== 'multisig' || selected.type !== 'multisig')
1119
- return (
1120
- SignatureEnvelope.serialize(retained).toLowerCase() ===
1121
- SignatureEnvelope.serialize(selected).toLowerCase()
1122
- )
1123
- if (retained.account.toLowerCase() !== selected.account.toLowerCase())
1124
- return false
1125
- if (!sameConfig(retained.config, selected.config)) return false
1126
- let index = 0
1127
- for (const approval of selected.signatures) {
1128
- while (
1129
- index < retained.signatures.length &&
1130
- !includesApproval(retained.signatures[index]!, approval)
1131
- )
1132
- index++
1133
- if (index === retained.signatures.length) return false
1134
- index++
1135
- }
1136
- return true
1137
- }
1138
-
1139
1024
  /**
1140
1025
  * Checks whether exactly `signatureCount` retained owners can produce `weight`.
1141
1026
  *
@@ -2,11 +2,11 @@ import { expectTypeOf, test } from 'vp/test'
2
2
  import * as MultisigSimulation from './MultisigSimulation.js'
3
3
 
4
4
  const rpc = {
5
- account: '0x2222222222222222222222222222222222222222',
6
5
  approvals: [
7
6
  {
7
+ keyType: 'webAuthn',
8
+ keyData: '0x0102030405',
8
9
  owner: '0x1111111111111111111111111111111111111111',
9
- type: 'primitive',
10
10
  },
11
11
  ],
12
12
  config:
@@ -3,70 +3,26 @@ import { describe, expect, test } from 'vp/test'
3
3
 
4
4
  const config =
5
5
  '0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01' as const
6
- const nestedConfig =
7
- '0xf83ba022222222222222222222222222222222222222222222222222222222222222220101d7d694222222222222222222222222222222222222222201' as const
8
6
  const rpc = {
9
- account: '0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa',
10
7
  approvals: [
11
8
  {
12
- keyData: '0x0102030405',
13
9
  keyType: 'webAuthn',
10
+ keyData: '0x0102030405',
14
11
  owner: '0x1111111111111111111111111111111111111111',
15
- type: 'primitive',
16
- },
17
- {
18
- spec: {
19
- account: '0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
20
- approvals: [
21
- {
22
- keyData: '0x010203040506',
23
- keyType: 'secp256k1',
24
- owner: '0x2222222222222222222222222222222222222222',
25
- },
26
- ],
27
- config: nestedConfig,
28
- },
29
- type: 'multisig',
30
12
  },
31
13
  ],
32
14
  config,
33
15
  } as const satisfies MultisigSimulation.Rpc
34
16
 
35
17
  describe('fromRpc', () => {
36
- test('behavior: decodes root and nested configurations', () => {
18
+ test('behavior: decodes the configuration', () => {
37
19
  expect(MultisigSimulation.fromRpc(rpc)).toMatchInlineSnapshot(`
38
20
  {
39
- "account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
40
21
  "approvals": [
41
22
  {
42
23
  "keyData": "0x0102030405",
43
24
  "keyType": "webAuthn",
44
25
  "owner": "0x1111111111111111111111111111111111111111",
45
- "type": "primitive",
46
- },
47
- {
48
- "spec": {
49
- "account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
50
- "approvals": [
51
- {
52
- "keyData": "0x010203040506",
53
- "keyType": "secp256k1",
54
- "owner": "0x2222222222222222222222222222222222222222",
55
- },
56
- ],
57
- "config": {
58
- "owners": [
59
- {
60
- "owner": "0x2222222222222222222222222222222222222222",
61
- "weight": 1,
62
- },
63
- ],
64
- "salt": "0x2222222222222222222222222222222222222222222222222222222222222222",
65
- "threshold": 1,
66
- "version": 1n,
67
- },
68
- },
69
- "type": "multisig",
70
26
  },
71
27
  ],
72
28
  "config": {
@@ -108,26 +64,14 @@ describe('fromRpc', () => {
108
64
  `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
109
65
  )
110
66
  })
111
-
112
- test('error: rejects excess nested approvals', () => {
67
+ test('error: rejects nested approvals', () => {
113
68
  expect(() =>
114
69
  MultisigSimulation.fromRpc({
115
70
  ...rpc,
116
- approvals: [
117
- {
118
- ...rpc.approvals[1],
119
- spec: {
120
- ...rpc.approvals[1].spec,
121
- approvals: Array.from(
122
- { length: 9 },
123
- () => rpc.approvals[1].spec.approvals[0],
124
- ),
125
- },
126
- },
127
- ],
128
- }),
71
+ approvals: [{ type: 'multisig', spec: rpc }],
72
+ } as never),
129
73
  ).toThrowErrorMatchingInlineSnapshot(
130
- `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
74
+ `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
131
75
  )
132
76
  })
133
77
 
@@ -172,33 +116,17 @@ describe('toRpc', () => {
172
116
  test('behavior: encodes configurations and shims long key data', () => {
173
117
  expect(MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)))
174
118
  .toMatchInlineSnapshot(`
175
- {
176
- "account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
177
- "approvals": [
178
- {
179
- "keyData": "0x0005",
180
- "keyType": "webAuthn",
181
- "owner": "0x1111111111111111111111111111111111111111",
182
- "type": "primitive",
183
- },
184
- {
185
- "spec": {
186
- "account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
187
- "approvals": [
188
- {
189
- "keyData": "0x0006",
190
- "keyType": "secp256k1",
191
- "owner": "0x2222222222222222222222222222222222222222",
192
- },
193
- ],
194
- "config": "${nestedConfig}",
119
+ {
120
+ "approvals": [
121
+ {
122
+ "keyData": "0x0005",
123
+ "keyType": "webAuthn",
124
+ "owner": "0x1111111111111111111111111111111111111111",
195
125
  },
196
- "type": "multisig",
197
- },
198
- ],
199
- "config": "${config}",
200
- }
201
- `)
126
+ ],
127
+ "config": "0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01",
128
+ }
129
+ `)
202
130
  })
203
131
 
204
132
  test('behavior: preserves short key data', () => {
@@ -213,7 +141,6 @@ describe('toRpc', () => {
213
141
  "keyData": "0x0102",
214
142
  "keyType": "webAuthn",
215
143
  "owner": "0x1111111111111111111111111111111111111111",
216
- "type": "primitive",
217
144
  },
218
145
  ]
219
146
  `)
@@ -254,30 +181,14 @@ describe('toRpc', () => {
254
181
  `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
255
182
  )
256
183
  })
257
-
258
- test('error: rejects excess nested approvals', () => {
259
- const spec = MultisigSimulation.fromRpc(rpc)
260
- const nested = spec.approvals[1]!
261
- if (nested.type !== 'multisig') throw new Error('unreachable')
262
-
184
+ test('error: rejects nested approvals', () => {
263
185
  expect(() =>
264
186
  MultisigSimulation.toRpc({
265
- ...spec,
266
- approvals: [
267
- {
268
- ...nested,
269
- spec: {
270
- ...nested.spec,
271
- approvals: Array.from(
272
- { length: 9 },
273
- () => nested.spec.approvals[0]!,
274
- ),
275
- },
276
- },
277
- ],
278
- }),
187
+ ...rpc,
188
+ approvals: [{ type: 'multisig', spec: rpc }],
189
+ } as never),
279
190
  ).toThrowErrorMatchingInlineSnapshot(
280
- `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
191
+ `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
281
192
  )
282
193
  })
283
194
  })
@@ -7,56 +7,10 @@ import * as MultisigConfig from './MultisigConfig.js'
7
7
  import type * as SignatureEnvelope from './SignatureEnvelope.js'
8
8
 
9
9
  /** Native multisig owner approval used for RPC simulation. */
10
- export type Approval = NestedApproval | PrimitiveApproval
10
+ export type Approval = PrimitiveApproval
11
11
 
12
12
  /** JSON-RPC native multisig owner approval used for RPC simulation. */
13
- export type ApprovalRpc = NestedApprovalRpc | PrimitiveApproval
14
-
15
- /** Nested native multisig owner approval used for RPC simulation. */
16
- export type NestedApproval = {
17
- /** Depth-2 multisig simulation spec. */
18
- spec: NestedSpec
19
- /** Approval type. */
20
- type: 'multisig'
21
- }
22
-
23
- /** JSON-RPC nested native multisig owner approval used for RPC simulation. */
24
- export type NestedApprovalRpc = {
25
- /** Depth-2 multisig simulation spec. */
26
- spec: NestedSpecRpc
27
- /** Approval type. */
28
- type: 'multisig'
29
- }
30
-
31
- /** Primitive approval in a depth-2 multisig simulation spec. */
32
- export type NestedPrimitiveApproval = {
33
- /** Optional signature-specific gas-estimation data. */
34
- keyData?: Hex.Hex | undefined
35
- /** Signature type to model. Omission uses a maximum-size WebAuthn signature. */
36
- keyType?: SignatureEnvelope.Type | undefined
37
- /** Configured owner address. */
38
- owner: Address.Address
39
- }
40
-
41
- /** Depth-2 native multisig spec used for RPC simulation. */
42
- export type NestedSpec = {
43
- /** Nested multisig account. */
44
- account: Address.Address
45
- /** Primitive owner approvals to model. */
46
- approvals: readonly NestedPrimitiveApproval[]
47
- /** Complete applicable configuration. */
48
- config: MultisigConfig.Config
49
- }
50
-
51
- /** JSON-RPC depth-2 native multisig spec used for RPC simulation. */
52
- export type NestedSpecRpc = {
53
- /** Nested multisig account. */
54
- account: Address.Address
55
- /** Primitive owner approvals to model. */
56
- approvals: readonly NestedPrimitiveApproval[]
57
- /** Canonical RLP-encoded applicable configuration. */
58
- config: Hex.Hex
59
- }
13
+ export type ApprovalRpc = PrimitiveApproval
60
14
 
61
15
  /** Primitive owner approval used for RPC simulation. */
62
16
  export type PrimitiveApproval = {
@@ -66,14 +20,10 @@ export type PrimitiveApproval = {
66
20
  keyType?: SignatureEnvelope.Type | undefined
67
21
  /** Configured owner address. */
68
22
  owner: Address.Address
69
- /** Approval type. */
70
- type: 'primitive'
71
23
  }
72
24
 
73
25
  /** JSON-RPC representation of a native multisig simulation spec. */
74
26
  export type Rpc = Compute<{
75
- /** Account authorized by this spec. */
76
- account: Address.Address
77
27
  /** Owner approvals to model. */
78
28
  approvals: readonly ApprovalRpc[]
79
29
  /** Canonical RLP-encoded applicable configuration. */
@@ -82,8 +32,6 @@ export type Rpc = Compute<{
82
32
 
83
33
  /** Native multisig spec used to construct an RPC simulation signature. */
84
34
  export type Spec = Compute<{
85
- /** Account authorized by this spec. */
86
- account: Address.Address
87
35
  /** Owner approvals to model. */
88
36
  approvals: readonly Approval[]
89
37
  /** Complete applicable configuration. */
@@ -105,24 +53,9 @@ export type Spec = Compute<{
105
53
  * @returns The multisig simulation spec with decoded configurations.
106
54
  */
107
55
  export function fromRpc(spec: Rpc): Spec {
108
- const { account, approvals, config } = spec
109
- assertApprovalCount(approvals)
110
- return {
111
- account,
112
- approvals: approvals.map((approval) => {
113
- if (approval.type === 'primitive') return approval
114
- assertApprovalCount(approval.spec.approvals)
115
- return {
116
- spec: {
117
- account: approval.spec.account,
118
- approvals: approval.spec.approvals,
119
- config: deserializeConfig(approval.spec.config),
120
- },
121
- type: 'multisig',
122
- }
123
- }),
124
- config: deserializeConfig(config),
125
- }
56
+ const { approvals, config } = spec
57
+ assertApprovals(approvals)
58
+ return { approvals, config: deserializeConfig(config) }
126
59
  }
127
60
 
128
61
  export declare namespace fromRpc {
@@ -151,33 +84,14 @@ export declare namespace fromRpc {
151
84
  * @returns The JSON-RPC multisig simulation spec with encoded configurations.
152
85
  */
153
86
  export function toRpc(spec: Spec): Rpc {
154
- const { account, approvals, config } = spec
155
- assertApprovalCount(approvals)
87
+ const { approvals, config } = spec
88
+ assertApprovals(approvals)
156
89
  return {
157
- account,
158
- approvals: approvals.map((approval) => {
159
- if (approval.type === 'primitive')
160
- return {
161
- ...approval,
162
- ...(typeof approval.keyData !== 'undefined'
163
- ? { keyData: shimKeyData(approval.keyData) }
164
- : {}),
165
- }
166
- assertApprovalCount(approval.spec.approvals)
167
- return {
168
- spec: {
169
- account: approval.spec.account,
170
- approvals: approval.spec.approvals.map((approval) => ({
171
- ...approval,
172
- ...(typeof approval.keyData !== 'undefined'
173
- ? { keyData: shimKeyData(approval.keyData) }
174
- : {}),
175
- })),
176
- config: serializeConfig(approval.spec.config),
177
- },
178
- type: 'multisig',
179
- }
180
- }),
90
+ approvals: approvals.map(({ keyData, keyType, owner }) => ({
91
+ ...(keyData !== undefined ? { keyData: shimKeyData(keyData) } : {}),
92
+ ...(keyType !== undefined ? { keyType } : {}),
93
+ owner,
94
+ })),
181
95
  config: serializeConfig(config),
182
96
  }
183
97
  }
@@ -193,7 +107,19 @@ export declare namespace toRpc {
193
107
  }
194
108
 
195
109
  /** @internal */
196
- function assertApprovalCount(approvals: readonly unknown[]) {
110
+ function assertApprovals(approvals: readonly unknown[]) {
111
+ if (
112
+ approvals.some(
113
+ (approval) =>
114
+ typeof approval !== 'object' ||
115
+ approval === null ||
116
+ 'spec' in approval ||
117
+ 'type' in approval,
118
+ )
119
+ )
120
+ throw new InvalidSimulationError({
121
+ reason: 'only untagged primitive owner approvals are allowed',
122
+ })
197
123
  if (approvals.length > MultisigConfig.maxSignatures)
198
124
  throw new InvalidSimulationError({
199
125
  reason: `approval count exceeds ${MultisigConfig.maxSignatures}`,