viem 2.55.4 → 2.55.5

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (30) hide show
  1. package/CHANGELOG.md +12 -0
  2. package/_cjs/actions/public/multicall.js +65 -18
  3. package/_cjs/actions/public/multicall.js.map +1 -1
  4. package/_cjs/actions/wallet/prepareTransactionRequest.js +8 -1
  5. package/_cjs/actions/wallet/prepareTransactionRequest.js.map +1 -1
  6. package/_cjs/errors/version.js +1 -1
  7. package/_cjs/tempo/Transaction.js +1 -1
  8. package/_cjs/tempo/Transaction.js.map +1 -1
  9. package/_esm/actions/public/multicall.js +65 -18
  10. package/_esm/actions/public/multicall.js.map +1 -1
  11. package/_esm/actions/wallet/prepareTransactionRequest.js +25 -1
  12. package/_esm/actions/wallet/prepareTransactionRequest.js.map +1 -1
  13. package/_esm/errors/version.js +1 -1
  14. package/_esm/tempo/Transaction.js +1 -1
  15. package/_esm/tempo/Transaction.js.map +1 -1
  16. package/_types/actions/public/multicall.d.ts +2 -1
  17. package/_types/actions/public/multicall.d.ts.map +1 -1
  18. package/_types/actions/wallet/prepareTransactionRequest.d.ts.map +1 -1
  19. package/_types/chains/definitions/tempo.d.ts +22 -22
  20. package/_types/chains/definitions/tempoDevnet.d.ts +22 -22
  21. package/_types/chains/definitions/tempoLocalnet.d.ts +22 -22
  22. package/_types/chains/definitions/tempoModerato.d.ts +22 -22
  23. package/_types/errors/version.d.ts +1 -1
  24. package/_types/tempo/chainConfig.d.ts +11 -11
  25. package/_types/tempo/zones/zone.d.ts +66 -66
  26. package/actions/public/multicall.ts +92 -12
  27. package/actions/wallet/prepareTransactionRequest.ts +28 -1
  28. package/errors/version.ts +1 -1
  29. package/package.json +2 -2
  30. package/tempo/Transaction.ts +1 -1
@@ -32,6 +32,11 @@ import {
32
32
  getContractError,
33
33
  } from '../../utils/errors/getContractError.js'
34
34
  import { getAction } from '../../utils/getAction.js'
35
+ import {
36
+ type CreateBatchSchedulerErrorType,
37
+ createBatchScheduler,
38
+ } from '../../utils/promise/createBatchScheduler.js'
39
+ import { stringify } from '../../utils/stringify.js'
35
40
  import type { CallParameters } from './call.js'
36
41
  import { type ReadContractErrorType, readContract } from './readContract.js'
37
42
 
@@ -82,6 +87,7 @@ export type MulticallReturnType<
82
87
  >
83
88
 
84
89
  export type MulticallErrorType =
90
+ | CreateBatchSchedulerErrorType
85
91
  | GetChainContractAddressErrorType
86
92
  | ReadContractErrorType
87
93
  | GetContractErrorReturnType<
@@ -89,6 +95,14 @@ export type MulticallErrorType =
89
95
  >
90
96
  | ErrorType
91
97
 
98
+ type Aggregate3Call = {
99
+ allowFailure: boolean
100
+ callData: Hex
101
+ target: Address
102
+ }
103
+
104
+ type Aggregate3Calls = Aggregate3Call[]
105
+
92
106
  /**
93
107
  * Similar to [`readContract`](https://viem.sh/docs/contract/readContract), but batches up multiple functions on a contract in a single RPC call via the [`multicall3` contract](https://github.com/mds1/multicall).
94
108
  *
@@ -169,12 +183,6 @@ export async function multicall<
169
183
  )
170
184
  })()
171
185
 
172
- type Aggregate3Calls = {
173
- allowFailure: boolean
174
- callData: Hex
175
- target: Address
176
- }[]
177
-
178
186
  const chunkedCalls: Aggregate3Calls[] = [[]]
179
187
  let currentChunk = 0
180
188
  let currentChunkSize = 0
@@ -227,9 +235,27 @@ export async function multicall<
227
235
  }
228
236
  }
229
237
 
238
+ const batching = Boolean(client.batch?.multicall)
239
+ const batches = batching
240
+ ? chunkedCalls.flatMap((calls) => calls.map((call) => [call]))
241
+ : chunkedCalls
230
242
  const aggregate3Results = await Promise.allSettled(
231
- chunkedCalls.map((calls) =>
232
- getAction(
243
+ batches.map((calls) => {
244
+ if (batching)
245
+ return scheduleMulticall(client, {
246
+ account,
247
+ authorizationList,
248
+ batchSize,
249
+ blockHash,
250
+ blockNumber,
251
+ blockOverrides,
252
+ blockTag,
253
+ call: calls[0]!,
254
+ multicallAddress,
255
+ requireCanonical,
256
+ stateOverride,
257
+ }).then((result) => [result])
258
+ return getAction(
233
259
  client,
234
260
  readContract,
235
261
  'readContract',
@@ -248,8 +274,8 @@ export async function multicall<
248
274
  functionName: 'aggregate3',
249
275
  requireCanonical,
250
276
  stateOverride,
251
- }),
252
- ),
277
+ })
278
+ }),
253
279
  )
254
280
 
255
281
  const results = []
@@ -260,7 +286,7 @@ export async function multicall<
260
286
  // then append the failure reason to each contract result.
261
287
  if (result.status === 'rejected') {
262
288
  if (!allowFailure) throw result.reason
263
- for (let j = 0; j < chunkedCalls[i].length; j++) {
289
+ for (let j = 0; j < batches[i].length; j++) {
264
290
  results.push({
265
291
  status: 'failure',
266
292
  error: result.reason,
@@ -277,7 +303,7 @@ export async function multicall<
277
303
  const { returnData, success } = aggregate3Result[j]
278
304
 
279
305
  // Extract the request call data from the original call.
280
- const { callData } = chunkedCalls[i][j]
306
+ const { callData } = batches[i][j]
281
307
 
282
308
  // Extract the contract config for this call from the `contracts` argument
283
309
  // for decoding.
@@ -313,3 +339,57 @@ export async function multicall<
313
339
  throw new BaseError('multicall results mismatch')
314
340
  return results as MulticallReturnType<contracts, allowFailure>
315
341
  }
342
+
343
+ type ScheduleMulticallParameters = Pick<
344
+ MulticallParameters,
345
+ | 'account'
346
+ | 'authorizationList'
347
+ | 'blockHash'
348
+ | 'blockNumber'
349
+ | 'blockOverrides'
350
+ | 'blockTag'
351
+ | 'requireCanonical'
352
+ | 'stateOverride'
353
+ > & {
354
+ batchSize: number
355
+ call: Aggregate3Call
356
+ multicallAddress: Address | null
357
+ }
358
+
359
+ async function scheduleMulticall(
360
+ client: Client<Transport>,
361
+ parameters: ScheduleMulticallParameters,
362
+ ) {
363
+ const { batchSize, call, multicallAddress, ...rest } = parameters
364
+ const { wait = 0 } =
365
+ typeof client.batch?.multicall === 'object' ? client.batch.multicall : {}
366
+ const { schedule } = createBatchScheduler({
367
+ id: stringify(['multicall', client.uid, batchSize, multicallAddress, rest]),
368
+ wait,
369
+ shouldSplitBatch(calls: Aggregate3Calls) {
370
+ if (batchSize === 0) return false
371
+ const size = calls.reduce(
372
+ (size, { callData }) => size + (callData.length - 2) / 2,
373
+ 0,
374
+ )
375
+ return size > batchSize
376
+ },
377
+ fn: (calls: Aggregate3Calls) =>
378
+ getAction(
379
+ client,
380
+ readContract,
381
+ 'readContract',
382
+ )({
383
+ ...(multicallAddress === null
384
+ ? { code: multicall3Bytecode }
385
+ : { address: multicallAddress }),
386
+ ...rest,
387
+ abi: multicall3Abi,
388
+ args: [calls],
389
+ functionName: 'aggregate3',
390
+ }),
391
+ })
392
+
393
+ const [result] = await schedule(call)
394
+ return result
395
+ }
@@ -337,7 +337,7 @@ export async function prepareTransactionRequest<
337
337
  return chainId
338
338
  }
339
339
 
340
- const account = account_ ? parseAccount(account_) : account_
340
+ let account = account_ ? parseAccount(account_) : account_
341
341
 
342
342
  let nonce = request.nonce
343
343
  if (
@@ -366,6 +366,8 @@ export async function prepareTransactionRequest<
366
366
  },
367
367
  )
368
368
  nonce ??= request.nonce
369
+ const sender = request.account ?? (request as TransactionRequest).from
370
+ account = sender ? parseAccount(sender) : undefined
369
371
  }
370
372
 
371
373
  const attemptFill = (() => {
@@ -381,6 +383,31 @@ export async function prepareTransactionRequest<
381
383
  // Do not attempt if `eth_fillTransaction` is not supported.
382
384
  if (supportsFillTransaction.get(client.uid) === false) return false
383
385
 
386
+ // Always attempt if the caller explicitly requested a non-empty set of
387
+ // `parameters` and a fee payer signature is being requested (e.g. Tempo
388
+ // sponsorship via `feePayer: true`/`Account`) and has not already been
389
+ // filled. The fee payer signature can only be obtained as a side effect
390
+ // of `eth_fillTransaction` (the relay co-signs the returned transaction),
391
+ // so it must be called even when nonce/gas/fees are already fully
392
+ // populated, or when the caller's `parameters` option omits `'fees'`/
393
+ // `'gas'` entirely — otherwise the transaction silently ends up without
394
+ // a fee payer signature. This check intentionally runs before the
395
+ // `shouldAttempt` gate below, since fee-payer sponsorship is independent
396
+ // of which parameters the caller opted into filling.
397
+ //
398
+ // The `parameters.length > 0` guard excludes internal micro-prepare calls
399
+ // that explicitly pass an empty `parameters` array (e.g. `estimateGas`'s
400
+ // internal `prepareTransactionRequest` call for local accounts) — those
401
+ // are deliberately scoped to fill nothing, and should not independently
402
+ // re-attempt `eth_fillTransaction` for fee-payer sponsorship.
403
+ if (
404
+ parameters.length > 0 &&
405
+ 'feePayer' in request &&
406
+ (request as any).feePayer &&
407
+ !('feePayerSignature' in request && (request as any).feePayerSignature)
408
+ )
409
+ return true
410
+
384
411
  // Should attempt `eth_fillTransaction` if "fees" or "gas" are required to be populated,
385
412
  // otherwise, can just use the other individual calls.
386
413
  const shouldAttempt = ['fees', 'gas'].some((parameter) =>
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.55.4'
1
+ export const version = '2.55.5'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.55.4",
4
+ "version": "2.55.5",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -234,7 +234,7 @@
234
234
  "@scure/bip39": "1.6.0",
235
235
  "abitype": "1.2.3",
236
236
  "isows": "1.0.7",
237
- "ox": "0.14.30",
237
+ "ox": "0.14.31",
238
238
  "ws": "8.21.0"
239
239
  },
240
240
  "license": "MIT",
@@ -379,7 +379,7 @@ async function serializeTempo(
379
379
  return SignatureEnvelope.from({
380
380
  genesisConfig: transaction.multisig,
381
381
  signatures: sorted,
382
- ...(nonce ? {} : { init: true }),
382
+ ...(nonce || transaction.nonceKey ? {} : { init: true }),
383
383
  })
384
384
  })()
385
385