ox 1.7.2 → 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 (142) hide show
  1. package/CHANGELOG.md +43 -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 +43 -39
  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 +45 -28
  24. package/dist/tempo/MultisigOperation.d.ts.map +1 -1
  25. package/dist/tempo/MultisigOperation.js +81 -131
  26. package/dist/tempo/MultisigOperation.js.map +1 -1
  27. package/dist/tempo/MultisigSimulation.d.ts +85 -0
  28. package/dist/tempo/MultisigSimulation.d.ts.map +1 -0
  29. package/dist/tempo/MultisigSimulation.js +112 -0
  30. package/dist/tempo/MultisigSimulation.js.map +1 -0
  31. package/dist/tempo/RpcSchemaTempo.d.ts +20 -10
  32. package/dist/tempo/RpcSchemaTempo.d.ts.map +1 -1
  33. package/dist/tempo/SignatureEnvelope.d.ts +74 -153
  34. package/dist/tempo/SignatureEnvelope.d.ts.map +1 -1
  35. package/dist/tempo/SignatureEnvelope.js +130 -187
  36. package/dist/tempo/SignatureEnvelope.js.map +1 -1
  37. package/dist/tempo/TransactionRequest.d.ts +9 -7
  38. package/dist/tempo/TransactionRequest.d.ts.map +1 -1
  39. package/dist/tempo/TransactionRequest.js +12 -7
  40. package/dist/tempo/TransactionRequest.js.map +1 -1
  41. package/dist/tempo/index.d.ts +7 -5
  42. package/dist/tempo/index.d.ts.map +1 -1
  43. package/dist/tempo/index.js +7 -5
  44. package/dist/tempo/index.js.map +1 -1
  45. package/dist/zod/tempo/AuthorizationTempo.d.ts +360 -140
  46. package/dist/zod/tempo/AuthorizationTempo.d.ts.map +1 -1
  47. package/dist/zod/tempo/KeyAuthorization.d.ts +247 -487
  48. package/dist/zod/tempo/KeyAuthorization.d.ts.map +1 -1
  49. package/dist/zod/tempo/KeyAuthorization.js +29 -31
  50. package/dist/zod/tempo/KeyAuthorization.js.map +1 -1
  51. package/dist/zod/tempo/MultisigOperation.d.ts +0 -10
  52. package/dist/zod/tempo/MultisigOperation.d.ts.map +1 -1
  53. package/dist/zod/tempo/MultisigOperation.js +0 -3
  54. package/dist/zod/tempo/MultisigOperation.js.map +1 -1
  55. package/dist/zod/tempo/MultisigSimulation.d.ts +52 -0
  56. package/dist/zod/tempo/MultisigSimulation.d.ts.map +1 -0
  57. package/dist/zod/tempo/MultisigSimulation.js +44 -0
  58. package/dist/zod/tempo/MultisigSimulation.js.map +1 -0
  59. package/dist/zod/tempo/RpcSchemaTempo.d.ts +357 -422
  60. package/dist/zod/tempo/RpcSchemaTempo.d.ts.map +1 -1
  61. package/dist/zod/tempo/RpcSchemaTempo.js +19 -10
  62. package/dist/zod/tempo/RpcSchemaTempo.js.map +1 -1
  63. package/dist/zod/tempo/SignatureEnvelope.d.ts +108 -42
  64. package/dist/zod/tempo/SignatureEnvelope.d.ts.map +1 -1
  65. package/dist/zod/tempo/SignatureEnvelope.js +21 -19
  66. package/dist/zod/tempo/SignatureEnvelope.js.map +1 -1
  67. package/dist/zod/tempo/Transaction.d.ts +678 -666
  68. package/dist/zod/tempo/Transaction.d.ts.map +1 -1
  69. package/dist/zod/tempo/TransactionRequest.d.ts +580 -619
  70. package/dist/zod/tempo/TransactionRequest.d.ts.map +1 -1
  71. package/dist/zod/tempo/TransactionRequest.js +18 -10
  72. package/dist/zod/tempo/TransactionRequest.js.map +1 -1
  73. package/dist/zod/tempo/TxEnvelopeTempo.d.ts +327 -213
  74. package/dist/zod/tempo/TxEnvelopeTempo.d.ts.map +1 -1
  75. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts +105 -3
  76. package/dist/zod/tempo/ZoneRpcAuthentication.d.ts.map +1 -1
  77. package/dist/zod/tempo/z.d.ts +1 -1
  78. package/dist/zod/tempo/z.d.ts.map +1 -1
  79. package/dist/zod/tempo/z.js +1 -1
  80. package/dist/zod/tempo/z.js.map +1 -1
  81. package/package.json +9 -9
  82. package/src/core/AbiEvent.ts +11 -9
  83. package/src/core/Blobs.ts +6 -2
  84. package/src/core/Signature.ts +41 -11
  85. package/src/core/Siwe.ts +20 -1
  86. package/src/core/_test/AbiEvent.test.ts +45 -0
  87. package/src/core/_test/Blobs.test.ts +22 -0
  88. package/src/core/_test/Signature.test.ts +166 -0
  89. package/src/core/_test/Siwe.test.ts +100 -1
  90. package/src/tempo/AuthorizationTempo.test.ts +8 -0
  91. package/src/tempo/KeyAuthorization.test-d.ts +16 -74
  92. package/src/tempo/KeyAuthorization.test.ts +113 -14
  93. package/src/tempo/KeyAuthorization.ts +110 -66
  94. package/src/tempo/MultisigConfig.test.ts +44 -20
  95. package/src/tempo/MultisigConfig.ts +65 -23
  96. package/src/tempo/MultisigOperation.test-d.ts +8 -6
  97. package/src/tempo/MultisigOperation.test.ts +187 -591
  98. package/src/tempo/MultisigOperation.ts +129 -205
  99. package/src/tempo/MultisigSimulation.test-d.ts +33 -0
  100. package/src/tempo/MultisigSimulation.test.ts +194 -0
  101. package/src/tempo/MultisigSimulation.ts +192 -0
  102. package/src/tempo/RpcSchemaTempo.test-d.ts +45 -10
  103. package/src/tempo/RpcSchemaTempo.ts +15 -6
  104. package/src/tempo/SignatureEnvelope.test-d.ts +27 -101
  105. package/src/tempo/SignatureEnvelope.test.ts +330 -533
  106. package/src/tempo/SignatureEnvelope.ts +198 -310
  107. package/src/tempo/Transaction.test.ts +61 -0
  108. package/src/tempo/TransactionRequest.test-d.ts +7 -7
  109. package/src/tempo/TransactionRequest.test.ts +81 -223
  110. package/src/tempo/TransactionRequest.ts +34 -13
  111. package/src/tempo/index.ts +7 -5
  112. package/src/tempo/multisig.e2e.test.ts +329 -818
  113. package/src/version.ts +1 -1
  114. package/src/zod/tempo/KeyAuthorization.ts +50 -39
  115. package/src/zod/tempo/MultisigOperation.ts +0 -3
  116. package/src/zod/tempo/MultisigSimulation.ts +53 -0
  117. package/src/zod/tempo/RpcSchemaTempo.ts +20 -10
  118. package/src/zod/tempo/SignatureEnvelope.ts +46 -59
  119. package/src/zod/tempo/TransactionRequest.ts +28 -14
  120. package/src/zod/tempo/_test/KeyAuthorization.test.ts +66 -30
  121. package/src/zod/tempo/_test/MultisigOperation.test.ts +0 -2
  122. package/src/zod/tempo/_test/MultisigSimulation.test-d.ts +13 -0
  123. package/src/zod/tempo/_test/MultisigSimulation.test.ts +53 -0
  124. package/src/zod/tempo/_test/RpcSchemaTempo.test-d.ts +19 -2
  125. package/src/zod/tempo/_test/RpcSchemaTempo.test.ts +46 -4
  126. package/src/zod/tempo/_test/SignatureEnvelope.test.ts +108 -129
  127. package/src/zod/tempo/_test/TransactionRequest.test.ts +17 -168
  128. package/src/zod/tempo/z.ts +1 -1
  129. package/dist/tempo/MultisigWitness.d.ts +0 -106
  130. package/dist/tempo/MultisigWitness.d.ts.map +0 -1
  131. package/dist/tempo/MultisigWitness.js +0 -118
  132. package/dist/tempo/MultisigWitness.js.map +0 -1
  133. package/dist/zod/tempo/MultisigWitness.d.ts +0 -152
  134. package/dist/zod/tempo/MultisigWitness.d.ts.map +0 -1
  135. package/dist/zod/tempo/MultisigWitness.js +0 -76
  136. package/dist/zod/tempo/MultisigWitness.js.map +0 -1
  137. package/src/tempo/MultisigWitness.test-d.ts +0 -42
  138. package/src/tempo/MultisigWitness.test.ts +0 -288
  139. package/src/tempo/MultisigWitness.ts +0 -210
  140. package/src/zod/tempo/MultisigWitness.ts +0 -101
  141. package/src/zod/tempo/_test/MultisigWitness.test-d.ts +0 -13
  142. package/src/zod/tempo/_test/MultisigWitness.test.ts +0 -149
@@ -15,14 +15,10 @@ export type Base<quantity = bigint> = {
15
15
  approvals: readonly Hex.Hex[]
16
16
  /** Root configuration used to verify approvals. */
17
17
  config: MultisigConfig.Config<quantity>
18
- /** Root configuration version. */
19
- configVersion: quantity
20
18
  /** Unix creation time in milliseconds. */
21
19
  createdAt: number
22
20
  /** Deterministic multisig operation hash. */
23
21
  hash: Hex.Hex
24
- /** Whether the operation initializes the root multisig account. */
25
- init: boolean
26
22
  /** Number of approvals selected for quorum evaluation. */
27
23
  signatureCount: number
28
24
  /** Required root owner weight. */
@@ -73,9 +69,6 @@ export type KeyAuthorizationRpc = KeyAuthorizationOperation<Hex.Hex>
73
69
  /** JSON-RPC multisig operation. */
74
70
  export type Rpc = Operation<Hex.Hex>
75
71
 
76
- /** Maximum supported multisig configuration version. */
77
- const maxConfigVersion = 2n ** 64n - 1n
78
-
79
72
  /**
80
73
  * Derives the deterministic hash for a multisig operation.
81
74
  *
@@ -86,7 +79,7 @@ const maxConfigVersion = 2n ** 64n - 1n
86
79
  *
87
80
  * const hash = MultisigOperation.getHash({
88
81
  * account,
89
- * configVersion: 1n,
82
+ * config,
90
83
  * transaction,
91
84
  * type: 'transaction'
92
85
  * })
@@ -96,7 +89,7 @@ const maxConfigVersion = 2n ** 64n - 1n
96
89
  * @returns The operation hash signed by each owner.
97
90
  */
98
91
  export function getHash(options: getHash.Options): Hex.Hex {
99
- const { account, configVersion } = options
92
+ const { account, config } = options
100
93
  const payload =
101
94
  options.type === 'transaction'
102
95
  ? TxEnvelopeTempo.getSignPayload(
@@ -107,7 +100,7 @@ export function getHash(options: getHash.Options): Hex.Hex {
107
100
  )
108
101
  return MultisigConfig.getSignPayload({
109
102
  account,
110
- config: { version: configVersion },
103
+ config,
111
104
  payload,
112
105
  })
113
106
  }
@@ -117,8 +110,8 @@ export declare namespace getHash {
117
110
  export type Options = {
118
111
  /** Root multisig account. */
119
112
  account: Address.Address
120
- /** Root configuration version. */
121
- configVersion: bigint
113
+ /** Complete root multisig configuration witness. */
114
+ config: MultisigConfig.Config
122
115
  } & (
123
116
  | {
124
117
  /** Canonical serialized key authorization. */
@@ -149,7 +142,8 @@ export declare namespace getHash {
149
142
  *
150
143
  * The function retains one canonical approval per owner. It selects the
151
144
  * smallest deterministic quorum by owner weight, then orders the selected
152
- * 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.
153
147
  *
154
148
  * @example
155
149
  * ```ts twoslash
@@ -160,8 +154,7 @@ export declare namespace getHash {
160
154
  * account,
161
155
  * approvals,
162
156
  * config,
163
- * hash,
164
- * resolveConfig
157
+ * hash
165
158
  * })
166
159
  * ```
167
160
  *
@@ -171,21 +164,18 @@ export declare namespace getHash {
171
164
  export async function selectApprovals(
172
165
  options: selectApprovals.Options,
173
166
  ): Promise<selectApprovals.ReturnValue> {
174
- const { account, approvals, hash, resolveConfig } = options
167
+ const { account, approvals, hash } = options
175
168
  if (!Address.validate(account) || Hex.toBigInt(account) === 0n)
176
169
  throw new InvalidApprovalError({ reason: 'account is invalid' })
177
170
  if (!Hash.validate(hash))
178
171
  throw new InvalidApprovalError({ reason: 'hash is invalid' })
179
- return selectApprovals_internal(
180
- {
181
- account,
182
- approvals,
183
- config: MultisigConfig.from(options.config),
184
- hash,
185
- resolveConfig,
186
- },
187
- [account.toLowerCase()],
188
- )
172
+ const config = MultisigConfig.from(options.config)
173
+ return selectApprovals_internal({
174
+ account,
175
+ approvals,
176
+ config,
177
+ hash,
178
+ })
189
179
  }
190
180
 
191
181
  export declare namespace selectApprovals {
@@ -193,33 +183,12 @@ export declare namespace selectApprovals {
193
183
  export type Options = {
194
184
  /** Root multisig account. */
195
185
  account: Address.Address
196
- /** Serialized primitive or nested owner approvals. */
186
+ /** Serialized primitive owner approvals. */
197
187
  approvals: readonly SignatureEnvelope.Serialized[]
198
188
  /** Current root multisig configuration. */
199
189
  config: MultisigConfig.Config
200
190
  /** Deterministic operation hash approved by root owners. */
201
191
  hash: Hex.Hex
202
- /** Resolves the current configuration of a nested multisig owner. */
203
- resolveConfig?: ResolveConfig | undefined
204
- }
205
-
206
- /** Resolves an initialized nested multisig configuration. */
207
- export type ResolveConfig = (
208
- options: ResolveConfigOptions,
209
- ) => ResolvedConfig | Promise<ResolvedConfig>
210
-
211
- /** Nested multisig configuration lookup parameters. */
212
- export type ResolveConfigOptions = {
213
- /** Nested multisig account. */
214
- account: Address.Address
215
- }
216
-
217
- /** Resolved nested multisig configuration. */
218
- export type ResolvedConfig = {
219
- /** Current nested multisig configuration. */
220
- config: MultisigConfig.Config
221
- /** Current nested multisig configuration version. */
222
- version: bigint
223
192
  }
224
193
 
225
194
  /** Result of validating and selecting approvals. */
@@ -248,6 +217,90 @@ export declare namespace selectApprovals {
248
217
  | Errors.GlobalErrorType
249
218
  }
250
219
 
220
+ /**
221
+ * Serializes a key authorization with selected multisig owner approvals.
222
+ *
223
+ * @example
224
+ * ```ts twoslash
225
+ * // @noErrors
226
+ * import { MultisigOperation } from 'ox/tempo'
227
+ *
228
+ * const authorization =
229
+ * MultisigOperation.serializeKeyAuthorization(
230
+ * keyAuthorization,
231
+ * {
232
+ * account,
233
+ * approvals: selection.selectedApprovals,
234
+ * config
235
+ * }
236
+ * )
237
+ * ```
238
+ *
239
+ * @param keyAuthorization - Canonical serialized unsigned key authorization.
240
+ * @param options - Multisig account, config, and selected approvals.
241
+ * @returns The signed serialized key authorization.
242
+ */
243
+ export function serializeKeyAuthorization(
244
+ keyAuthorization: Hex.Hex,
245
+ options: serializeKeyAuthorization.Options,
246
+ ): Hex.Hex {
247
+ const authorization = KeyAuthorization_.deserialize(keyAuthorization)
248
+ if (authorization.signature)
249
+ throw new InvalidOperationError({
250
+ reason: 'keyAuthorization must not contain a signature',
251
+ })
252
+ if (
253
+ KeyAuthorization_.serialize(authorization).toLowerCase() !==
254
+ keyAuthorization.toLowerCase()
255
+ )
256
+ throw new InvalidOperationError({
257
+ reason: 'keyAuthorization is not canonically serialized',
258
+ })
259
+ const config = MultisigConfig.from(options.config)
260
+ const signatures = SignatureEnvelope.sortMultisigApprovals({
261
+ account: options.account,
262
+ config,
263
+ payload: KeyAuthorization_.getSignPayload(authorization),
264
+ signatures: options.approvals.map((approval) =>
265
+ assertApproval(options.account, approval, config),
266
+ ),
267
+ })
268
+ return KeyAuthorization_.serialize(
269
+ KeyAuthorization_.from(authorization, {
270
+ signature: SignatureEnvelope.from({
271
+ account: options.account,
272
+ config,
273
+ signatures,
274
+ }),
275
+ }),
276
+ )
277
+ }
278
+
279
+ export declare namespace serializeKeyAuthorization {
280
+ /** Options for `serializeKeyAuthorization`. */
281
+ export type Options = {
282
+ /** Root multisig account. */
283
+ account: Address.Address
284
+ /** Selected serialized owner approvals. */
285
+ approvals: readonly SignatureEnvelope.Serialized[]
286
+ /** Complete applicable root multisig config. */
287
+ config: MultisigConfig.Config
288
+ }
289
+
290
+ /** Error type for `serializeKeyAuthorization`. */
291
+ export type ErrorType =
292
+ | InvalidOperationError
293
+ | KeyAuthorization_.deserialize.ErrorType
294
+ | KeyAuthorization_.from.ErrorType
295
+ | KeyAuthorization_.getSignPayload.ErrorType
296
+ | KeyAuthorization_.serialize.ErrorType
297
+ | MultisigConfig.assert.ErrorType
298
+ | SignatureEnvelope.assert.ErrorType
299
+ | SignatureEnvelope.InvalidSerializedError
300
+ | SignatureEnvelope.sortMultisigApprovals.ErrorType
301
+ | Errors.GlobalErrorType
302
+ }
303
+
251
304
  /**
252
305
  * Serializes a multisig transaction operation with selected owner approvals.
253
306
  *
@@ -277,7 +330,7 @@ export function serializeTransaction(
277
330
  value.transaction as TxEnvelopeTempo.Serialized,
278
331
  )
279
332
  const approvals = options.approvals.map((approval) =>
280
- SignatureEnvelope.from(approval),
333
+ assertApproval(value.account, approval, value.config),
281
334
  )
282
335
  assertRetainedApprovals(value, approvals)
283
336
  const signatures = SignatureEnvelope.sortMultisigApprovals({
@@ -340,10 +393,6 @@ export function from<const operation extends Operation>(
340
393
  ): from.ReturnValue<operation> {
341
394
  try {
342
395
  const config = MultisigConfig.from(operation.config)
343
- if (config.version !== operation.configVersion)
344
- throw new InvalidOperationError({
345
- reason: 'config.version must equal configVersion',
346
- })
347
396
  if (
348
397
  typeof config.threshold !== 'number' ||
349
398
  config.owners.some((owner) => typeof owner.weight !== 'number')
@@ -393,22 +442,19 @@ export function fromRpc<const operation extends Rpc>(
393
442
  operation: operation,
394
443
  ): fromRpc.ReturnValue<operation> {
395
444
  try {
396
- if (
397
- typeof operation.configVersion !== 'string' ||
398
- !Hex.validate(operation.configVersion)
399
- )
445
+ const version = operation.config?.version
446
+ if (typeof version !== 'string' || !Hex.validate(version))
400
447
  throw new InvalidOperationError({
401
- reason: 'configVersion must be a hexadecimal quantity',
448
+ reason: 'config.version must be a hexadecimal quantity',
402
449
  })
403
- const configVersion = Hex.toBigInt(operation.configVersion)
404
- if (Hex.fromNumber(configVersion) !== operation.configVersion)
450
+ const version_ = Hex.toBigInt(version)
451
+ if (Hex.fromNumber(version_) !== version)
405
452
  throw new InvalidOperationError({
406
- reason: 'configVersion must use canonical quantity encoding',
453
+ reason: 'config.version must use canonical quantity encoding',
407
454
  })
408
455
  return from({
409
456
  ...operation,
410
457
  config: MultisigConfig.fromRpc(operation.config),
411
- configVersion,
412
458
  } as Operation) as never
413
459
  } catch (cause) {
414
460
  if (cause instanceof InvalidOperationError) throw cause
@@ -448,7 +494,6 @@ export function toRpc<const operation extends Operation>(
448
494
  return {
449
495
  ...value,
450
496
  config: MultisigConfig.toRpc(value.config),
451
- configVersion: Hex.fromNumber(value.configVersion),
452
497
  } as never
453
498
  }
454
499
 
@@ -460,7 +505,7 @@ export declare namespace toRpc {
460
505
  : KeyAuthorizationRpc
461
506
 
462
507
  /** Error type for `toRpc`. */
463
- export type ErrorType = from.ErrorType | Hex.fromNumber.ErrorType
508
+ export type ErrorType = from.ErrorType | MultisigConfig.toRpc.ErrorType
464
509
  }
465
510
 
466
511
  /**
@@ -470,7 +515,6 @@ export declare namespace toRpc {
470
515
  */
471
516
  async function selectApprovals_internal(
472
517
  options: selectApprovals.Options,
473
- path: readonly string[],
474
518
  ): Promise<selectApprovals.ReturnValue> {
475
519
  const owners = new Map(
476
520
  options.config.owners.map((owner) => [
@@ -481,17 +525,14 @@ async function selectApprovals_internal(
481
525
  const groups = new Map<string, ApprovalGroup>()
482
526
  for (const serialized of options.approvals) {
483
527
  const signature = SignatureEnvelope.from(serialized)
484
- if (signature.type === 'keychain')
528
+ if (signature.type === 'keychain' || signature.type === 'multisig')
485
529
  throw new InvalidApprovalError({
486
- reason: 'keychain signatures cannot approve a multisig operation',
530
+ reason: 'only primitive signatures can approve a multisig operation',
487
531
  })
488
- const address =
489
- signature.type === 'multisig'
490
- ? signature.account
491
- : SignatureEnvelope.extractAddress({
492
- payload: options.hash,
493
- signature,
494
- })
532
+ const address = SignatureEnvelope.extractAddress({
533
+ payload: options.hash,
534
+ signature,
535
+ })
495
536
  const owner = owners.get(address.toLowerCase())
496
537
  if (!owner)
497
538
  throw new InvalidApprovalError({
@@ -511,78 +552,6 @@ async function selectApprovals_internal(
511
552
  const valid: SelectedApproval[] = []
512
553
  const retained: RetainedApproval[] = []
513
554
  for (const group of groups.values()) {
514
- const nested = group.signatures.filter(
515
- (signature) => signature.type === 'multisig',
516
- )
517
- if (nested.length > 0) {
518
- if (nested.length !== group.signatures.length)
519
- throw new InvalidApprovalError({
520
- reason: `owner ${group.address} has conflicting signature types`,
521
- })
522
- if (
523
- path.length >= MultisigConfig.maxNestingDepth ||
524
- path.includes(group.address.toLowerCase())
525
- )
526
- throw new InvalidApprovalError({
527
- reason: `nested multisig owner ${group.address} is invalid`,
528
- })
529
- if (!options.resolveConfig)
530
- throw new InvalidApprovalError({
531
- reason: `nested multisig owner ${group.address} requires a config resolver`,
532
- })
533
- const resolved = await options.resolveConfig({ account: group.address })
534
- const config = MultisigConfig.from(resolved.config)
535
- if (config.version !== resolved.version)
536
- throw new InvalidApprovalError({
537
- reason: `resolved config.version must equal version for nested multisig owner ${group.address}`,
538
- })
539
- const selected = await selectApprovals_internal(
540
- {
541
- account: group.address,
542
- approvals: nested.flatMap((signature) =>
543
- signature.signatures.map((approval) =>
544
- SignatureEnvelope.serialize(approval),
545
- ),
546
- ),
547
- config,
548
- hash: MultisigConfig.getSignPayload({
549
- account: group.address,
550
- config,
551
- payload: options.hash,
552
- }),
553
- resolveConfig: options.resolveConfig,
554
- },
555
- [...path, group.address.toLowerCase()],
556
- )
557
- retained.push({
558
- address: group.address,
559
- signature: SignatureEnvelope.serialize(
560
- SignatureEnvelope.from({
561
- account: group.address,
562
- config,
563
- signatures: selected.approvals.map((approval) =>
564
- SignatureEnvelope.from(approval),
565
- ),
566
- }),
567
- ),
568
- })
569
- if (selected.weight >= selected.threshold)
570
- valid.push({
571
- address: group.address,
572
- signature: SignatureEnvelope.serialize(
573
- SignatureEnvelope.from({
574
- account: group.address,
575
- config,
576
- signatures: selected.selectedApprovals.map((approval) =>
577
- SignatureEnvelope.from(approval),
578
- ),
579
- }),
580
- ),
581
- weight: group.weight,
582
- })
583
- continue
584
- }
585
-
586
555
  const signatures = group.signatures.map((signature) => {
587
556
  if (
588
557
  !SignatureEnvelope.verify(signature, {
@@ -651,7 +620,7 @@ type ApprovalGroup = {
651
620
  type RetainedApproval = {
652
621
  /** Configured owner address. */
653
622
  address: Address.Address
654
- /** Serialized primitive or normalized nested approval. */
623
+ /** Serialized primitive approval. */
655
624
  signature: SignatureEnvelope.Serialized
656
625
  }
657
626
 
@@ -692,16 +661,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
692
661
  throw new InvalidOperationError({ reason: 'account cannot be zero' })
693
662
  if (!Hash.validate(operation.hash))
694
663
  throw new InvalidOperationError({ reason: 'hash is invalid' })
695
- if (typeof operation.init !== 'boolean')
696
- throw new InvalidOperationError({ reason: 'init must be a boolean' })
697
- if (
698
- typeof operation.configVersion !== 'bigint' ||
699
- operation.configVersion < 0n ||
700
- operation.configVersion > maxConfigVersion
701
- )
702
- throw new InvalidOperationError({
703
- reason: 'configVersion must be an unsigned 64-bit integer',
704
- })
705
664
  assertInteger(operation.createdAt, 'createdAt')
706
665
  assertInteger(operation.updatedAt, 'updatedAt')
707
666
  if (operation.updatedAt < operation.createdAt)
@@ -781,19 +740,6 @@ function assertBase(operation: Operation, config: MultisigConfig.Config): void {
781
740
  reason:
782
741
  'weight is not reachable by signatureCount retained owner approvals',
783
742
  })
784
- if (operation.init) {
785
- if (operation.configVersion !== 0n)
786
- throw new InvalidOperationError({
787
- reason: 'bootstrap operations must use config version zero',
788
- })
789
- if (
790
- MultisigConfig.getAddress(config).toLowerCase() !==
791
- operation.account.toLowerCase()
792
- )
793
- throw new InvalidOperationError({
794
- reason: 'bootstrap config does not derive the operation account',
795
- })
796
- }
797
743
  }
798
744
 
799
745
  /**
@@ -981,8 +927,12 @@ function assertApproval(
981
927
  account: Address.Address,
982
928
  serialized: SignatureEnvelope.Serialized,
983
929
  config: MultisigConfig.Config,
984
- ): SignatureEnvelope.SignatureEnvelope {
930
+ ): SignatureEnvelope.Primitive {
985
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
+ })
986
936
  SignatureEnvelope.assert({
987
937
  account,
988
938
  config,
@@ -1010,8 +960,10 @@ function assertRetainedApprovals(
1010
960
  SignatureEnvelope.deserialize(approval),
1011
961
  )
1012
962
  for (const approval of selected) {
1013
- const index = retained.findIndex((candidate) =>
1014
- includesApproval(candidate, approval),
963
+ const index = retained.findIndex(
964
+ (candidate) =>
965
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
966
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1015
967
  )
1016
968
  if (index === -1)
1017
969
  throw new InvalidOperationError({
@@ -1035,8 +987,10 @@ function assertSelectedApprovals(
1035
987
  SignatureEnvelope.deserialize(approval),
1036
988
  )
1037
989
  for (const approval of selected) {
1038
- const index = retained.findIndex((candidate) =>
1039
- includesApproval(candidate, approval),
990
+ const index = retained.findIndex(
991
+ (candidate) =>
992
+ SignatureEnvelope.serialize(candidate).toLowerCase() ===
993
+ SignatureEnvelope.serialize(approval).toLowerCase(),
1040
994
  )
1041
995
  if (index === -1)
1042
996
  throw new InvalidOperationError({
@@ -1067,36 +1021,6 @@ function assertSelectedApprovals(
1067
1021
  }
1068
1022
  }
1069
1023
 
1070
- /**
1071
- * Checks whether a selected approval is contained in a retained approval tree.
1072
- *
1073
- * @internal
1074
- */
1075
- function includesApproval(
1076
- retained: SignatureEnvelope.SignatureEnvelope,
1077
- selected: SignatureEnvelope.SignatureEnvelope,
1078
- ): boolean {
1079
- if (retained.type !== 'multisig' || selected.type !== 'multisig')
1080
- return (
1081
- SignatureEnvelope.serialize(retained).toLowerCase() ===
1082
- SignatureEnvelope.serialize(selected).toLowerCase()
1083
- )
1084
- if (retained.account.toLowerCase() !== selected.account.toLowerCase())
1085
- return false
1086
- if (!sameConfig(retained.config, selected.config)) return false
1087
- let index = 0
1088
- for (const approval of selected.signatures) {
1089
- while (
1090
- index < retained.signatures.length &&
1091
- !includesApproval(retained.signatures[index]!, approval)
1092
- )
1093
- index++
1094
- if (index === retained.signatures.length) return false
1095
- index++
1096
- }
1097
- return true
1098
- }
1099
-
1100
1024
  /**
1101
1025
  * Checks whether exactly `signatureCount` retained owners can produce `weight`.
1102
1026
  *
@@ -0,0 +1,33 @@
1
+ import { expectTypeOf, test } from 'vp/test'
2
+ import * as MultisigSimulation from './MultisigSimulation.js'
3
+
4
+ const rpc = {
5
+ approvals: [
6
+ {
7
+ keyType: 'webAuthn',
8
+ keyData: '0x0102030405',
9
+ owner: '0x1111111111111111111111111111111111111111',
10
+ },
11
+ ],
12
+ config:
13
+ '0xf83ba000000000000000000000000000000000000000000000000000000000000000008001d7d694111111111111111111111111111111111111111101',
14
+ } as const satisfies MultisigSimulation.Rpc
15
+
16
+ test('fromRpc returns a domain spec', () => {
17
+ const spec = MultisigSimulation.fromRpc(rpc)
18
+
19
+ expectTypeOf(spec).toEqualTypeOf<MultisigSimulation.Spec>()
20
+ expectTypeOf(spec.config.version).toEqualTypeOf<bigint>()
21
+ })
22
+
23
+ test('RPC specs use encoded configurations', () => {
24
+ expectTypeOf<
25
+ MultisigSimulation.Rpc['config']
26
+ >().toEqualTypeOf<`0x${string}`>()
27
+ })
28
+
29
+ test('toRpc returns an RPC spec', () => {
30
+ expectTypeOf(
31
+ MultisigSimulation.toRpc(MultisigSimulation.fromRpc(rpc)),
32
+ ).toEqualTypeOf<MultisigSimulation.Rpc>()
33
+ })