viem 2.55.17 → 2.55.19

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 (147) hide show
  1. package/CHANGELOG.md +16 -0
  2. package/_cjs/chains/definitions/morphTachyon.js +25 -0
  3. package/_cjs/chains/definitions/morphTachyon.js.map +1 -0
  4. package/_cjs/chains/definitions/morphTachyonTestnet.js +26 -0
  5. package/_cjs/chains/definitions/morphTachyonTestnet.js.map +1 -0
  6. package/_cjs/chains/definitions/ynxTestnet.js +26 -0
  7. package/_cjs/chains/definitions/ynxTestnet.js.map +1 -0
  8. package/_cjs/chains/index.js +13 -7
  9. package/_cjs/chains/index.js.map +1 -1
  10. package/_cjs/errors/version.js +1 -1
  11. package/_cjs/tempo/Abis.js +102 -1
  12. package/_cjs/tempo/Abis.js.map +1 -1
  13. package/_cjs/tempo/Account.js +188 -16
  14. package/_cjs/tempo/Account.js.map +1 -1
  15. package/_cjs/tempo/Addresses.js +2 -1
  16. package/_cjs/tempo/Addresses.js.map +1 -1
  17. package/_cjs/tempo/Decorator.js +8 -0
  18. package/_cjs/tempo/Decorator.js.map +1 -1
  19. package/_cjs/tempo/Formatters.js +1 -1
  20. package/_cjs/tempo/Formatters.js.map +1 -1
  21. package/_cjs/tempo/Selectors.js +7 -1
  22. package/_cjs/tempo/Selectors.js.map +1 -1
  23. package/_cjs/tempo/Transaction.js +31 -10
  24. package/_cjs/tempo/Transaction.js.map +1 -1
  25. package/_cjs/tempo/actions/accessKey.js +49 -8
  26. package/_cjs/tempo/actions/accessKey.js.map +1 -1
  27. package/_cjs/tempo/actions/earn.js +12 -4
  28. package/_cjs/tempo/actions/earn.js.map +1 -1
  29. package/_cjs/tempo/actions/index.js +2 -1
  30. package/_cjs/tempo/actions/index.js.map +1 -1
  31. package/_cjs/tempo/actions/multisig.js +95 -0
  32. package/_cjs/tempo/actions/multisig.js.map +1 -0
  33. package/_cjs/tempo/actions/zone.js +16 -16
  34. package/_cjs/tempo/actions/zone.js.map +1 -1
  35. package/_cjs/tempo/chainConfig.js +58 -29
  36. package/_cjs/tempo/chainConfig.js.map +1 -1
  37. package/_cjs/tempo/internal/multisig.js +56 -0
  38. package/_cjs/tempo/internal/multisig.js.map +1 -0
  39. package/_cjs/tempo/zones/Abis.js +2 -2
  40. package/_cjs/tempo/zones/Abis.js.map +1 -1
  41. package/_esm/chains/definitions/morphTachyon.js +22 -0
  42. package/_esm/chains/definitions/morphTachyon.js.map +1 -0
  43. package/_esm/chains/definitions/morphTachyonTestnet.js +23 -0
  44. package/_esm/chains/definitions/morphTachyonTestnet.js.map +1 -0
  45. package/_esm/chains/definitions/ynxTestnet.js +23 -0
  46. package/_esm/chains/definitions/ynxTestnet.js.map +1 -0
  47. package/_esm/chains/index.js +3 -0
  48. package/_esm/chains/index.js.map +1 -1
  49. package/_esm/errors/version.js +1 -1
  50. package/_esm/tempo/Abis.js +101 -0
  51. package/_esm/tempo/Abis.js.map +1 -1
  52. package/_esm/tempo/Account.js +196 -66
  53. package/_esm/tempo/Account.js.map +1 -1
  54. package/_esm/tempo/Addresses.js +1 -0
  55. package/_esm/tempo/Addresses.js.map +1 -1
  56. package/_esm/tempo/Decorator.js +8 -0
  57. package/_esm/tempo/Decorator.js.map +1 -1
  58. package/_esm/tempo/Formatters.js +3 -6
  59. package/_esm/tempo/Formatters.js.map +1 -1
  60. package/_esm/tempo/Selectors.js +6 -0
  61. package/_esm/tempo/Selectors.js.map +1 -1
  62. package/_esm/tempo/Transaction.js +31 -10
  63. package/_esm/tempo/Transaction.js.map +1 -1
  64. package/_esm/tempo/actions/accessKey.js +65 -23
  65. package/_esm/tempo/actions/accessKey.js.map +1 -1
  66. package/_esm/tempo/actions/earn.js +14 -6
  67. package/_esm/tempo/actions/earn.js.map +1 -1
  68. package/_esm/tempo/actions/index.js +1 -0
  69. package/_esm/tempo/actions/index.js.map +1 -1
  70. package/_esm/tempo/actions/multisig.js +245 -0
  71. package/_esm/tempo/actions/multisig.js.map +1 -0
  72. package/_esm/tempo/actions/zone.js +17 -17
  73. package/_esm/tempo/actions/zone.js.map +1 -1
  74. package/_esm/tempo/chainConfig.js +65 -35
  75. package/_esm/tempo/chainConfig.js.map +1 -1
  76. package/_esm/tempo/internal/multisig.js +56 -0
  77. package/_esm/tempo/internal/multisig.js.map +1 -0
  78. package/_esm/tempo/zones/Abis.js +2 -2
  79. package/_esm/tempo/zones/Abis.js.map +1 -1
  80. package/_types/chains/definitions/morphTachyon.d.ts +51 -0
  81. package/_types/chains/definitions/morphTachyon.d.ts.map +1 -0
  82. package/_types/chains/definitions/morphTachyonTestnet.d.ts +51 -0
  83. package/_types/chains/definitions/morphTachyonTestnet.d.ts.map +1 -0
  84. package/_types/chains/definitions/tempo.d.ts +30 -2
  85. package/_types/chains/definitions/tempo.d.ts.map +1 -1
  86. package/_types/chains/definitions/tempoDevnet.d.ts +30 -2
  87. package/_types/chains/definitions/tempoDevnet.d.ts.map +1 -1
  88. package/_types/chains/definitions/tempoLocalnet.d.ts +30 -2
  89. package/_types/chains/definitions/tempoLocalnet.d.ts.map +1 -1
  90. package/_types/chains/definitions/tempoModerato.d.ts +30 -2
  91. package/_types/chains/definitions/tempoModerato.d.ts.map +1 -1
  92. package/_types/chains/definitions/ynxTestnet.d.ts +51 -0
  93. package/_types/chains/definitions/ynxTestnet.d.ts.map +1 -0
  94. package/_types/chains/index.d.ts +3 -0
  95. package/_types/chains/index.d.ts.map +1 -1
  96. package/_types/errors/version.d.ts +1 -1
  97. package/_types/tempo/Abis.d.ts +321 -0
  98. package/_types/tempo/Abis.d.ts.map +1 -1
  99. package/_types/tempo/Account.d.ts +42 -26
  100. package/_types/tempo/Account.d.ts.map +1 -1
  101. package/_types/tempo/Addresses.d.ts +1 -0
  102. package/_types/tempo/Addresses.d.ts.map +1 -1
  103. package/_types/tempo/Decorator.d.ts +34 -2
  104. package/_types/tempo/Decorator.d.ts.map +1 -1
  105. package/_types/tempo/Formatters.d.ts.map +1 -1
  106. package/_types/tempo/Selectors.d.ts +6 -0
  107. package/_types/tempo/Selectors.d.ts.map +1 -1
  108. package/_types/tempo/Transaction.d.ts +17 -2
  109. package/_types/tempo/Transaction.d.ts.map +1 -1
  110. package/_types/tempo/actions/accessKey.d.ts +35 -17
  111. package/_types/tempo/actions/accessKey.d.ts.map +1 -1
  112. package/_types/tempo/actions/earn.d.ts +5 -5
  113. package/_types/tempo/actions/earn.d.ts.map +1 -1
  114. package/_types/tempo/actions/index.d.ts +1 -0
  115. package/_types/tempo/actions/index.d.ts.map +1 -1
  116. package/_types/tempo/actions/multisig.d.ts +472 -0
  117. package/_types/tempo/actions/multisig.d.ts.map +1 -0
  118. package/_types/tempo/actions/zone.d.ts +17 -17
  119. package/_types/tempo/actions/zone.d.ts.map +1 -1
  120. package/_types/tempo/chainConfig.d.ts +15 -1
  121. package/_types/tempo/chainConfig.d.ts.map +1 -1
  122. package/_types/tempo/internal/multisig.d.ts +13 -0
  123. package/_types/tempo/internal/multisig.d.ts.map +1 -0
  124. package/_types/tempo/zones/Abis.d.ts +2 -2
  125. package/_types/tempo/zones/zone.d.ts +90 -6
  126. package/_types/tempo/zones/zone.d.ts.map +1 -1
  127. package/chains/definitions/morphTachyon.ts +22 -0
  128. package/chains/definitions/morphTachyonTestnet.ts +23 -0
  129. package/chains/definitions/ynxTestnet.ts +23 -0
  130. package/chains/index.ts +3 -0
  131. package/errors/version.ts +1 -1
  132. package/package.json +2 -2
  133. package/tempo/Abis.ts +102 -0
  134. package/tempo/Account.ts +329 -51
  135. package/tempo/Addresses.ts +1 -0
  136. package/tempo/Decorator.ts +52 -2
  137. package/tempo/Formatters.ts +4 -5
  138. package/tempo/Selectors.ts +7 -0
  139. package/tempo/Transaction.ts +52 -12
  140. package/tempo/actions/accessKey.ts +128 -32
  141. package/tempo/actions/earn.ts +19 -11
  142. package/tempo/actions/index.ts +1 -0
  143. package/tempo/actions/multisig.ts +356 -0
  144. package/tempo/actions/zone.ts +35 -31
  145. package/tempo/chainConfig.ts +82 -35
  146. package/tempo/internal/multisig.ts +72 -0
  147. package/tempo/zones/Abis.ts +2 -2
package/tempo/Abis.ts CHANGED
@@ -443,6 +443,107 @@ export const addressRegistry = [
443
443
  { name: 'VirtualAddressUnregistered', type: 'error', inputs: [] },
444
444
  ] as const
445
445
 
446
+ export const nativeMultisig = [
447
+ {
448
+ name: 'deriveAccount',
449
+ type: 'function',
450
+ stateMutability: 'pure',
451
+ inputs: [
452
+ { type: 'bytes32', name: 'salt' },
453
+ { type: 'uint8', name: 'threshold' },
454
+ {
455
+ type: 'tuple[]',
456
+ name: 'owners',
457
+ components: [
458
+ { type: 'address', name: 'owner' },
459
+ { type: 'uint8', name: 'weight' },
460
+ ],
461
+ },
462
+ ],
463
+ outputs: [{ type: 'address', name: 'account' }],
464
+ },
465
+ {
466
+ name: 'isMultisigAccount',
467
+ type: 'function',
468
+ stateMutability: 'view',
469
+ inputs: [{ type: 'address', name: 'account' }],
470
+ outputs: [{ type: 'bool' }],
471
+ },
472
+ {
473
+ name: 'getConfig',
474
+ type: 'function',
475
+ stateMutability: 'view',
476
+ inputs: [{ type: 'address', name: 'account' }],
477
+ outputs: [
478
+ {
479
+ type: 'tuple',
480
+ components: [
481
+ { type: 'uint64', name: 'version' },
482
+ { type: 'uint8', name: 'threshold' },
483
+ {
484
+ type: 'tuple[]',
485
+ name: 'owners',
486
+ components: [
487
+ { type: 'address', name: 'owner' },
488
+ { type: 'uint8', name: 'weight' },
489
+ ],
490
+ },
491
+ ],
492
+ },
493
+ ],
494
+ },
495
+ {
496
+ name: 'updateConfig',
497
+ type: 'function',
498
+ stateMutability: 'nonpayable',
499
+ inputs: [
500
+ { type: 'uint8', name: 'threshold' },
501
+ {
502
+ type: 'tuple[]',
503
+ name: 'owners',
504
+ components: [
505
+ { type: 'address', name: 'owner' },
506
+ { type: 'uint8', name: 'weight' },
507
+ ],
508
+ },
509
+ ],
510
+ outputs: [],
511
+ },
512
+ {
513
+ name: 'MultisigInitialized',
514
+ type: 'event',
515
+ inputs: [{ type: 'address', name: 'account', indexed: true }],
516
+ },
517
+ {
518
+ name: 'MultisigConfigUpdated',
519
+ type: 'event',
520
+ inputs: [
521
+ { type: 'address', name: 'account', indexed: true },
522
+ { type: 'uint8', name: 'threshold' },
523
+ {
524
+ type: 'tuple[]',
525
+ name: 'owners',
526
+ components: [
527
+ { type: 'address', name: 'owner' },
528
+ { type: 'uint8', name: 'weight' },
529
+ ],
530
+ },
531
+ ],
532
+ },
533
+ { name: 'NotMultisigAccount', type: 'error', inputs: [] },
534
+ { name: 'InvalidAccount', type: 'error', inputs: [] },
535
+ { name: 'InvalidConfig', type: 'error', inputs: [] },
536
+ { name: 'InvalidThreshold', type: 'error', inputs: [] },
537
+ { name: 'InvalidOwner', type: 'error', inputs: [] },
538
+ { name: 'InvalidWeight', type: 'error', inputs: [] },
539
+ { name: 'TooManyOwners', type: 'error', inputs: [] },
540
+ { name: 'DuplicateOwner', type: 'error', inputs: [] },
541
+ { name: 'InvalidOwnerOrder', type: 'error', inputs: [] },
542
+ { name: 'AccountAlreadyInitialized', type: 'error', inputs: [] },
543
+ { name: 'UnauthorizedCaller', type: 'error', inputs: [] },
544
+ { name: 'SameTransactionUpdateNotAllowed', type: 'error', inputs: [] },
545
+ ] as const
546
+
446
547
  export const nonce = [
447
548
  {
448
549
  name: 'getNonce',
@@ -2975,6 +3076,7 @@ export const validatorConfigV2 = [
2975
3076
  export const abis = [
2976
3077
  ...accountKeychain,
2977
3078
  ...addressRegistry,
3079
+ ...nativeMultisig,
2978
3080
  ...nonce,
2979
3081
  ...receivePolicyGuard,
2980
3082
  ...signatureVerifier,
package/tempo/Account.ts CHANGED
@@ -275,16 +275,17 @@ export declare namespace fromSecp256k1 {
275
275
  }
276
276
 
277
277
  /**
278
- * Instantiates a synthetic Account for a native multisig (TIP-1061) config.
278
+ * Instantiates an Account for a native multisig (TIP-1061) config.
279
279
  *
280
- * The returned account does not hold a key. It is used purely to drive the
281
- * standard `sendTransaction` flow: it derives the multisig address from the
282
- * config and passes the prepared request (carrying the collected owner
283
- * `signatures`) through to the chain serializer, which combines the approvals
284
- * into the multisig signature envelope.
280
+ * The returned account does not hold a key itself. Pass an initial config to
281
+ * derive and bootstrap an account, or pass the address of an initialized
282
+ * account to reconstruct it without retaining the initial config.
285
283
  *
286
- * Owner approvals are produced separately by signing with `multisig` request
287
- * metadata (see `signTransaction`), and provided here via `signatures`.
284
+ * Owners can be accounts or addresses directly, or weighted `{ owner, weight }`
285
+ * entries. Direct owners default to weight `1`, and `threshold` defaults to `1`.
286
+ * Local owner accounts are retained for signing, including nested multisigs.
287
+ * Address-only configs keep the external approval flow, where owner approvals
288
+ * are collected separately and provided via `signatures`.
288
289
  *
289
290
  * Accepts a raw config and normalizes it internally (via `MultisigConfig.from`),
290
291
  * so callers don't need to call `MultisigConfig.from` themselves.
@@ -295,33 +296,67 @@ export declare namespace fromSecp256k1 {
295
296
  *
296
297
  * const account = Account.fromMultisig({
297
298
  * threshold: 2,
298
- * owners: [
299
- * { owner: owner_1.address, weight: 1 },
300
- * { owner: owner_2.address, weight: 1 },
301
- * ],
299
+ * owners: [owner_1, owner_2],
302
300
  * })
303
301
  *
304
- * // Pass the account to `prepareTransactionRequest` — the multisig config is
305
- * // inferred from it, so no `multisig` field is needed.
302
+ * // The multisig config is inferred from the account.
306
303
  * const request = await client.prepareTransactionRequest({ account, ...rest })
307
304
  *
308
- * // The prepared request carries the multisig account as sender, so it doesn't
309
- * // need to be re-passed to `sendTransaction`.
310
- * const transaction = await client.sendTransaction({
311
- * ...request,
312
- * signatures: [signature_1, signature_2],
313
- * })
305
+ * const transaction = await client.signTransaction(request)
314
306
  * ```
315
307
  *
316
- * @param config Multisig config (raw or from `MultisigConfig.from`).
308
+ * @param value Initial multisig config or initialized multisig address.
317
309
  * @returns Multisig account.
318
310
  */
319
- export function fromMultisig(config: MultisigConfig.Config): MultisigAccount {
320
- const normalized = MultisigConfig.from(config)
321
- const address = Address.checksum(MultisigConfig.getAddress(normalized))
322
- return {
311
+ export function fromMultisig(
312
+ config: fromMultisig.Config,
313
+ ): MultisigAccount<MultisigConfig.Config>
314
+ export function fromMultisig(
315
+ address: Address.Address,
316
+ ): MultisigAccount<undefined>
317
+ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount
318
+ export function fromMultisig(value: fromMultisig.Parameters): MultisigAccount {
319
+ const config = (() => {
320
+ if (typeof value === 'string') return undefined
321
+ const ownerEntries = value.owners.map((value) =>
322
+ typeof value === 'string' || 'address' in value
323
+ ? { owner: value, weight: 1 }
324
+ : value,
325
+ )
326
+ return MultisigConfig.from({
327
+ ...value,
328
+ owners: ownerEntries.map(({ owner, weight }) => ({
329
+ owner: typeof owner === 'string' ? owner : owner.address,
330
+ weight,
331
+ })),
332
+ threshold: value.threshold ?? 1,
333
+ })
334
+ })()
335
+ const address = Address.checksum(
336
+ typeof value === 'string' ? value : MultisigConfig.getAddress(config!),
337
+ )
338
+ const ownerAccounts =
339
+ typeof value === 'string'
340
+ ? []
341
+ : value.owners.flatMap((value) => {
342
+ const owner =
343
+ typeof value === 'string' || 'address' in value
344
+ ? value
345
+ : value.owner
346
+ return typeof owner === 'string' ? [] : [parseAccount(owner)]
347
+ })
348
+ const owners =
349
+ config?.owners.flatMap(({ owner }) => {
350
+ const account = ownerAccounts.find((account) =>
351
+ Address.isEqual(account.address, owner),
352
+ )
353
+ return account ? [account] : []
354
+ }) ?? []
355
+
356
+ const account: MultisigAccount = {
323
357
  address,
324
- config: normalized,
358
+ config,
359
+ owners,
325
360
  publicKey: '0x',
326
361
  source: 'multisig',
327
362
  type: 'local',
@@ -333,17 +368,201 @@ export function fromMultisig(config: MultisigConfig.Config): MultisigAccount {
333
368
  },
334
369
  async signTransaction(transaction, options) {
335
370
  const { serializer = Transaction.serialize } = options ?? {}
336
- return (await serializer(transaction as never)) as Hex.Hex
371
+ const request = transaction as Transaction.TransactionSerializableTempo
372
+ if (owners.length === 0)
373
+ return (await serializer(transaction as never)) as Hex.Hex
374
+
375
+ const presign = {
376
+ ...request,
377
+ signatures: undefined,
378
+ ...(request.feePayerSignature === undefined
379
+ ? {}
380
+ : { feePayerSignature: null }),
381
+ }
382
+ const payload = keccak256(await serializer(presign as never))
383
+ const requestMultisig = request.multisig
384
+ const requestAccount = requestMultisig
385
+ ? typeof requestMultisig === 'string'
386
+ ? requestMultisig
387
+ : MultisigConfig.getAddress(requestMultisig)
388
+ : address
389
+
390
+ if (!Address.isEqual(requestAccount, address)) {
391
+ const state = request.multisigOwnerStates?.find((state) =>
392
+ Address.isEqual(state.account, address),
393
+ )
394
+ if (state && !state.initialized)
395
+ throw new Error('Nested multisig owners must be initialized.')
396
+ const parentDigest = MultisigConfig.getSignPayload({
397
+ ...(typeof requestMultisig === 'string'
398
+ ? { account: requestMultisig }
399
+ : { initialConfig: requestMultisig! }),
400
+ payload,
401
+ version: request.multisigVersion,
402
+ })
403
+ return SignatureEnvelope.serialize(
404
+ await signMultisig(account, {
405
+ payload: parentDigest,
406
+ states: request.multisigOwnerStates,
407
+ version: state?.version,
408
+ }),
409
+ )
410
+ }
411
+
412
+ const keyAuthorizationSignature = request.keyAuthorization?.signature
413
+ const keyAuthorizationInitializes =
414
+ keyAuthorizationSignature?.type === 'multisig' &&
415
+ typeof keyAuthorizationSignature.init !== 'undefined'
416
+ const signature = await signMultisig(account, {
417
+ init:
418
+ typeof config !== 'undefined' &&
419
+ !request.nonce &&
420
+ !request.nonceKey &&
421
+ !keyAuthorizationInitializes,
422
+ payload,
423
+ signatures: request.signatures?.map((signature) =>
424
+ SignatureEnvelope.from(signature),
425
+ ),
426
+ states: request.multisigOwnerStates,
427
+ version: request.multisigVersion,
428
+ })
429
+ return (await serializer(
430
+ transaction as never,
431
+ signature as never,
432
+ )) as Hex.Hex
337
433
  },
338
434
  async signTypedData() {
339
435
  throw new Error('`signTypedData` is not supported for multisig accounts.')
340
436
  },
341
437
  }
438
+ return account
439
+ }
440
+
441
+ export declare namespace fromMultisig {
442
+ export type Owner =
443
+ | Address.Address
444
+ | LocalAccount
445
+ | (Omit<MultisigConfig.Owner, 'owner'> & {
446
+ owner: Address.Address | LocalAccount
447
+ })
448
+
449
+ export type Config = Omit<MultisigConfig.Config, 'owners' | 'threshold'> & {
450
+ owners: readonly Owner[]
451
+ threshold?: number | undefined
452
+ }
453
+
454
+ export type Parameters = Address.Address | Config
455
+ }
456
+
457
+ export type MultisigAccount<
458
+ config extends MultisigConfig.Config | undefined =
459
+ | MultisigConfig.Config
460
+ | undefined,
461
+ > = LocalAccount<'multisig'> & {
462
+ /** Normalized initial config, or `undefined` when instantiated from an address. */
463
+ config: config
464
+ /** @internal Local owner accounts available for signing. */
465
+ owners: readonly LocalAccount[]
466
+ }
467
+
468
+ function isMultisigAccount(account: LocalAccount): account is MultisigAccount {
469
+ return account.source === 'multisig'
342
470
  }
343
471
 
344
- export type MultisigAccount = LocalAccount<'multisig'> & {
345
- /** Multisig config (from `MultisigConfig.from`). */
346
- config: MultisigConfig.Config
472
+ async function signMultisig(
473
+ account: MultisigAccount,
474
+ parameters: {
475
+ init?: boolean | undefined
476
+ payload: Hex.Hex
477
+ signatures?: readonly SignatureEnvelope.SignatureEnvelope[] | undefined
478
+ states?: readonly Transaction.MultisigOwnerState[] | undefined
479
+ version?: bigint | undefined
480
+ },
481
+ ): Promise<SignatureEnvelope.Multisig> {
482
+ const {
483
+ init = false,
484
+ payload,
485
+ signatures: providedSignatures = [],
486
+ states = [],
487
+ version = 0n,
488
+ } = parameters
489
+ const state = states.find((state) =>
490
+ Address.isEqual(state.account, account.address),
491
+ )
492
+ const currentConfig = state?.config ?? account.config
493
+ if (!currentConfig)
494
+ throw new Error('Current multisig config is required for local signing.')
495
+ const digest = MultisigConfig.getSignPayload({
496
+ account: account.address,
497
+ payload,
498
+ version,
499
+ })
500
+ const signatures = [...providedSignatures]
501
+ const signedOwners = new Set<Address.Address>()
502
+ let weight = 0
503
+
504
+ for (const signature of providedSignatures) {
505
+ const address = SignatureEnvelope.extractAddress({
506
+ payload: digest,
507
+ signature,
508
+ }).toLowerCase() as Address.Address
509
+ const owner = currentConfig.owners.find((owner) =>
510
+ Address.isEqual(owner.owner, address),
511
+ )
512
+ if (!owner || signedOwners.has(address)) continue
513
+ signedOwners.add(address)
514
+ weight += Number(owner.weight)
515
+ }
516
+
517
+ for (const owner of currentConfig.owners) {
518
+ const address = owner.owner.toLowerCase() as Address.Address
519
+ if (signedOwners.has(address)) continue
520
+ const ownerAccount = account.owners.find((account) =>
521
+ Address.isEqual(account.address, owner.owner),
522
+ )
523
+ if (!ownerAccount) continue
524
+
525
+ if (isMultisigAccount(ownerAccount)) {
526
+ const state = states.find((state) =>
527
+ Address.isEqual(state.account, ownerAccount.address),
528
+ )
529
+ if (state && !state.initialized)
530
+ throw new Error('Nested multisig owners must be initialized.')
531
+ signatures.push(
532
+ await signMultisig(ownerAccount, {
533
+ payload: digest,
534
+ states,
535
+ version: state?.version,
536
+ }),
537
+ )
538
+ } else {
539
+ if (!ownerAccount.sign)
540
+ throw new Error('Multisig owner account cannot sign.')
541
+ signatures.push(
542
+ SignatureEnvelope.from(await ownerAccount.sign({ hash: digest })),
543
+ )
544
+ }
545
+
546
+ signedOwners.add(address)
547
+ weight += Number(owner.weight)
548
+ if (weight >= Number(currentConfig.threshold)) break
549
+ }
550
+
551
+ if (weight < Number(currentConfig.threshold))
552
+ throw new Error('Local multisig owners do not meet the threshold.')
553
+
554
+ if (init && !account.config)
555
+ throw new Error('Initial multisig config is required for bootstrap.')
556
+ return SignatureEnvelope.from({
557
+ account: account.address,
558
+ ...(init ? { init: account.config } : {}),
559
+ signatures: SignatureEnvelope.sortMultisigApprovals({
560
+ account: account.address,
561
+ payload,
562
+ signatures,
563
+ version,
564
+ }),
565
+ }) as SignatureEnvelope.Multisig
347
566
  }
348
567
 
349
568
  /**
@@ -538,11 +757,44 @@ function isAccessKeyAccount(
538
757
  return account.source === 'accessKey' && 'accessKeyAddress' in account
539
758
  }
540
759
 
760
+ /** @internal */
761
+ export function getKeyAuthorizationSignPayload(
762
+ account: LocalAccount,
763
+ parameters: signKeyAuthorization.Parameters,
764
+ ): Hex.Hex {
765
+ const { admin, chainId, expiry, key, limits, scopes, witness } = parameters
766
+ const { accessKeyAddress, keyType: type } = resolveAccessKey(key)
767
+ const boundFields =
768
+ isAccessKeyAccount(account) || isMultisigAccount(account)
769
+ ? { account: account.address }
770
+ : {}
771
+ const restrictions = admin ? {} : { expiry, limits, scopes }
772
+ return KeyAuthorization.getSignPayload({
773
+ address: accessKeyAddress,
774
+ chainId,
775
+ type,
776
+ witness,
777
+ ...(admin ? { isAdmin: true } : {}),
778
+ ...boundFields,
779
+ ...restrictions,
780
+ } as never)
781
+ }
782
+
541
783
  export async function signKeyAuthorization(
542
784
  account: LocalAccount,
543
785
  parameters: signKeyAuthorization.Parameters,
544
786
  ): Promise<signKeyAuthorization.ReturnValue> {
545
- const { chainId, key, expiry, limits, scopes, witness, admin } = parameters
787
+ const {
788
+ admin,
789
+ chainId,
790
+ expiry,
791
+ key,
792
+ limits,
793
+ multisig: multisigState,
794
+ scopes,
795
+ signatures,
796
+ witness,
797
+ } = parameters
546
798
  const { accessKeyAddress, keyType: type } = resolveAccessKey(key)
547
799
 
548
800
  // When the signer is an admin access key, the authorization must be
@@ -550,24 +802,36 @@ export async function signKeyAuthorization(
550
802
  // on behalf of, so the signed payload cannot be replayed against another
551
803
  // account. [TIP-1049]
552
804
  const isAccessKey = isAccessKeyAccount(account)
553
- const boundFields = isAccessKey ? { account: account.address } : {}
805
+ const isMultisig = isMultisigAccount(account)
806
+ const boundFields =
807
+ isAccessKey || isMultisig ? { account: account.address } : {}
554
808
 
555
809
  // Admin key authorizations are unrestricted and must not carry expiry,
556
810
  // limits, or call scopes (the protocol rejects them). [TIP-1049]
557
811
  const restrictions = admin ? {} : { expiry, limits, scopes }
558
812
 
559
- const hash = KeyAuthorization.getSignPayload({
560
- address: accessKeyAddress,
561
- chainId,
562
- type,
563
- witness,
564
- ...(admin ? { isAdmin: true } : {}),
565
- ...boundFields,
566
- ...restrictions,
567
- } as never)
568
- const signature = isAccessKey
569
- ? await account.sign({ hash, raw: true })
570
- : await account.sign!({ hash })
813
+ const hash = getKeyAuthorizationSignPayload(account, parameters)
814
+ const signature = await (async () => {
815
+ if (isAccessKey) return account.sign({ hash, raw: true })
816
+ if (isMultisig) {
817
+ if (!multisigState)
818
+ throw new Error(
819
+ 'Multisig state is required to sign a key authorization.',
820
+ )
821
+ return SignatureEnvelope.serialize(
822
+ await signMultisig(account, {
823
+ init: multisigState.init,
824
+ payload: hash,
825
+ signatures: signatures?.map((signature) =>
826
+ SignatureEnvelope.from(signature),
827
+ ),
828
+ states: multisigState.states,
829
+ version: multisigState.version,
830
+ }),
831
+ )
832
+ }
833
+ return account.sign!({ hash })
834
+ })()
571
835
  return KeyAuthorization.from({
572
836
  address: accessKeyAddress,
573
837
  chainId,
@@ -585,7 +849,6 @@ export declare namespace signKeyAuthorization {
585
849
  KeyAuthorization.KeyAuthorization,
586
850
  'chainId' | 'expiry' | 'limits' | 'scopes' | 'witness'
587
851
  > & {
588
- key: resolveAccessKey.Parameters
589
852
  /**
590
853
  * Whether to authorize the key as an admin key. Admin keys are
591
854
  * unrestricted and can manage the account's other access keys; `expiry`,
@@ -594,6 +857,17 @@ export declare namespace signKeyAuthorization {
594
857
  * [TIP-1049](https://tips.sh/1049)
595
858
  */
596
859
  admin?: boolean | undefined
860
+ key: resolveAccessKey.Parameters
861
+ /** @internal Current state used when a multisig account signs the authorization. */
862
+ multisig?:
863
+ | {
864
+ init: boolean
865
+ states?: readonly Transaction.MultisigOwnerState[] | undefined
866
+ version: bigint
867
+ }
868
+ | undefined
869
+ /** Serialized approvals from external multisig owners. */
870
+ signatures?: readonly SignatureEnvelope.Serialized[] | undefined
597
871
  }
598
872
 
599
873
  type ReturnValue = KeyAuthorization.Signed
@@ -674,17 +948,21 @@ function fromBase(parameters: fromBase.Parameters): Account_base {
674
948
 
675
949
  const payload = keccak256(await serializer(presign))
676
950
 
677
- // Native multisig (TIP-1061): return this owner's approval — a serialized
678
- // primitive signature over the multisig owner approval digest — instead of
951
+ // Native multisig (TIP-1061): return this owner's approval, a serialized
952
+ // primitive signature over the multisig owner approval digest, instead of
679
953
  // a full serialized transaction. Approvals are combined later in
680
954
  // `sendTransaction({ signatures })`.
681
- const multisig = (
682
- transaction as { multisig?: MultisigConfig.Config | undefined }
683
- ).multisig
955
+ const { multisig, multisigVersion } = transaction as {
956
+ multisig?: Address.Address | MultisigConfig.Config | undefined
957
+ multisigVersion?: bigint | undefined
958
+ }
684
959
  if (multisig) {
685
960
  const digest = MultisigConfig.getSignPayload({
961
+ ...(typeof multisig === 'string'
962
+ ? { account: multisig }
963
+ : { initialConfig: multisig }),
686
964
  payload,
687
- genesisConfig: multisig,
965
+ version: multisigVersion,
688
966
  })
689
967
  return await sign({ hash: digest, raw: true })
690
968
  }
@@ -4,6 +4,7 @@ export const accountKeychain = '0xaAAAaaAA00000000000000000000000000000000'
4
4
  export const accountRegistrar = '0x7702ac0000000000000000000000000000000000'
5
5
  export const addressRegistry = '0xfdc0000000000000000000000000000000000000'
6
6
  export const feeManager = '0xfeec000000000000000000000000000000000000'
7
+ export const nativeMultisig = '0xAACC000000000000000000000000000000000000'
7
8
  export const nonceManager = '0x4e4F4E4345000000000000000000000000000000'
8
9
  export const pathUsd = '0x20c0000000000000000000000000000000000000'
9
10
  export const receivePolicyGuard = '0xB10C000000000000000000000000000000000000'
@@ -10,6 +10,7 @@ import * as dexActions from './actions/dex.js'
10
10
  import * as earnActions from './actions/earn.js'
11
11
  import * as faucetActions from './actions/faucet.js'
12
12
  import * as feeActions from './actions/fee.js'
13
+ import * as multisigActions from './actions/multisig.js'
13
14
  import * as nonceActions from './actions/nonce.js'
14
15
  import * as policyActions from './actions/policy.js'
15
16
  import * as receivePolicyActions from './actions/receivePolicy.js'
@@ -1926,8 +1927,8 @@ type DecoratorBase<
1926
1927
  * assetToken: '0x...',
1927
1928
  * gateway: '0x...',
1928
1929
  * recipient: '0x...',
1929
- * recoveryRecipient: '0x...',
1930
1930
  * shareAmountMin: 99_500_000n,
1931
+ * tempoRefundRecipient: '0x...',
1931
1932
  * vault: '0x...',
1932
1933
  * vaultAssetAmountMin: 99_000_000n,
1933
1934
  * zoneId: 7,
@@ -2157,9 +2158,9 @@ type DecoratorBase<
2157
2158
  * const prepared = await parentClient.earn.privateRedeem.prepare({
2158
2159
  * gateway: '0x...',
2159
2160
  * recipient: '0x...',
2160
- * recoveryRecipient: '0x...',
2161
2161
  * shareAmount: 100_000_000n,
2162
2162
  * slippageBps: 50,
2163
+ * tempoRefundRecipient: '0x...',
2163
2164
  * vault: '0x...',
2164
2165
  * zoneId: 7,
2165
2166
  * })
@@ -2363,6 +2364,44 @@ type DecoratorBase<
2363
2364
  parameters: faucetActions.fundSync.Parameters,
2364
2365
  ) => Promise<faucetActions.fundSync.ReturnValue>
2365
2366
  }
2367
+ multisig: {
2368
+ /**
2369
+ * Gets the current configuration for an initialized multisig account.
2370
+ *
2371
+ * @param parameters - Parameters.
2372
+ * @returns The current version, threshold, and owners.
2373
+ */
2374
+ getConfig: (
2375
+ parameters: multisigActions.getConfig.Parameters,
2376
+ ) => Promise<multisigActions.getConfig.ReturnValue>
2377
+ /**
2378
+ * Checks whether an address is an initialized native multisig account.
2379
+ *
2380
+ * @param parameters - Parameters.
2381
+ * @returns Whether the account is initialized.
2382
+ */
2383
+ isInitialized: (
2384
+ parameters: multisigActions.isInitialized.Parameters,
2385
+ ) => Promise<multisigActions.isInitialized.ReturnValue>
2386
+ /**
2387
+ * Replaces the current configuration for a native multisig account.
2388
+ *
2389
+ * @param parameters - New configuration and transaction parameters.
2390
+ * @returns The transaction hash.
2391
+ */
2392
+ updateConfig: (
2393
+ parameters: multisigActions.updateConfig.Parameters<chain, account>,
2394
+ ) => Promise<multisigActions.updateConfig.ReturnValue>
2395
+ /**
2396
+ * Replaces a native multisig configuration and waits for confirmation.
2397
+ *
2398
+ * @param parameters - New configuration and transaction parameters.
2399
+ * @returns The update event and transaction receipt.
2400
+ */
2401
+ updateConfigSync: (
2402
+ parameters: multisigActions.updateConfigSync.Parameters<chain, account>,
2403
+ ) => Promise<multisigActions.updateConfigSync.ReturnValue>
2404
+ }
2366
2405
  nonce: {
2367
2406
  /**
2368
2407
  * Gets the nonce for an account and nonce key.
@@ -5783,6 +5822,10 @@ export type Decorator<
5783
5822
  DecoratorBase<chain, account>['faucet'],
5784
5823
  typeof faucetActions
5785
5824
  >
5825
+ multisig: DecorateNamespace<
5826
+ DecoratorBase<chain, account>['multisig'],
5827
+ typeof multisigActions
5828
+ >
5786
5829
  nonce: DecorateNamespace<
5787
5830
  DecoratorBase<chain, account>['nonce'],
5788
5831
  typeof nonceActions
@@ -5854,6 +5897,7 @@ export function decorator() {
5854
5897
  'getRemainingLimit',
5855
5898
  'isAdmin',
5856
5899
  'isWitnessBurned',
5900
+ 'prepareAuthorization',
5857
5901
  'revoke',
5858
5902
  'revokeSync',
5859
5903
  'signAuthorization',
@@ -5952,6 +5996,12 @@ export function decorator() {
5952
5996
  'withdrawExactSync',
5953
5997
  ]),
5954
5998
  faucet: bindActions(client, faucetActions, ['fund', 'fundSync']),
5999
+ multisig: bindActions(client, multisigActions, [
6000
+ 'getConfig',
6001
+ 'isInitialized',
6002
+ 'updateConfig',
6003
+ 'updateConfigSync',
6004
+ ]),
5955
6005
  nonce: bindActions(client, nonceActions, [
5956
6006
  'getNonce',
5957
6007
  'watchNonceIncremented',