viem 2.56.5 → 2.56.7

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 (159) hide show
  1. package/CHANGELOG.md +28 -0
  2. package/_cjs/actions/public/simulateCalls.js +6 -1
  3. package/_cjs/actions/public/simulateCalls.js.map +1 -1
  4. package/_cjs/chains/definitions/arc.js +19 -1
  5. package/_cjs/chains/definitions/arc.js.map +1 -1
  6. package/_cjs/chains/definitions/eCanna.js +23 -0
  7. package/_cjs/chains/definitions/eCanna.js.map +1 -0
  8. package/_cjs/chains/definitions/eCannaTestnet.js +24 -0
  9. package/_cjs/chains/definitions/eCannaTestnet.js.map +1 -0
  10. package/_cjs/chains/definitions/hyveChain.js +29 -0
  11. package/_cjs/chains/definitions/hyveChain.js.map +1 -0
  12. package/_cjs/chains/definitions/robinhood.js +5 -1
  13. package/_cjs/chains/definitions/robinhood.js.map +1 -1
  14. package/_cjs/chains/definitions/zagros.js +33 -0
  15. package/_cjs/chains/definitions/zagros.js.map +1 -0
  16. package/_cjs/chains/index.js +20 -12
  17. package/_cjs/chains/index.js.map +1 -1
  18. package/_cjs/errors/version.js +1 -1
  19. package/_cjs/op-stack/chains.js +3 -1
  20. package/_cjs/op-stack/chains.js.map +1 -1
  21. package/_cjs/tempo/Account.js +21 -30
  22. package/_cjs/tempo/Account.js.map +1 -1
  23. package/_cjs/tempo/Addresses.js +2 -1
  24. package/_cjs/tempo/Addresses.js.map +1 -1
  25. package/_cjs/tempo/Multisig.js +14 -107
  26. package/_cjs/tempo/Multisig.js.map +1 -1
  27. package/_cjs/tempo/Transaction.js +5 -2
  28. package/_cjs/tempo/Transaction.js.map +1 -1
  29. package/_cjs/tempo/actions/accessKey.js +2 -1
  30. package/_cjs/tempo/actions/accessKey.js.map +1 -1
  31. package/_cjs/tempo/chainConfig.js +3 -31
  32. package/_cjs/tempo/chainConfig.js.map +1 -1
  33. package/_cjs/tempo/multisig/Config.js +8 -4
  34. package/_cjs/tempo/multisig/Config.js.map +1 -1
  35. package/_cjs/tempo/multisig/Signature.js +11 -0
  36. package/_cjs/tempo/multisig/Signature.js.map +1 -0
  37. package/_cjs/tokens/definitions/cirbtc.js +1 -0
  38. package/_cjs/tokens/definitions/cirbtc.js.map +1 -1
  39. package/_cjs/tokens/definitions/eurc.js +1 -0
  40. package/_cjs/tokens/definitions/eurc.js.map +1 -1
  41. package/_cjs/tokens/definitions/usdc.js +1 -0
  42. package/_cjs/tokens/definitions/usdc.js.map +1 -1
  43. package/_cjs/utils/buildRequest.js +2 -0
  44. package/_cjs/utils/buildRequest.js.map +1 -1
  45. package/_esm/actions/public/simulateCalls.js +7 -1
  46. package/_esm/actions/public/simulateCalls.js.map +1 -1
  47. package/_esm/chains/definitions/arc.js +19 -1
  48. package/_esm/chains/definitions/arc.js.map +1 -1
  49. package/_esm/chains/definitions/eCanna.js +20 -0
  50. package/_esm/chains/definitions/eCanna.js.map +1 -0
  51. package/_esm/chains/definitions/eCannaTestnet.js +21 -0
  52. package/_esm/chains/definitions/eCannaTestnet.js.map +1 -0
  53. package/_esm/chains/definitions/hyveChain.js +26 -0
  54. package/_esm/chains/definitions/hyveChain.js.map +1 -0
  55. package/_esm/chains/definitions/robinhood.js +5 -1
  56. package/_esm/chains/definitions/robinhood.js.map +1 -1
  57. package/_esm/chains/definitions/zagros.js +30 -0
  58. package/_esm/chains/definitions/zagros.js.map +1 -0
  59. package/_esm/chains/index.js +4 -0
  60. package/_esm/chains/index.js.map +1 -1
  61. package/_esm/errors/version.js +1 -1
  62. package/_esm/op-stack/chains.js +1 -0
  63. package/_esm/op-stack/chains.js.map +1 -1
  64. package/_esm/tempo/Account.js +21 -30
  65. package/_esm/tempo/Account.js.map +1 -1
  66. package/_esm/tempo/Addresses.js +2 -0
  67. package/_esm/tempo/Addresses.js.map +1 -1
  68. package/_esm/tempo/Multisig.js +15 -111
  69. package/_esm/tempo/Multisig.js.map +1 -1
  70. package/_esm/tempo/Transaction.js +5 -2
  71. package/_esm/tempo/Transaction.js.map +1 -1
  72. package/_esm/tempo/actions/accessKey.js +2 -1
  73. package/_esm/tempo/actions/accessKey.js.map +1 -1
  74. package/_esm/tempo/chainConfig.js +3 -31
  75. package/_esm/tempo/chainConfig.js.map +1 -1
  76. package/_esm/tempo/multisig/Config.js +10 -5
  77. package/_esm/tempo/multisig/Config.js.map +1 -1
  78. package/_esm/tempo/multisig/Signature.js +9 -0
  79. package/_esm/tempo/multisig/Signature.js.map +1 -0
  80. package/_esm/tokens/definitions/cirbtc.js +1 -0
  81. package/_esm/tokens/definitions/cirbtc.js.map +1 -1
  82. package/_esm/tokens/definitions/eurc.js +1 -0
  83. package/_esm/tokens/definitions/eurc.js.map +1 -1
  84. package/_esm/tokens/definitions/usdc.js +1 -0
  85. package/_esm/tokens/definitions/usdc.js.map +1 -1
  86. package/_esm/utils/buildRequest.js +7 -0
  87. package/_esm/utils/buildRequest.js.map +1 -1
  88. package/_types/actions/public/simulateCalls.d.ts.map +1 -1
  89. package/_types/chains/definitions/arc.d.ts +13 -21
  90. package/_types/chains/definitions/arc.d.ts.map +1 -1
  91. package/_types/chains/definitions/eCanna.d.ts +51 -0
  92. package/_types/chains/definitions/eCanna.d.ts.map +1 -0
  93. package/_types/chains/definitions/eCannaTestnet.d.ts +51 -0
  94. package/_types/chains/definitions/eCannaTestnet.d.ts.map +1 -0
  95. package/_types/chains/definitions/hyveChain.d.ts +49 -0
  96. package/_types/chains/definitions/hyveChain.d.ts.map +1 -0
  97. package/_types/chains/definitions/robinhood.d.ts +2 -1
  98. package/_types/chains/definitions/robinhood.d.ts.map +1 -1
  99. package/_types/chains/definitions/tempo.d.ts +2 -4
  100. package/_types/chains/definitions/tempo.d.ts.map +1 -1
  101. package/_types/chains/definitions/tempoDevnet.d.ts +2 -4
  102. package/_types/chains/definitions/tempoDevnet.d.ts.map +1 -1
  103. package/_types/chains/definitions/tempoLocalnet.d.ts +2 -4
  104. package/_types/chains/definitions/tempoLocalnet.d.ts.map +1 -1
  105. package/_types/chains/definitions/tempoModerato.d.ts +2 -4
  106. package/_types/chains/definitions/tempoModerato.d.ts.map +1 -1
  107. package/_types/chains/definitions/zagros.d.ts +50 -0
  108. package/_types/chains/definitions/zagros.d.ts.map +1 -0
  109. package/_types/chains/index.d.ts +4 -0
  110. package/_types/chains/index.d.ts.map +1 -1
  111. package/_types/errors/version.d.ts +1 -1
  112. package/_types/op-stack/chains.d.ts +1 -0
  113. package/_types/op-stack/chains.d.ts.map +1 -1
  114. package/_types/tempo/Account.d.ts +9 -3
  115. package/_types/tempo/Account.d.ts.map +1 -1
  116. package/_types/tempo/Addresses.d.ts +2 -0
  117. package/_types/tempo/Addresses.d.ts.map +1 -1
  118. package/_types/tempo/Multisig.d.ts.map +1 -1
  119. package/_types/tempo/Transaction.d.ts +16 -7
  120. package/_types/tempo/Transaction.d.ts.map +1 -1
  121. package/_types/tempo/actions/accessKey.d.ts +2 -2
  122. package/_types/tempo/actions/accessKey.d.ts.map +1 -1
  123. package/_types/tempo/chainConfig.d.ts +1 -3
  124. package/_types/tempo/chainConfig.d.ts.map +1 -1
  125. package/_types/tempo/multisig/Config.d.ts.map +1 -1
  126. package/_types/tempo/multisig/Signature.d.ts +4 -0
  127. package/_types/tempo/multisig/Signature.d.ts.map +1 -0
  128. package/_types/tokens/definitions/cirbtc.d.ts +1 -0
  129. package/_types/tokens/definitions/cirbtc.d.ts.map +1 -1
  130. package/_types/tokens/definitions/eurc.d.ts +1 -0
  131. package/_types/tokens/definitions/eurc.d.ts.map +1 -1
  132. package/_types/tokens/definitions/usdc.d.ts +1 -0
  133. package/_types/tokens/definitions/usdc.d.ts.map +1 -1
  134. package/_types/tokens/sets.d.ts +4 -0
  135. package/_types/tokens/sets.d.ts.map +1 -1
  136. package/_types/utils/buildRequest.d.ts.map +1 -1
  137. package/actions/public/simulateCalls.ts +8 -1
  138. package/chains/definitions/arc.ts +19 -1
  139. package/chains/definitions/eCanna.ts +20 -0
  140. package/chains/definitions/eCannaTestnet.ts +21 -0
  141. package/chains/definitions/hyveChain.ts +26 -0
  142. package/chains/definitions/robinhood.ts +5 -1
  143. package/chains/definitions/zagros.ts +30 -0
  144. package/chains/index.ts +4 -0
  145. package/errors/version.ts +1 -1
  146. package/op-stack/chains.ts +1 -0
  147. package/package.json +2 -2
  148. package/tempo/Account.ts +32 -43
  149. package/tempo/Addresses.ts +3 -0
  150. package/tempo/Multisig.ts +19 -132
  151. package/tempo/Transaction.ts +24 -9
  152. package/tempo/actions/accessKey.ts +4 -3
  153. package/tempo/chainConfig.ts +3 -42
  154. package/tempo/multisig/Config.ts +15 -5
  155. package/tempo/multisig/Signature.ts +11 -0
  156. package/tokens/definitions/cirbtc.ts +1 -0
  157. package/tokens/definitions/eurc.ts +1 -0
  158. package/tokens/definitions/usdc.ts +1 -0
  159. package/utils/buildRequest.ts +6 -0
package/tempo/Account.ts CHANGED
@@ -24,7 +24,9 @@ import { keccak256 } from '../utils/hash/keccak256.js'
24
24
  import { hashMessage } from '../utils/signature/hashMessage.js'
25
25
  import { hashTypedData } from '../utils/signature/hashTypedData.js'
26
26
  import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js'
27
+ import { nativeMultisigFactory } from './Addresses.js'
27
28
  import type { KeyAuthorizationManager } from './KeyAuthorizationManager.js'
29
+ import { parseApproval } from './multisig/Signature.js'
28
30
  import * as Transaction from './Transaction.js'
29
31
 
30
32
  export type Account_base<source extends string = string> = RequiredBy<
@@ -284,7 +286,7 @@ export declare namespace fromSecp256k1 {
284
286
  *
285
287
  * Owners can be accounts or addresses directly, or weighted `{ owner, weight }`
286
288
  * entries. Direct owners default to weight `1`, and `threshold` defaults to `1`.
287
- * Local owner accounts are retained for signing, including nested multisigs.
289
+ * Local owner accounts are retained for signing, using primitive signatures.
288
290
  * Configs containing only owner addresses require external approvals through
289
291
  * `signatures`.
290
292
  *
@@ -359,11 +361,13 @@ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount {
359
361
  (() => {
360
362
  if (typeof value === 'string') return value
361
363
  if (value.address === undefined || value.address === 'infer')
362
- return MultisigConfig.getAddress(config!)
364
+ return MultisigConfig.getAddress(config!, {
365
+ factory: nativeMultisigFactory,
366
+ })
363
367
  return value.address
364
368
  })(),
365
369
  )
366
- const ownerAccounts = (() => {
370
+ const ownerAccounts: LocalAccount[] = (() => {
367
371
  if (!configInput) return []
368
372
  return configInput.owners.flatMap((value) => {
369
373
  const owner =
@@ -371,6 +375,12 @@ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount {
371
375
  return typeof owner === 'string' ? [] : [parseAccount(owner)]
372
376
  })
373
377
  })()
378
+ if (
379
+ ownerAccounts.some(
380
+ (owner) => owner.source === 'multisig' || isAccessKeyAccount(owner),
381
+ )
382
+ )
383
+ throw new Error('Multisig owners must use primitive signatures.')
374
384
  const owners =
375
385
  config?.owners.flatMap(({ owner }) => {
376
386
  const account = ownerAccounts.find((account) =>
@@ -403,7 +413,7 @@ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount {
403
413
  throw new Error(
404
414
  'A local owner account is required to approve a multisig transaction.',
405
415
  )
406
- if (owner.source !== 'root' && owner.source !== 'multisig')
416
+ if (owner.source !== 'root')
407
417
  throw new Error(
408
418
  'A Tempo owner account is required to approve a multisig transaction.',
409
419
  )
@@ -422,29 +432,16 @@ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount {
422
432
  }
423
433
  const payload = keccak256(await serializer(presign as never))
424
434
  const simulation = request.multisigSimulation
425
- const requestAccount = simulation?.account ?? address
426
-
427
- if (!Address.isEqual(requestAccount, address)) {
428
- if (!simulation)
429
- throw new Error('A multisig config is required for local signing.')
430
- const parentConfig = MultisigConfig.from(simulation.config)
431
- const parentDigest = MultisigConfig.getSignPayload({
432
- account: requestAccount,
433
- config: parentConfig,
434
- payload,
435
- })
436
- return SignatureEnvelope.serialize(
437
- await signMultisig(account, {
438
- payload: parentDigest,
439
- }),
435
+ if (request.from && !Address.isEqual(request.from, address))
436
+ throw new Error(
437
+ 'Multisig account does not match the transaction sender.',
440
438
  )
441
- }
442
439
 
443
440
  const signature = await signMultisig(account, {
444
441
  config: simulation?.config,
445
442
  payload,
446
443
  signatures: request.signatures?.map((signature) =>
447
- SignatureEnvelope.from(signature),
444
+ parseApproval(signature),
448
445
  ),
449
446
  })
450
447
  return (await serializer(
@@ -494,9 +491,11 @@ export declare namespace fromMultisig {
494
491
  /** Multisig owner account or address, optionally with an explicit weight. */
495
492
  export type Owner =
496
493
  | Address.Address
497
- | LocalAccount
494
+ | (LocalAccount & { accessKeyAddress?: never; owners?: never })
498
495
  | (Omit<MultisigConfig.Owner, 'owner'> & {
499
- owner: Address.Address | LocalAccount
496
+ owner:
497
+ | Address.Address
498
+ | (LocalAccount & { accessKeyAddress?: never; owners?: never })
500
499
  })
501
500
 
502
501
  /** Parameters for {@link fromMultisig}. */
@@ -523,7 +522,7 @@ async function signMultisig(
523
522
  parameters: {
524
523
  config?: MultisigConfig.Config | undefined
525
524
  payload: Hex.Hex
526
- signatures?: readonly SignatureEnvelope.SignatureEnvelope[] | undefined
525
+ signatures?: readonly SignatureEnvelope.Primitive[] | undefined
527
526
  },
528
527
  ): Promise<SignatureEnvelope.Multisig> {
529
528
  const { config, payload, signatures: providedSignatures = [] } = parameters
@@ -558,6 +557,7 @@ async function signMultisig(
558
557
  a.owner.toLowerCase().localeCompare(b.owner.toLowerCase()),
559
558
  )
560
559
  for (const owner of owners) {
560
+ if (weight >= Number(currentConfig.threshold)) break
561
561
  const address = owner.owner.toLowerCase() as Address.Address
562
562
  if (signedOwners.has(address)) continue
563
563
  const ownerAccount = account.owners.find((account) =>
@@ -565,19 +565,9 @@ async function signMultisig(
565
565
  )
566
566
  if (!ownerAccount) continue
567
567
 
568
- if (isMultisigAccount(ownerAccount)) {
569
- signatures.push(
570
- await signMultisig(ownerAccount, {
571
- payload: digest,
572
- }),
573
- )
574
- } else {
575
- if (!ownerAccount.sign)
576
- throw new Error('Multisig owner account cannot sign.')
577
- signatures.push(
578
- SignatureEnvelope.from(await ownerAccount.sign({ hash: digest })),
579
- )
580
- }
568
+ if (!ownerAccount.sign)
569
+ throw new Error('Multisig owner account cannot sign.')
570
+ signatures.push(parseApproval(await ownerAccount.sign({ hash: digest })))
581
571
 
582
572
  signedOwners.add(address)
583
573
  weight += Number(owner.weight)
@@ -856,9 +846,7 @@ export async function signKeyAuthorization(
856
846
  await signMultisig(account, {
857
847
  config: multisigState.config,
858
848
  payload: hash,
859
- signatures: signatures?.map((signature) =>
860
- SignatureEnvelope.from(signature),
861
- ),
849
+ signatures: signatures?.map((signature) => parseApproval(signature)),
862
850
  }),
863
851
  )
864
852
  }
@@ -982,16 +970,17 @@ function fromBase(parameters: fromBase.Parameters): Account_base {
982
970
  // primitive signature over the multisig owner approval digest, instead of
983
971
  // a full serialized transaction. Approvals are combined later in
984
972
  // `sendTransaction({ signatures })`.
985
- const { multisigSimulation } = transaction as {
973
+ const { multisigSimulation, from } = transaction as {
974
+ from?: Address.Address
986
975
  multisigSimulation?: {
987
- account: Address.Address
988
976
  config: MultisigConfig.Config
989
977
  }
990
978
  }
991
979
  if (multisigSimulation) {
980
+ if (!from) throw new Error('A multisig sender is required for signing.')
992
981
  const config = MultisigConfig.from(multisigSimulation.config)
993
982
  const digest = MultisigConfig.getSignPayload({
994
- account: multisigSimulation.account,
983
+ account: from,
995
984
  config,
996
985
  payload,
997
986
  })
@@ -14,6 +14,9 @@ export const accountRegistrar = '0x7702ac0000000000000000000000000000000000'
14
14
  export const addressRegistry = '0xfdc0000000000000000000000000000000000000'
15
15
  export const feeManager = '0xfeec000000000000000000000000000000000000'
16
16
  export const nativeMultisig = '0xAACC000000000000000000000000000000000000'
17
+ /** Temporary recovery factory until the production address is finalized. */
18
+ export const nativeMultisigFactory =
19
+ '0x7171717171717171717171717171717171717171'
17
20
  export const nonceManager = '0x4e4F4E4345000000000000000000000000000000'
18
21
  export const pathUsd = '0x20c0000000000000000000000000000000000000'
19
22
  export const receivePolicyGuard = '0xB10C000000000000000000000000000000000000'
package/tempo/Multisig.ts CHANGED
@@ -140,13 +140,12 @@ export function handleRequest(
140
140
  }
141
141
  }
142
142
  if (operation.status === 'pending')
143
- return await toTransaction(client, operation, parameters.store)
143
+ return await toTransaction(client, operation)
144
144
  const transactionHash = await getSubmittedTransactionHash(
145
145
  parameters.store,
146
146
  operation,
147
147
  )
148
- if (!transactionHash)
149
- return await toTransaction(client, operation, parameters.store)
148
+ if (!transactionHash) return await toTransaction(client, operation)
150
149
  const transaction = await next(
151
150
  {
152
151
  ...request,
@@ -155,7 +154,7 @@ export function handleRequest(
155
154
  requestOptions,
156
155
  )
157
156
  if (!transaction || typeof transaction !== 'object')
158
- return await toTransaction(client, operation, parameters.store)
157
+ return await toTransaction(client, operation)
159
158
  const success =
160
159
  operation.status === 'submitting'
161
160
  ? await completeSubmission(
@@ -306,23 +305,16 @@ async function submit(options: submit.Options) {
306
305
  const existingApprovals = existing
307
306
  ? await selectApprovals({
308
307
  account: signature.account,
309
- client: options.client,
310
308
  config,
311
- discardInvalidNested: true,
312
309
  hash: operationHash,
313
310
  approvals: existing.approvals,
314
- blockNumber,
315
- store: options.store,
316
311
  })
317
312
  : undefined
318
313
  const approvals = await selectApprovals({
319
314
  account: signature.account,
320
- client: options.client,
321
315
  config,
322
316
  hash: operationHash,
323
317
  approvals: [...(existingApprovals?.approvals ?? []), ...incoming],
324
- blockNumber,
325
- store: options.store,
326
318
  })
327
319
  return MultisigOperation.from({
328
320
  account: signature.account,
@@ -427,12 +419,9 @@ async function submit(options: submit.Options) {
427
419
  })
428
420
  const finalApprovals = await selectApprovals({
429
421
  account: claim.account,
430
- blockNumber,
431
- client: options.client,
432
422
  config: claim.config,
433
423
  hash: claim.hash,
434
424
  approvals: claim.approvals,
435
- store: options.store,
436
425
  })
437
426
  const final = MultisigOperation.serializeTransaction(claim, {
438
427
  approvals: finalApprovals.selectedApprovals,
@@ -703,12 +692,8 @@ async function approveKeyAuthorization(
703
692
  ? await selectApprovals({
704
693
  account: approval.account,
705
694
  approvals: existing.approvals,
706
- blockNumber,
707
- client: options.client,
708
695
  config: approval.config,
709
- discardInvalidNested: true,
710
696
  hash: approval.hash,
711
- store: options.store,
712
697
  })
713
698
  : undefined
714
699
  const approvals = await selectApprovals({
@@ -717,11 +702,8 @@ async function approveKeyAuthorization(
717
702
  ...(existingApprovals?.approvals ?? []),
718
703
  ...approval.approvals,
719
704
  ],
720
- blockNumber,
721
- client: options.client,
722
705
  config: approval.config,
723
706
  hash: approval.hash,
724
- store: options.store,
725
707
  })
726
708
  if (
727
709
  existing &&
@@ -804,16 +786,18 @@ function assertConfig(options: {
804
786
  const { account, commitment, config } = options
805
787
  if (
806
788
  config.version === 0n &&
807
- MultisigConfig.getAddress(config).toLowerCase() !== account.toLowerCase()
789
+ MultisigConfig.getAddress(config, {
790
+ factory: Addresses.nativeMultisigFactory,
791
+ }).toLowerCase() !== account.toLowerCase()
808
792
  )
809
793
  throw new RpcResponse.InvalidParamsError({
810
794
  message: 'Initial multisig config does not match the multisig account.',
811
795
  })
812
- const expected = (() => {
813
- if (config.version === 0n) return Hex.fromNumber(0, { size: 32 })
814
- return MultisigConfig.getCommitment(config)
815
- })()
816
- if (commitment.toLowerCase() !== expected.toLowerCase())
796
+ if (config.version === 0n && Hex.toBigInt(commitment) === 0n) return
797
+ if (
798
+ commitment.toLowerCase() !==
799
+ MultisigConfig.getCommitment(config).toLowerCase()
800
+ )
817
801
  throw new RpcResponse.InvalidParamsError({
818
802
  message: `Multisig config does not match account ${account}.`,
819
803
  })
@@ -904,82 +888,12 @@ declare namespace cacheNextConfigs {
904
888
  }
905
889
  }
906
890
 
907
- /** Selects approvals after validating every nested config. */
908
- // biome-ignore lint/correctness/noUnusedVariables: _
909
- async function selectApprovals(options: selectApprovals.Options) {
910
- const validation = new Map<string, Promise<Hex.Hex>>()
911
- let validationCount = 0
912
- const select = async (approvals: readonly SignatureEnvelope.Serialized[]) => {
913
- const result = await MultisigOperation.selectApprovals({
914
- account: options.account,
915
- approvals,
916
- config: options.config,
917
- hash: options.hash,
918
- })
919
- const configs = new Map<
920
- string,
921
- { account: Address; config: MultisigConfig.Config }
922
- >()
923
- const visit = (signature: SignatureEnvelope.SignatureEnvelope) => {
924
- if (signature.type !== 'multisig') return
925
- const config = MultisigConfig.from(signature.config)
926
- const key = signature.account.toLowerCase()
927
- configs.set(key, { account: signature.account, config })
928
- for (const approval of signature.signatures) visit(approval)
929
- }
930
- for (const approval of result.approvals)
931
- visit(SignatureEnvelope.from(approval))
932
-
933
- for (const { account, config } of configs.values()) {
934
- const key = `${account.toLowerCase()}:${MultisigConfig.getCommitment(config)}`
935
- const pending = (() => {
936
- const existing = validation.get(key)
937
- if (existing) return existing
938
- validationCount++
939
- if (validationCount > MultisigConfig.maxOwners)
940
- throw new RpcResponse.InvalidParamsError({
941
- message: 'Multisig approval validation exceeds the owner limit.',
942
- })
943
- const pending = validateConfig({
944
- account,
945
- blockNumber: options.blockNumber,
946
- client: options.client,
947
- config,
948
- })
949
- validation.set(key, pending)
950
- return pending
951
- })()
952
- const commitment = await pending
953
- await ConfigStore.write(options.store, {
954
- address: account,
955
- commitment,
956
- config,
957
- })
958
- }
959
- return result
960
- }
891
+ /** Validates primitive approvals and translates invalid input into an RPC error. */
892
+ async function selectApprovals(
893
+ options: MultisigOperation.selectApprovals.Options,
894
+ ) {
961
895
  try {
962
- if (!options.discardInvalidNested) return await select(options.approvals)
963
-
964
- const approvals: SignatureEnvelope.Serialized[] = []
965
- for (const approval of options.approvals) {
966
- if (SignatureEnvelope.from(approval).type !== 'multisig') {
967
- approvals.push(approval)
968
- continue
969
- }
970
- try {
971
- const retained = (await select([approval])).approvals[0]
972
- if (retained) approvals.push(retained)
973
- } catch (error) {
974
- if (
975
- error instanceof MultisigOperation.InvalidApprovalError ||
976
- error instanceof RpcResponse.InvalidParamsError
977
- )
978
- continue
979
- throw error
980
- }
981
- }
982
- return await select(approvals)
896
+ return await MultisigOperation.selectApprovals(options)
983
897
  } catch (error) {
984
898
  if (error instanceof MultisigOperation.InvalidApprovalError)
985
899
  throw new RpcResponse.InvalidParamsError({ message: error.shortMessage })
@@ -987,20 +901,6 @@ async function selectApprovals(options: selectApprovals.Options) {
987
901
  }
988
902
  }
989
903
 
990
- declare namespace selectApprovals {
991
- /** Options for {@link selectApprovals}. */
992
- export type Options = MultisigOperation.selectApprovals.Options & {
993
- /** Block used to validate every config. */
994
- blockNumber: bigint
995
- /** Client used to validate nested multisig configurations. */
996
- client: ReturnType<typeof createClient>
997
- /** Discards stored nested approvals invalidated by a child configuration change. */
998
- discardInvalidNested?: boolean | undefined
999
- /** Shared multisig store. */
1000
- store: Store.Store
1001
- }
1002
- }
1003
-
1004
904
  /** Returns an existing successful operation through the requested send method. */
1005
905
  async function submittedResult(
1006
906
  options: submit.Options,
@@ -1186,7 +1086,6 @@ function mergeTransaction(existing: Hex.Hex | undefined, incoming: Hex.Hex) {
1186
1086
  async function toTransaction(
1187
1087
  client: ReturnType<typeof createClient>,
1188
1088
  operation: MultisigOperation.TransactionOperation,
1189
- store: Store.Store,
1190
1089
  ) {
1191
1090
  const approvals = await (async () => {
1192
1091
  if (operation.status !== 'pending')
@@ -1206,12 +1105,8 @@ async function toTransaction(
1206
1105
  return await selectApprovals({
1207
1106
  account: operation.account,
1208
1107
  approvals: operation.approvals,
1209
- blockNumber,
1210
- client,
1211
1108
  config: operation.config,
1212
- discardInvalidNested: true,
1213
1109
  hash: operation.hash,
1214
- store,
1215
1110
  })
1216
1111
  })()
1217
1112
  const current =
@@ -1224,17 +1119,9 @@ async function toTransaction(
1224
1119
  weight: approvals.weight,
1225
1120
  })
1226
1121
  : operation
1227
- const serialized = MultisigOperation.serializeTransaction(
1228
- approvals.approvals.length > 0 ? current : operation,
1229
- {
1230
- // Keep one stored approval in the synthetic envelope when every nested
1231
- // approval became stale. The next submission replaces it before broadcast.
1232
- approvals:
1233
- approvals.selectedApprovals.length > 0
1234
- ? approvals.selectedApprovals
1235
- : operation.approvals.slice(0, 1),
1236
- },
1237
- )
1122
+ const serialized = MultisigOperation.serializeTransaction(current, {
1123
+ approvals: approvals.selectedApprovals,
1124
+ })
1238
1125
  const transaction = deserialize(serialized)
1239
1126
  return {
1240
1127
  ...ox_Transaction.toRpc(
@@ -42,7 +42,8 @@ import {
42
42
  parseTransaction as viem_parseTransaction,
43
43
  } from '../utils/transaction/parseTransaction.js'
44
44
  import { serializeTransaction as viem_serializeTransaction } from '../utils/transaction/serializeTransaction.js'
45
- import type { MultisigAccount, RootAccount } from './Account.js'
45
+ import type { RootAccount } from './Account.js'
46
+ import { parseApproval } from './multisig/Signature.js'
46
47
 
47
48
  export type Transaction<
48
49
  bigintType = bigint,
@@ -135,7 +136,7 @@ export type TransactionRequestTempo<
135
136
  keyAuthorization?: KeyAuthorization.Signed<quantity, index> | undefined
136
137
  multisigSimulation?: MultisigSimulation.Spec | undefined
137
138
  nonceKey?: 'expiring' | quantity | undefined
138
- owner?: MultisigAccount | RootAccount | undefined
139
+ owner?: RootAccount | undefined
139
140
  signatures?: readonly SignatureEnvelope.Serialized[] | undefined
140
141
  validBefore?: index | undefined
141
142
  validAfter?: index | undefined
@@ -155,17 +156,30 @@ export type TransactionSerializableTempo<
155
156
  chainId: number
156
157
  feeToken?: Address | bigint | undefined
157
158
  feePayerSignature?: viem_Signature | null | undefined
158
- from?: Address | undefined
159
159
  keyAuthorization?: KeyAuthorization.Signed<quantity, index> | undefined
160
- multisigSimulation?: MultisigSimulation.Spec | undefined
161
160
  nonceKey?: quantity | undefined
162
- owner?: MultisigAccount | RootAccount | undefined
161
+ owner?: RootAccount | undefined
163
162
  signature?: SignatureEnvelope.SignatureEnvelope<quantity, index> | undefined
164
- signatures?: readonly SignatureEnvelope.Serialized[] | undefined
165
163
  validBefore?: index | undefined
166
164
  validAfter?: index | undefined
167
165
  type?: 'tempo' | undefined
168
- }
166
+ } & (
167
+ | {
168
+ from: Address
169
+ multisigSimulation?: MultisigSimulation.Spec | undefined
170
+ signatures?: readonly SignatureEnvelope.Serialized[] | undefined
171
+ }
172
+ | {
173
+ from?: Address | undefined
174
+ multisigSimulation?: undefined
175
+ signatures?: readonly SignatureEnvelope.Serialized[] | undefined
176
+ }
177
+ | {
178
+ from?: Address | undefined
179
+ multisigSimulation?: MultisigSimulation.Spec | undefined
180
+ signatures?: undefined
181
+ }
182
+ )
169
183
 
170
184
  export type TransactionSerialized<
171
185
  type extends TransactionType = TransactionType,
@@ -381,10 +395,11 @@ async function serializeTempo(
381
395
 
382
396
  const payload = TxTempo.getSignPayload(TxTempo.from(transaction_sender_ox))
383
397
  const signatures = transaction.signatures.map((approval) =>
384
- SignatureEnvelope.from(approval),
398
+ parseApproval(approval),
385
399
  )
386
400
  const config = MultisigConfig.from(multisigSimulation.config)
387
- const account = multisigSimulation.account
401
+ const account = transaction.from
402
+ if (!account) throw new Error('A multisig sender is required for signing.')
388
403
  const sorted = SignatureEnvelope.sortMultisigApprovals({
389
404
  account,
390
405
  config,
@@ -48,6 +48,7 @@ import type {
48
48
  WriteParameters,
49
49
  } from '../internal/types.js'
50
50
  import { defineCall } from '../internal/utils.js'
51
+ import { parseApproval } from '../multisig/Signature.js'
51
52
  import type { TransactionReceipt } from '../Transaction.js'
52
53
  import { getConfig } from './multisig.js'
53
54
 
@@ -1240,7 +1241,7 @@ export async function signAuthorization<
1240
1241
  signature: SignatureEnvelope.from({
1241
1242
  account: account.address,
1242
1243
  config,
1243
- signatures: [SignatureEnvelope.from(signature)],
1244
+ signatures: [parseApproval(signature)],
1244
1245
  }),
1245
1246
  type,
1246
1247
  ...(witness ? { witness } : {}),
@@ -1323,7 +1324,7 @@ export namespace signAuthorization {
1323
1324
  /** Multisig account being authorized. */
1324
1325
  account: Address | MultisigAccount
1325
1326
  /** Local owner that approves the authorization. */
1326
- owner: RootAccount | MultisigAccount
1327
+ owner: RootAccount
1327
1328
  }
1328
1329
 
1329
1330
  /** Coordinated key authorization parameters. */
@@ -1333,7 +1334,7 @@ export namespace signAuthorization {
1333
1334
  /** Stored multisig operation hash. */
1334
1335
  hash: Hex
1335
1336
  /** Local owner that approves the authorization. */
1336
- owner: RootAccount | MultisigAccount
1337
+ owner: RootAccount
1337
1338
  }
1338
1339
  >
1339
1340
 
@@ -85,10 +85,7 @@ export const chainConfig = {
85
85
  throw new Error(
86
86
  'A local owner account is required to approve a stored multisig transaction.',
87
87
  )
88
- if (
89
- request.owner.source !== 'root' &&
90
- request.owner.source !== 'multisig'
91
- )
88
+ if (request.owner.source !== 'root')
92
89
  throw new Error(
93
90
  'A Tempo owner account is required to approve a stored multisig transaction.',
94
91
  )
@@ -125,7 +122,6 @@ export const chainConfig = {
125
122
  account: request.account,
126
123
  from: operation.account,
127
124
  multisigSimulation: getMultisigSimulation({
128
- account: operation.account,
129
125
  config: operation.config,
130
126
  local: request.account,
131
127
  }),
@@ -168,11 +164,7 @@ export const chainConfig = {
168
164
  throw new Error(
169
165
  'A local owner account is required to approve a multisig transaction.',
170
166
  )
171
- if (
172
- request.owner &&
173
- request.owner.source !== 'root' &&
174
- request.owner.source !== 'multisig'
175
- )
167
+ if (request.owner && request.owner.source !== 'root')
176
168
  throw new Error(
177
169
  'A Tempo owner account is required to approve a multisig transaction.',
178
170
  )
@@ -206,7 +198,6 @@ export const chainConfig = {
206
198
  )
207
199
  request.from = account
208
200
  request.multisigSimulation = getMultisigSimulation({
209
- account,
210
201
  config,
211
202
  local,
212
203
  })
@@ -409,48 +400,18 @@ export type ChainConfig = typeof chainConfig
409
400
 
410
401
  /** Builds a bounded multisig spec for gas simulation. */
411
402
  function getMultisigSimulation(options: {
412
- account: Address
413
403
  config: MultisigConfig.Config
414
404
  local?: MultisigAccount | undefined
415
405
  }): MultisigSimulation.Spec {
416
- const { account, config, local } = options
406
+ const { config, local } = options
417
407
  return {
418
- account,
419
408
  approvals: selectOwners(config).map((owner) => {
420
409
  const localOwner = local?.owners.find((account) =>
421
410
  isAddressEqual(account.address, owner.owner),
422
411
  )
423
- if (localOwner?.source === 'multisig') {
424
- const nested = localOwner as MultisigAccount
425
- if (!nested.config)
426
- throw new Error(
427
- 'A nested multisig config is required for gas estimation.',
428
- )
429
- return {
430
- type: 'multisig',
431
- spec: {
432
- account: nested.address,
433
- approvals: selectOwners(nested.config).map((owner) => {
434
- const nestedOwner = nested.owners.find((account) =>
435
- isAddressEqual(account.address, owner.owner),
436
- )
437
- if (nestedOwner?.source === 'multisig')
438
- throw new Error(
439
- 'Multisig simulation nesting exceeds depth two.',
440
- )
441
- return {
442
- ...getSimulationKey(nestedOwner),
443
- owner: owner.owner,
444
- }
445
- }),
446
- config: nested.config,
447
- },
448
- }
449
- }
450
412
  return {
451
413
  ...getSimulationKey(localOwner),
452
414
  owner: owner.owner,
453
- type: 'primitive',
454
415
  }
455
416
  }),
456
417
  config,
@@ -4,12 +4,13 @@ import type * as Hex from 'ox/Hex'
4
4
  import * as Json from 'ox/Json'
5
5
  import { MultisigConfig } from 'ox/tempo'
6
6
  import { BaseError } from '../../errors/base.js'
7
+ import { nativeMultisigFactory } from '../Addresses.js'
7
8
  import type * as Store from '../Store.js'
8
9
 
9
10
  /** A valid 48-owner config is much smaller; 64 KiB bounds hostile store parsing work. */
10
11
  const maxStoredValueLength = 65_536
11
12
 
12
- /** Zero commitment used before an account records its first config update. */
13
+ /** Zero commitment used before an account is initialized. */
13
14
  const zeroCommitment = `0x${'00'.repeat(32)}` as const
14
15
 
15
16
  /** Reads a cached multisig config. */
@@ -43,7 +44,14 @@ export async function write(
43
44
  const { address, commitment } = options
44
45
  const config = MultisigConfig.from(options.config)
45
46
  assertKey({ address, commitment, config })
46
- await store.setItem(key({ address, commitment }), serialize(config))
47
+ const value = serialize(config)
48
+ await store.setItem(key({ address, commitment }), value)
49
+ // Initialization commits the version-zero config without changing its version.
50
+ if (commitment.toLowerCase() === zeroCommitment)
51
+ await store.setItem(
52
+ key({ address, commitment: MultisigConfig.getCommitment(config) }),
53
+ value,
54
+ )
47
55
  }
48
56
 
49
57
  export declare namespace write {
@@ -69,11 +77,13 @@ function assertKey(options: {
69
77
  throw new InvalidStoreValueError()
70
78
  if (config.version === 0n) {
71
79
  if (
72
- commitment.toLowerCase() !== zeroCommitment ||
73
- !Address.isEqual(MultisigConfig.getAddress(config), address)
80
+ !Address.isEqual(
81
+ MultisigConfig.getAddress(config, { factory: nativeMultisigFactory }),
82
+ address,
83
+ )
74
84
  )
75
85
  throw new InvalidStoreValueError()
76
- return
86
+ if (commitment.toLowerCase() === zeroCommitment) return
77
87
  }
78
88
  if (
79
89
  MultisigConfig.getCommitment(config).toLowerCase() !==