viem 2.55.18 → 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 (127) hide show
  1. package/CHANGELOG.md +10 -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/index.js +2 -1
  28. package/_cjs/tempo/actions/index.js.map +1 -1
  29. package/_cjs/tempo/actions/multisig.js +95 -0
  30. package/_cjs/tempo/actions/multisig.js.map +1 -0
  31. package/_cjs/tempo/chainConfig.js +58 -29
  32. package/_cjs/tempo/chainConfig.js.map +1 -1
  33. package/_cjs/tempo/internal/multisig.js +56 -0
  34. package/_cjs/tempo/internal/multisig.js.map +1 -0
  35. package/_esm/chains/definitions/morphTachyon.js +22 -0
  36. package/_esm/chains/definitions/morphTachyon.js.map +1 -0
  37. package/_esm/chains/definitions/morphTachyonTestnet.js +23 -0
  38. package/_esm/chains/definitions/morphTachyonTestnet.js.map +1 -0
  39. package/_esm/chains/definitions/ynxTestnet.js +23 -0
  40. package/_esm/chains/definitions/ynxTestnet.js.map +1 -0
  41. package/_esm/chains/index.js +3 -0
  42. package/_esm/chains/index.js.map +1 -1
  43. package/_esm/errors/version.js +1 -1
  44. package/_esm/tempo/Abis.js +101 -0
  45. package/_esm/tempo/Abis.js.map +1 -1
  46. package/_esm/tempo/Account.js +196 -66
  47. package/_esm/tempo/Account.js.map +1 -1
  48. package/_esm/tempo/Addresses.js +1 -0
  49. package/_esm/tempo/Addresses.js.map +1 -1
  50. package/_esm/tempo/Decorator.js +8 -0
  51. package/_esm/tempo/Decorator.js.map +1 -1
  52. package/_esm/tempo/Formatters.js +3 -6
  53. package/_esm/tempo/Formatters.js.map +1 -1
  54. package/_esm/tempo/Selectors.js +6 -0
  55. package/_esm/tempo/Selectors.js.map +1 -1
  56. package/_esm/tempo/Transaction.js +31 -10
  57. package/_esm/tempo/Transaction.js.map +1 -1
  58. package/_esm/tempo/actions/accessKey.js +65 -23
  59. package/_esm/tempo/actions/accessKey.js.map +1 -1
  60. package/_esm/tempo/actions/index.js +1 -0
  61. package/_esm/tempo/actions/index.js.map +1 -1
  62. package/_esm/tempo/actions/multisig.js +245 -0
  63. package/_esm/tempo/actions/multisig.js.map +1 -0
  64. package/_esm/tempo/chainConfig.js +65 -35
  65. package/_esm/tempo/chainConfig.js.map +1 -1
  66. package/_esm/tempo/internal/multisig.js +56 -0
  67. package/_esm/tempo/internal/multisig.js.map +1 -0
  68. package/_types/chains/definitions/morphTachyon.d.ts +51 -0
  69. package/_types/chains/definitions/morphTachyon.d.ts.map +1 -0
  70. package/_types/chains/definitions/morphTachyonTestnet.d.ts +51 -0
  71. package/_types/chains/definitions/morphTachyonTestnet.d.ts.map +1 -0
  72. package/_types/chains/definitions/tempo.d.ts +30 -2
  73. package/_types/chains/definitions/tempo.d.ts.map +1 -1
  74. package/_types/chains/definitions/tempoDevnet.d.ts +30 -2
  75. package/_types/chains/definitions/tempoDevnet.d.ts.map +1 -1
  76. package/_types/chains/definitions/tempoLocalnet.d.ts +30 -2
  77. package/_types/chains/definitions/tempoLocalnet.d.ts.map +1 -1
  78. package/_types/chains/definitions/tempoModerato.d.ts +30 -2
  79. package/_types/chains/definitions/tempoModerato.d.ts.map +1 -1
  80. package/_types/chains/definitions/ynxTestnet.d.ts +51 -0
  81. package/_types/chains/definitions/ynxTestnet.d.ts.map +1 -0
  82. package/_types/chains/index.d.ts +3 -0
  83. package/_types/chains/index.d.ts.map +1 -1
  84. package/_types/errors/version.d.ts +1 -1
  85. package/_types/tempo/Abis.d.ts +321 -0
  86. package/_types/tempo/Abis.d.ts.map +1 -1
  87. package/_types/tempo/Account.d.ts +42 -26
  88. package/_types/tempo/Account.d.ts.map +1 -1
  89. package/_types/tempo/Addresses.d.ts +1 -0
  90. package/_types/tempo/Addresses.d.ts.map +1 -1
  91. package/_types/tempo/Decorator.d.ts +32 -0
  92. package/_types/tempo/Decorator.d.ts.map +1 -1
  93. package/_types/tempo/Formatters.d.ts.map +1 -1
  94. package/_types/tempo/Selectors.d.ts +6 -0
  95. package/_types/tempo/Selectors.d.ts.map +1 -1
  96. package/_types/tempo/Transaction.d.ts +17 -2
  97. package/_types/tempo/Transaction.d.ts.map +1 -1
  98. package/_types/tempo/actions/accessKey.d.ts +35 -17
  99. package/_types/tempo/actions/accessKey.d.ts.map +1 -1
  100. package/_types/tempo/actions/index.d.ts +1 -0
  101. package/_types/tempo/actions/index.d.ts.map +1 -1
  102. package/_types/tempo/actions/multisig.d.ts +472 -0
  103. package/_types/tempo/actions/multisig.d.ts.map +1 -0
  104. package/_types/tempo/chainConfig.d.ts +15 -1
  105. package/_types/tempo/chainConfig.d.ts.map +1 -1
  106. package/_types/tempo/internal/multisig.d.ts +13 -0
  107. package/_types/tempo/internal/multisig.d.ts.map +1 -0
  108. package/_types/tempo/zones/zone.d.ts +90 -6
  109. package/_types/tempo/zones/zone.d.ts.map +1 -1
  110. package/chains/definitions/morphTachyon.ts +22 -0
  111. package/chains/definitions/morphTachyonTestnet.ts +23 -0
  112. package/chains/definitions/ynxTestnet.ts +23 -0
  113. package/chains/index.ts +3 -0
  114. package/errors/version.ts +1 -1
  115. package/package.json +2 -2
  116. package/tempo/Abis.ts +102 -0
  117. package/tempo/Account.ts +329 -51
  118. package/tempo/Addresses.ts +1 -0
  119. package/tempo/Decorator.ts +50 -0
  120. package/tempo/Formatters.ts +4 -5
  121. package/tempo/Selectors.ts +7 -0
  122. package/tempo/Transaction.ts +52 -12
  123. package/tempo/actions/accessKey.ts +128 -32
  124. package/tempo/actions/index.ts +1 -0
  125. package/tempo/actions/multisig.ts +356 -0
  126. package/tempo/chainConfig.ts +82 -35
  127. package/tempo/internal/multisig.ts +72 -0
@@ -0,0 +1,356 @@
1
+ import type { Address } from 'abitype'
2
+ import { MultisigConfig } from 'ox/tempo'
3
+ import type { Account } from '../../accounts/types.js'
4
+ import type { ReadContractReturnType } from '../../actions/public/readContract.js'
5
+ import { readContract } from '../../actions/public/readContract.js'
6
+ import type { WriteContractReturnType } from '../../actions/wallet/writeContract.js'
7
+ import { writeContract } from '../../actions/wallet/writeContract.js'
8
+ import { writeContractSync } from '../../actions/wallet/writeContractSync.js'
9
+ import type { Client } from '../../clients/createClient.js'
10
+ import type { Transport } from '../../clients/transports/createTransport.js'
11
+ import type { BaseErrorType } from '../../errors/base.js'
12
+ import type { Chain } from '../../types/chain.js'
13
+ import type { GetEventArgs } from '../../types/contract.js'
14
+ import type { Log } from '../../types/log.js'
15
+ import type { Compute } from '../../types/utils.js'
16
+ import { parseEventLogs } from '../../utils/abi/parseEventLogs.js'
17
+ import * as Abis from '../Abis.js'
18
+ import * as Addresses from '../Addresses.js'
19
+ import type { ReadParameters, WriteParameters } from '../internal/types.js'
20
+ import { defineCall } from '../internal/utils.js'
21
+ import type { TransactionReceipt } from '../Transaction.js'
22
+
23
+ /**
24
+ * Checks whether an address is an initialized native multisig account.
25
+ *
26
+ * @example
27
+ * ```ts
28
+ * import { createClient, http } from 'viem'
29
+ * import { tempo } from 'viem/chains'
30
+ * import { Actions } from 'viem/tempo'
31
+ *
32
+ * const client = createClient({
33
+ * chain: tempo,
34
+ * transport: http(),
35
+ * })
36
+ *
37
+ * const initialized = await Actions.multisig.isInitialized(client, {
38
+ * account: '0x...',
39
+ * })
40
+ * ```
41
+ *
42
+ * @param client - Client.
43
+ * @param parameters - Parameters.
44
+ * @returns Whether the account is an initialized native multisig account.
45
+ */
46
+ export async function isInitialized<
47
+ chain extends Chain | undefined,
48
+ account extends Account | undefined,
49
+ >(
50
+ client: Client<Transport, chain, account>,
51
+ parameters: isInitialized.Parameters,
52
+ ): Promise<isInitialized.ReturnValue> {
53
+ const { account, ...rest } = parameters
54
+ return readContract(client, {
55
+ ...rest,
56
+ ...isInitialized.call({ account }),
57
+ })
58
+ }
59
+
60
+ export namespace isInitialized {
61
+ export type Parameters = ReadParameters & Args
62
+
63
+ export type Args = {
64
+ /** Account address. */
65
+ account: Address
66
+ }
67
+
68
+ export type ReturnValue = ReadContractReturnType<
69
+ typeof Abis.nativeMultisig,
70
+ 'isMultisigAccount',
71
+ never
72
+ >
73
+
74
+ /**
75
+ * Defines a call to the precompile's `isMultisigAccount` function.
76
+ *
77
+ * Can be passed to [`multicall`](https://viem.sh/docs/contract/multicall).
78
+ *
79
+ * @param args - Arguments.
80
+ * @returns The call.
81
+ */
82
+ export function call(args: Args) {
83
+ return defineCall({
84
+ address: Addresses.nativeMultisig,
85
+ abi: Abis.nativeMultisig,
86
+ args: [args.account],
87
+ functionName: 'isMultisigAccount',
88
+ })
89
+ }
90
+ }
91
+
92
+ /**
93
+ * Gets the current configuration for an initialized native multisig account.
94
+ *
95
+ * @example
96
+ * ```ts
97
+ * import { createClient, http } from 'viem'
98
+ * import { tempo } from 'viem/chains'
99
+ * import { Actions } from 'viem/tempo'
100
+ *
101
+ * const client = createClient({
102
+ * chain: tempo,
103
+ * transport: http(),
104
+ * })
105
+ *
106
+ * const config = await Actions.multisig.getConfig(client, {
107
+ * account: '0x...',
108
+ * })
109
+ * ```
110
+ *
111
+ * @param client - Client.
112
+ * @param parameters - Parameters.
113
+ * @returns The current version, threshold, and owners.
114
+ */
115
+ export async function getConfig<
116
+ chain extends Chain | undefined,
117
+ account extends Account | undefined,
118
+ >(
119
+ client: Client<Transport, chain, account>,
120
+ parameters: getConfig.Parameters,
121
+ ): Promise<getConfig.ReturnValue> {
122
+ const { account, ...rest } = parameters
123
+ return readContract(client, {
124
+ ...rest,
125
+ ...getConfig.call({ account }),
126
+ })
127
+ }
128
+
129
+ export namespace getConfig {
130
+ export type Parameters = ReadParameters & Args
131
+
132
+ export type Args = {
133
+ /** Initialized multisig account address. */
134
+ account: Address
135
+ }
136
+
137
+ export type ReturnValue = ReadContractReturnType<
138
+ typeof Abis.nativeMultisig,
139
+ 'getConfig',
140
+ never
141
+ >
142
+
143
+ /**
144
+ * Defines a call to the `getConfig` function.
145
+ *
146
+ * Can be passed to [`multicall`](https://viem.sh/docs/contract/multicall).
147
+ *
148
+ * @param args - Arguments.
149
+ * @returns The call.
150
+ */
151
+ export function call(args: Args) {
152
+ return defineCall({
153
+ address: Addresses.nativeMultisig,
154
+ abi: Abis.nativeMultisig,
155
+ args: [args.account],
156
+ functionName: 'getConfig',
157
+ })
158
+ }
159
+ }
160
+
161
+ /**
162
+ * Replaces the current configuration for a native multisig account.
163
+ *
164
+ * The transaction must be authorized directly by the account's current owner
165
+ * quorum. Local owner-signing flows can include {@link updateConfig.call} in a
166
+ * prepared transaction before collecting approvals.
167
+ *
168
+ * @example
169
+ * ```ts
170
+ * import { createClient, http } from 'viem'
171
+ * import { sendTransactionSync } from 'viem/actions'
172
+ * import { tempoLocalnet } from 'viem/chains'
173
+ * import { Account, Actions } from 'viem/tempo'
174
+ *
175
+ * const owner = Account.fromSecp256k1(
176
+ * '0xac0974bec39a17e36ba4a6b4d238ff944bacb478cbed5efcae784d7bf4f2ff80',
177
+ * )
178
+ * const account = Account.fromMultisig({ owners: [owner] })
179
+ * const client = createClient({
180
+ * chain: tempoLocalnet,
181
+ * transport: http(),
182
+ * })
183
+ *
184
+ * await sendTransactionSync(client, {
185
+ * account,
186
+ * to: account.address,
187
+ * })
188
+ *
189
+ * const hash = await Actions.multisig.updateConfig(client, {
190
+ * account,
191
+ * threshold: 1,
192
+ * owners: [{ owner: owner.address, weight: 1 }],
193
+ * })
194
+ * ```
195
+ *
196
+ * @param client - Client.
197
+ * @param parameters - Parameters.
198
+ * @returns The transaction hash.
199
+ */
200
+ export async function updateConfig<
201
+ chain extends Chain | undefined,
202
+ account extends Account | undefined,
203
+ >(
204
+ client: Client<Transport, chain, account>,
205
+ parameters: updateConfig.Parameters<chain, account>,
206
+ ): Promise<updateConfig.ReturnValue> {
207
+ return updateConfig.inner(writeContract, client, parameters)
208
+ }
209
+
210
+ export namespace updateConfig {
211
+ export type Parameters<
212
+ chain extends Chain | undefined = Chain | undefined,
213
+ account extends Account | undefined = Account | undefined,
214
+ > = WriteParameters<chain, account> & Args
215
+
216
+ export type Args = {
217
+ /** New multisig owners and their weights. */
218
+ owners: MultisigConfig.Config['owners']
219
+ /** New signature weight required to authorize the account. */
220
+ threshold: MultisigConfig.Config['threshold']
221
+ }
222
+
223
+ export type ReturnValue = WriteContractReturnType
224
+
225
+ // TODO: exhaustive error type
226
+ export type ErrorType = BaseErrorType
227
+
228
+ /** @internal */
229
+ export async function inner<
230
+ action extends typeof writeContract | typeof writeContractSync,
231
+ chain extends Chain | undefined,
232
+ account extends Account | undefined,
233
+ >(
234
+ action: action,
235
+ client: Client<Transport, chain, account>,
236
+ parameters: Parameters<chain, account>,
237
+ ): Promise<ReturnType<action>> {
238
+ const { owners, threshold, ...rest } = parameters
239
+ return (await action(client, {
240
+ ...rest,
241
+ ...updateConfig.call({ owners, threshold }),
242
+ } as never)) as never
243
+ }
244
+
245
+ /**
246
+ * Defines a call to the `updateConfig` function.
247
+ *
248
+ * Can be passed as a parameter to:
249
+ * - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate gas
250
+ * - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the update
251
+ * - [`sendCalls`](https://viem.sh/docs/actions/wallet/sendCalls): include the update in a call batch
252
+ *
253
+ * @example
254
+ * ```ts
255
+ * import { Actions } from 'viem/tempo'
256
+ *
257
+ * const call = Actions.multisig.updateConfig.call({
258
+ * threshold: 1,
259
+ * owners: [{ owner: '0x...', weight: 1 }],
260
+ * })
261
+ * ```
262
+ *
263
+ * @param args - New multisig configuration.
264
+ * @returns The call.
265
+ */
266
+ export function call(args: Args) {
267
+ const config = MultisigConfig.from(args)
268
+ return defineCall({
269
+ address: Addresses.nativeMultisig,
270
+ abi: Abis.nativeMultisig,
271
+ args: [config.threshold, config.owners],
272
+ functionName: 'updateConfig',
273
+ })
274
+ }
275
+
276
+ /**
277
+ * Extracts the `MultisigConfigUpdated` event from logs.
278
+ *
279
+ * @param logs - Transaction logs.
280
+ * @returns The configuration update event.
281
+ */
282
+ export function extractEvent(logs: Log[]) {
283
+ const [log] = parseEventLogs({
284
+ abi: Abis.nativeMultisig,
285
+ logs,
286
+ eventName: 'MultisigConfigUpdated',
287
+ strict: true,
288
+ })
289
+ if (!log) throw new Error('`MultisigConfigUpdated` event not found.')
290
+ return log
291
+ }
292
+ }
293
+
294
+ /**
295
+ * Replaces a native multisig configuration and waits for confirmation.
296
+ *
297
+ * @example
298
+ * ```ts
299
+ * import { createWalletClient, custom, type EIP1193Provider } from 'viem'
300
+ * import { tempo } from 'viem/chains'
301
+ * import { Actions } from 'viem/tempo'
302
+ *
303
+ * declare const provider: EIP1193Provider
304
+ *
305
+ * const client = createWalletClient({
306
+ * account: '0x...',
307
+ * chain: tempo,
308
+ * transport: custom(provider),
309
+ * })
310
+ *
311
+ * const { receipt } = await Actions.multisig.updateConfigSync(client, {
312
+ * threshold: 1,
313
+ * owners: [{ owner: '0x...', weight: 1 }],
314
+ * })
315
+ * ```
316
+ *
317
+ * @param client - Client.
318
+ * @param parameters - Parameters.
319
+ * @returns The updated configuration event and transaction receipt.
320
+ */
321
+ export async function updateConfigSync<
322
+ chain extends Chain | undefined,
323
+ account extends Account | undefined,
324
+ >(
325
+ client: Client<Transport, chain, account>,
326
+ parameters: updateConfigSync.Parameters<chain, account>,
327
+ ): Promise<updateConfigSync.ReturnValue> {
328
+ const { throwOnReceiptRevert = true, ...rest } = parameters
329
+ const receipt = await updateConfig.inner(writeContractSync, client, {
330
+ ...rest,
331
+ throwOnReceiptRevert,
332
+ } as never)
333
+ const { args } = updateConfig.extractEvent(receipt.logs)
334
+ return { ...args, receipt } as never
335
+ }
336
+
337
+ export namespace updateConfigSync {
338
+ export type Parameters<
339
+ chain extends Chain | undefined = Chain | undefined,
340
+ account extends Account | undefined = Account | undefined,
341
+ > = updateConfig.Parameters<chain, account>
342
+
343
+ export type Args = updateConfig.Args
344
+
345
+ export type ReturnValue = Compute<
346
+ GetEventArgs<
347
+ typeof Abis.nativeMultisig,
348
+ 'MultisigConfigUpdated',
349
+ { IndexedOnly: false; Required: true }
350
+ > & {
351
+ receipt: TransactionReceipt
352
+ }
353
+ >
354
+
355
+ export type ErrorType = updateConfig.ErrorType
356
+ }
@@ -15,9 +15,14 @@ import { keccak256 } from '../utils/hash/keccak256.js'
15
15
  import type { SerializeTransactionFn } from '../utils/transaction/serializeTransaction.js'
16
16
  import type { Account, MultisigAccount } from './Account.js'
17
17
  import { getMetadata } from './actions/accessKey.js'
18
+ import * as multisig from './actions/multisig.js'
18
19
  import * as Formatters from './Formatters.js'
19
20
  import type { Hardfork } from './Hardfork.js'
20
21
  import * as Concurrent from './internal/concurrent.js'
22
+ import {
23
+ createMultisigStateResolver,
24
+ getMultisigOwnerStates,
25
+ } from './internal/multisig.js'
21
26
  import * as Transaction from './Transaction.js'
22
27
 
23
28
  const maxExpirySecs = 25
@@ -60,7 +65,10 @@ export const chainConfig = {
60
65
  | undefined
61
66
  feePayerSignature?: Transaction.TransactionSerializableTempo['feePayerSignature']
62
67
  from?: Address | undefined
63
- multisig?: MultisigConfig.Config | undefined
68
+ keyData?: Hex.Hex | undefined
69
+ keyType?: 'p256' | 'secp256k1' | 'webAuthn' | undefined
70
+ multisig?: Address | MultisigConfig.Config | undefined
71
+ multisigOwnerStates?: Transaction.TransactionRequestTempo['multisigOwnerStates']
64
72
  signatures?: readonly unknown[] | undefined
65
73
  }
66
74
 
@@ -82,36 +90,88 @@ export const chainConfig = {
82
90
  return request as unknown as typeof r
83
91
  }
84
92
 
85
- // Native multisig (TIP-1061). The transaction sender is the derived
86
- // multisig account, not a signing account (owner accounts only contribute
87
- // approvals later via `signTransaction`). Derive the sender from the
88
- // config; core fills nonce/gas/fees for it via `request.from`, and the
89
- // serializer auto-detects bootstrap (`init`) from `nonce == 0`.
93
+ // Native multisig (TIP-1061). The transaction sender is the stable
94
+ // multisig address, not a signing account. Initial configs support
95
+ // bootstrap; initialized accounts can be reconstructed from their
96
+ // address and resolved through the multisig precompile.
90
97
  //
91
98
  // The config is taken from an explicit `multisig` field, or inferred from
92
99
  // a multisig account (so callers can just pass `account` to
93
100
  // `prepareTransactionRequest` without also passing `multisig`).
94
- const multisig =
101
+ const multisigIdentity =
95
102
  request.multisig ??
96
103
  (request.account?.source === 'multisig'
97
- ? (request.account as MultisigAccount).config
104
+ ? ((request.account as MultisigAccount).config ??
105
+ request.account.address)
98
106
  : undefined)
99
- if (multisig) {
100
- const config = MultisigConfig.from(multisig)
101
- request.multisig = config
102
- request.from = MultisigConfig.getAddress(config)
103
- request.multisigInit = {
104
- salt: config.salt ?? MultisigConfig.zeroSalt,
105
- threshold: Number(config.threshold),
106
- owners: config.owners.map((owner) => ({
107
- owner: owner.owner,
108
- weight: Number(owner.weight),
109
- })),
107
+ if (multisigIdentity) {
108
+ const initialConfig =
109
+ typeof multisigIdentity === 'string'
110
+ ? undefined
111
+ : MultisigConfig.from(multisigIdentity)
112
+ const multisigAddress =
113
+ typeof multisigIdentity === 'string'
114
+ ? multisigIdentity
115
+ : MultisigConfig.getAddress(initialConfig!)
116
+ request.multisig = initialConfig ?? multisigIdentity
117
+ request.from = multisigAddress
118
+ // Key types are not part of the config, so conservatively model every
119
+ // approval as a maximum-size WebAuthn signature.
120
+ if (typeof request.keyType === 'undefined') {
121
+ request.keyType = 'webAuthn'
122
+ if (typeof request.keyData === 'undefined') request.keyData = '0x0578'
123
+ }
124
+ const getState = createMultisigStateResolver((account) =>
125
+ getAction(client, multisig.getConfig, 'getConfig')({ account }),
126
+ )
127
+ const ownerStates =
128
+ request.account?.source === 'multisig'
129
+ ? getMultisigOwnerStates(
130
+ request.account as MultisigAccount,
131
+ getState,
132
+ )
133
+ : undefined
134
+ const state = ownerStates
135
+ ? (await ownerStates)[0]!
136
+ : await getState(multisigAddress)
137
+ if (!initialConfig && !state.initialized)
138
+ throw new Error(
139
+ 'Cannot prepare an uninitialized multisig account from an address. Provide its initial config instead.',
140
+ )
141
+ if (typeof request.multisigVersion === 'undefined')
142
+ request.multisigVersion = state.version
143
+ const authorizationSignature = request.keyAuthorization?.signature
144
+ if (
145
+ authorizationSignature?.type === 'multisig' &&
146
+ typeof authorizationSignature.init !== 'undefined'
147
+ )
148
+ request.keyAuthorization = {
149
+ ...request.keyAuthorization!,
150
+ signature: SignatureEnvelope.from({
151
+ initialConfig: authorizationSignature.init,
152
+ signatures: authorizationSignature.signatures,
153
+ }),
154
+ }
155
+ const keyAuthorizationSignature = request.keyAuthorization?.signature
156
+ const keyAuthorizationInitializes =
157
+ keyAuthorizationSignature?.type === 'multisig' &&
158
+ typeof keyAuthorizationSignature.init !== 'undefined'
159
+ if (initialConfig && !keyAuthorizationInitializes)
160
+ request.multisigInit = {
161
+ salt: initialConfig.salt ?? MultisigConfig.zeroSalt,
162
+ threshold: Number(initialConfig.threshold),
163
+ owners: initialConfig.owners.map((owner) => ({
164
+ owner: owner.owner,
165
+ weight: Number(owner.weight),
166
+ })),
167
+ }
168
+ if (ownerStates) {
169
+ const states = await ownerStates
170
+ if (states.length > 0) request.multisigOwnerStates = states
110
171
  }
111
- request.multisigSignatureCount ??= inferMultisigSignatureCount(config)
112
172
  // A non-multisig `account` (e.g. the client's default) isn't the sender,
113
173
  // so drop it: core then fills nonce/gas/fees for the multisig sender via
114
- // `request.from`. A multisig account *is* the sender — keep it so the
174
+ // `request.from`. A multisig account *is* the sender, so keep it so the
115
175
  // prepared request can be sent without re-passing `account`.
116
176
  if (request.account?.source !== 'multisig') delete request.account
117
177
  }
@@ -121,7 +181,7 @@ export const chainConfig = {
121
181
  const useExpiringNonce = await (async () => {
122
182
  if (request.nonceKey === 'expiring' || request.nonceKey === maxUint256)
123
183
  return true
124
- if (multisig) return false
184
+ if (multisigIdentity) return false
125
185
  if (request.feePayer && typeof request.nonceKey === 'undefined')
126
186
  return true
127
187
  const account = request.account as
@@ -286,16 +346,3 @@ export const chainConfig = {
286
346
  } as const satisfies viem_ChainConfig & { blockTime: number }
287
347
 
288
348
  export type ChainConfig = typeof chainConfig
289
-
290
- function inferMultisigSignatureCount(config: MultisigConfig.Config) {
291
- const threshold = Number(config.threshold)
292
- const weights = config.owners
293
- .map((owner) => Number(owner.weight))
294
- .sort((a, b) => b - a)
295
- let total = 0
296
- for (const [index, weight] of weights.entries()) {
297
- total += weight
298
- if (total >= threshold) return index + 1
299
- }
300
- return weights.length
301
- }
@@ -0,0 +1,72 @@
1
+ import type { Address } from 'abitype'
2
+ import type { MultisigConfig } from 'ox/tempo'
3
+ import { BaseError } from '../../errors/base.js'
4
+ import { ContractFunctionRevertedError } from '../../errors/contract.js'
5
+ import type { MultisigAccount } from '../Account.js'
6
+ import type { MultisigOwnerState } from '../Transaction.js'
7
+
8
+ /** @internal */
9
+ export function createMultisigStateResolver(
10
+ getConfig: (account: Address) => Promise<{
11
+ owners: MultisigConfig.Config['owners']
12
+ threshold: MultisigConfig.Config['threshold']
13
+ version: bigint
14
+ }>,
15
+ ) {
16
+ // Cache only within one operation because config versions can change between requests.
17
+ const cache = new Map<Address, Promise<MultisigOwnerState>>()
18
+ return (account: Address): Promise<MultisigOwnerState> => {
19
+ const address = account.toLowerCase() as Address
20
+ const cached = cache.get(address)
21
+ if (cached) return cached
22
+
23
+ const state = (async () => {
24
+ try {
25
+ const config = await getConfig(account)
26
+ return {
27
+ account,
28
+ config: { owners: config.owners, threshold: config.threshold },
29
+ initialized: true,
30
+ version: config.version,
31
+ }
32
+ } catch (error) {
33
+ const cause =
34
+ error instanceof BaseError
35
+ ? error.walk(
36
+ (error) => error instanceof ContractFunctionRevertedError,
37
+ )
38
+ : undefined
39
+ if (
40
+ !(cause instanceof ContractFunctionRevertedError) ||
41
+ cause.data?.errorName !== 'NotMultisigAccount'
42
+ )
43
+ throw error
44
+ return { account, initialized: false, version: 0n }
45
+ }
46
+ })()
47
+ cache.set(address, state)
48
+ return state
49
+ }
50
+ }
51
+
52
+ /** @internal */
53
+ export function getMultisigOwnerStates(
54
+ account: MultisigAccount,
55
+ getState: (account: Address) => Promise<MultisigOwnerState>,
56
+ ): Promise<readonly MultisigOwnerState[]> {
57
+ // Collect locally first so all config reads start in one latency wave.
58
+ const owners: MultisigAccount[] = [account]
59
+ const seen = new Set<Address>([account.address.toLowerCase() as Address])
60
+ const visit = (account: MultisigAccount) => {
61
+ for (const owner of account.owners) {
62
+ if (owner.source !== 'multisig') continue
63
+ const address = owner.address.toLowerCase() as Address
64
+ if (seen.has(address)) continue
65
+ seen.add(address)
66
+ owners.push(owner as MultisigAccount)
67
+ visit(owner as MultisigAccount)
68
+ }
69
+ }
70
+ visit(account)
71
+ return Promise.all(owners.map((owner) => getState(owner.address)))
72
+ }