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
@@ -144,7 +144,8 @@ export declare namespace getHash {
144
144
  *
145
145
  * The function retains one canonical approval per owner. It selects the
146
146
  * smallest deterministic quorum by owner weight, then orders the selected
147
- * approvals by owner address for serialization.
147
+ * approvals by owner address for serialization. The caller supplies the
148
+ * account and config; the chain validates their factory-derived identity.
148
149
  *
149
150
  * @example
150
151
  * ```ts twoslash
@@ -171,22 +172,12 @@ export async function selectApprovals(
171
172
  if (!Hash.validate(hash))
172
173
  throw new InvalidApprovalError({ reason: 'hash is invalid' })
173
174
  const config = MultisigConfig.from(options.config)
174
- if (
175
- config.version === 0n &&
176
- !Address.isEqual(MultisigConfig.getAddress(config), account)
177
- )
178
- throw new InvalidApprovalError({
179
- reason: 'initial config does not derive the root multisig account',
180
- })
181
- return selectApprovals_internal(
182
- {
183
- account,
184
- approvals,
185
- config,
186
- hash,
187
- },
188
- [account.toLowerCase()],
189
- )
175
+ return selectApprovals_internal({
176
+ account,
177
+ approvals,
178
+ config,
179
+ hash,
180
+ })
190
181
  }
191
182
 
192
183
  export declare namespace selectApprovals {
@@ -194,7 +185,7 @@ export declare namespace selectApprovals {
194
185
  export type Options = {
195
186
  /** Root multisig account. */
196
187
  account: Address.Address
197
- /** Serialized primitive or nested owner approvals. */
188
+ /** Serialized primitive owner approvals. */
198
189
  approvals: readonly SignatureEnvelope.Serialized[]
199
190
  /** Current root multisig configuration. */
200
191
  config: MultisigConfig.Config
@@ -272,7 +263,7 @@ export function serializeKeyAuthorization(
272
263
  config,
273
264
  payload: KeyAuthorization_.getSignPayload(authorization),
274
265
  signatures: options.approvals.map((approval) =>
275
- SignatureEnvelope.deserialize(approval),
266
+ assertApproval(options.account, approval, config),
276
267
  ),
277
268
  })
278
269
  return KeyAuthorization_.serialize(
@@ -337,7 +328,7 @@ export function serializeTransaction(
337
328
  value.transaction as TxEnvelopeTempo.Serialized,
338
329
  )
339
330
  const approvals = options.approvals.map((approval) =>
340
- SignatureEnvelope.from(approval),
331
+ assertApproval(value.account, approval, value.config),
341
332
  )
342
333
  assertRetainedApprovals(value, approvals)
343
334
  const signatures = SignatureEnvelope.sortMultisigApprovals({
@@ -522,7 +513,6 @@ export declare namespace toRpc {
522
513
  */
523
514
  async function selectApprovals_internal(
524
515
  options: selectApprovals.Options,
525
- path: readonly string[],
526
516
  ): Promise<selectApprovals.ReturnValue> {
527
517
  const owners = new Map(
528
518
  options.config.owners.map((owner) => [
@@ -533,17 +523,14 @@ async function selectApprovals_internal(
533
523
  const groups = new Map<string, ApprovalGroup>()
534
524
  for (const serialized of options.approvals) {
535
525
  const signature = SignatureEnvelope.from(serialized)
536
- if (signature.type === 'keychain')
526
+ if (signature.type === 'keychain' || signature.type === 'multisig')
537
527
  throw new InvalidApprovalError({
538
- reason: 'keychain signatures cannot approve a multisig operation',
528
+ reason: 'only primitive signatures can approve a multisig operation',
539
529
  })
540
- const address =
541
- signature.type === 'multisig'
542
- ? signature.account
543
- : SignatureEnvelope.extractAddress({
544
- payload: options.hash,
545
- signature,
546
- })
530
+ const address = SignatureEnvelope.extractAddress({
531
+ payload: options.hash,
532
+ signature,
533
+ })
547
534
  const owner = owners.get(address.toLowerCase())
548
535
  if (!owner)
549
536
  throw new InvalidApprovalError({
@@ -563,79 +550,6 @@ async function selectApprovals_internal(
563
550
  const valid: SelectedApproval[] = []
564
551
  const retained: RetainedApproval[] = []
565
552
  for (const group of groups.values()) {
566
- const nested = group.signatures.filter(
567
- (signature) => signature.type === 'multisig',
568
- )
569
- if (nested.length > 0) {
570
- if (nested.length !== group.signatures.length)
571
- throw new InvalidApprovalError({
572
- reason: `owner ${group.address} has conflicting signature types`,
573
- })
574
- if (
575
- path.length >= MultisigConfig.maxNestingDepth ||
576
- path.includes(group.address.toLowerCase())
577
- )
578
- throw new InvalidApprovalError({
579
- reason: `nested multisig owner ${group.address} is invalid`,
580
- })
581
- const config = MultisigConfig.from(nested[0]!.config)
582
- if (nested.some((signature) => !sameConfig(signature.config, config)))
583
- throw new InvalidApprovalError({
584
- reason: `nested multisig owner ${group.address} has conflicting config witnesses`,
585
- })
586
- if (
587
- config.version === 0n &&
588
- !Address.isEqual(MultisigConfig.getAddress(config), group.address)
589
- )
590
- throw new InvalidApprovalError({
591
- reason: `initial config does not derive nested multisig owner ${group.address}`,
592
- })
593
- const selected = await selectApprovals_internal(
594
- {
595
- account: group.address,
596
- approvals: nested.flatMap((signature) =>
597
- signature.signatures.map((approval) =>
598
- SignatureEnvelope.serialize(approval),
599
- ),
600
- ),
601
- config,
602
- hash: MultisigConfig.getSignPayload({
603
- account: group.address,
604
- config,
605
- payload: options.hash,
606
- }),
607
- },
608
- [...path, group.address.toLowerCase()],
609
- )
610
- retained.push({
611
- address: group.address,
612
- signature: SignatureEnvelope.serialize(
613
- SignatureEnvelope.from({
614
- account: group.address,
615
- config,
616
- signatures: selected.approvals.map((approval) =>
617
- SignatureEnvelope.from(approval),
618
- ),
619
- }),
620
- ),
621
- })
622
- if (selected.weight >= selected.threshold)
623
- valid.push({
624
- address: group.address,
625
- signature: SignatureEnvelope.serialize(
626
- SignatureEnvelope.from({
627
- account: group.address,
628
- config,
629
- signatures: selected.selectedApprovals.map((approval) =>
630
- SignatureEnvelope.from(approval),
631
- ),
632
- }),
633
- ),
634
- weight: group.weight,
635
- })
636
- continue
637
- }
638
-
639
553
  const signatures = group.signatures.map((signature) => {
640
554
  if (
641
555
  !SignatureEnvelope.verify(signature, {
@@ -704,7 +618,7 @@ type ApprovalGroup = {
704
618
  type RetainedApproval = {
705
619
  /** Configured owner address. */
706
620
  address: Address.Address
707
- /** Serialized primitive or normalized nested approval. */
621
+ /** Serialized primitive approval. */
708
622
  signature: SignatureEnvelope.Serialized
709
623
  }
710
624
 
@@ -824,13 +738,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
824
738
  reason:
825
739
  'weight is not reachable by signatureCount retained owner approvals',
826
740
  })
827
- if (
828
- config.version === 0n &&
829
- !Address.isEqual(MultisigConfig.getAddress(config), operation.account)
830
- )
831
- throw new InvalidOperationError({
832
- reason: 'initial config does not derive the operation account',
833
- })
834
741
  }
835
742
 
836
743
  /**
@@ -1018,8 +925,12 @@ function assertApproval(
1018
925
  account: Address.Address,
1019
926
  serialized: SignatureEnvelope.Serialized,
1020
927
  config: MultisigConfig.Config,
1021
- ): SignatureEnvelope.SignatureEnvelope {
928
+ ): SignatureEnvelope.Primitive {
1022
929
  const approval = SignatureEnvelope.deserialize(serialized)
930
+ if (approval.type === 'keychain' || approval.type === 'multisig')
931
+ throw new InvalidOperationError({
932
+ reason: 'only primitive owner approvals are allowed',
933
+ })
1023
934
  SignatureEnvelope.assert({
1024
935
  account,
1025
936
  config,
@@ -1047,8 +958,10 @@ function assertRetainedApprovals(
1047
958
  SignatureEnvelope.deserialize(approval),
1048
959
  )
1049
960
  for (const approval of selected) {
1050
- const index = retained.findIndex((candidate) =>
1051
- includesApproval(candidate, approval),
961
+ const index = retained.findIndex(
962
+ (candidate) =>
963
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
964
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1052
965
  )
1053
966
  if (index === -1)
1054
967
  throw new InvalidOperationError({
@@ -1072,8 +985,10 @@ function assertSelectedApprovals(
1072
985
  SignatureEnvelope.deserialize(approval),
1073
986
  )
1074
987
  for (const approval of selected) {
1075
- const index = retained.findIndex((candidate) =>
1076
- includesApproval(candidate, approval),
988
+ const index = retained.findIndex(
989
+ (candidate) =>
990
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
991
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1077
992
  )
1078
993
  if (index === -1)
1079
994
  throw new InvalidOperationError({
@@ -1104,36 +1019,6 @@ function assertSelectedApprovals(
1104
1019
  }
1105
1020
  }
1106
1021
 
1107
- /**
1108
- * Checks whether a selected approval is contained in a retained approval tree.
1109
- *
1110
- * @internal
1111
- */
1112
- function includesApproval(
1113
- retained: SignatureEnvelope.SignatureEnvelope,
1114
- selected: SignatureEnvelope.SignatureEnvelope,
1115
- ): boolean {
1116
- if (retained.type !== 'multisig' || selected.type !== 'multisig')
1117
- return (
1118
- SignatureEnvelope.serialize(retained).toLowerCase() ===
1119
- SignatureEnvelope.serialize(selected).toLowerCase()
1120
- )
1121
- if (retained.account.toLowerCase() !== selected.account.toLowerCase())
1122
- return false
1123
- if (!sameConfig(retained.config, selected.config)) return false
1124
- let index = 0
1125
- for (const approval of selected.signatures) {
1126
- while (
1127
- index < retained.signatures.length &&
1128
- !includesApproval(retained.signatures[index]!, approval)
1129
- )
1130
- index++
1131
- if (index === retained.signatures.length) return false
1132
- index++
1133
- }
1134
- return true
1135
- }
1136
-
1137
1022
  /**
1138
1023
  * Checks whether exactly `signatureCount` retained owners can produce `weight`.
1139
1024
  *
@@ -2,11 +2,11 @@ import { expectTypeOf, test } from 'vitest'
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 'vitest'
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
 
@@ -173,33 +117,17 @@ describe('toRpc', () => {
173
117
  expect(
174
118
  MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)),
175
119
  ).toMatchInlineSnapshot(`
176
- {
177
- "account": "0xaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaaa",
178
- "approvals": [
179
- {
180
- "keyData": "0x0005",
181
- "keyType": "webAuthn",
182
- "owner": "0x1111111111111111111111111111111111111111",
183
- "type": "primitive",
184
- },
185
- {
186
- "spec": {
187
- "account": "0xbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb",
188
- "approvals": [
189
- {
190
- "keyData": "0x0006",
191
- "keyType": "secp256k1",
192
- "owner": "0x2222222222222222222222222222222222222222",
193
- },
194
- ],
195
- "config": "${nestedConfig}",
120
+ {
121
+ "approvals": [
122
+ {
123
+ "keyData": "0x0005",
124
+ "keyType": "webAuthn",
125
+ "owner": "0x1111111111111111111111111111111111111111",
196
126
  },
197
- "type": "multisig",
198
- },
199
- ],
200
- "config": "${config}",
201
- }
202
- `)
127
+ ],
128
+ "config": "0xf852a011111111111111111111111111111111111111111111111111111111111111118002eed694111111111111111111111111111111111111111101d694bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb01",
129
+ }
130
+ `)
203
131
  })
204
132
 
205
133
  test('behavior: preserves short key data', () => {
@@ -214,7 +142,6 @@ describe('toRpc', () => {
214
142
  "keyData": "0x0102",
215
143
  "keyType": "webAuthn",
216
144
  "owner": "0x1111111111111111111111111111111111111111",
217
- "type": "primitive",
218
145
  },
219
146
  ]
220
147
  `)
@@ -255,30 +182,14 @@ describe('toRpc', () => {
255
182
  `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
256
183
  )
257
184
  })
258
-
259
- test('error: rejects excess nested approvals', () => {
260
- const spec = MultisigSimulation.fromRpc(rpc)
261
- const nested = spec.approvals[1]!
262
- if (nested.type !== 'multisig') throw new Error('unreachable')
263
-
185
+ test('error: rejects nested approvals', () => {
264
186
  expect(() =>
265
187
  MultisigSimulation.toRpc({
266
- ...spec,
267
- approvals: [
268
- {
269
- ...nested,
270
- spec: {
271
- ...nested.spec,
272
- approvals: Array.from(
273
- { length: 9 },
274
- () => nested.spec.approvals[0]!,
275
- ),
276
- },
277
- },
278
- ],
279
- }),
188
+ ...rpc,
189
+ approvals: [{ type: 'multisig', spec: rpc }],
190
+ } as never),
280
191
  ).toThrowErrorMatchingInlineSnapshot(
281
- `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: approval count exceeds 8.]`,
192
+ `[MultisigSimulation.InvalidSimulationError: Invalid multisig simulation: only untagged primitive owner approvals are allowed.]`,
282
193
  )
283
194
  })
284
195
  })
@@ -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}`,