viem 2.54.0 → 2.54.2

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 (148) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/_cjs/account-abstraction/actions/bundler/estimateUserOperationGas.js +1 -2
  3. package/_cjs/account-abstraction/actions/bundler/estimateUserOperationGas.js.map +1 -1
  4. package/_cjs/chains/definitions/marsCredit.js +23 -0
  5. package/_cjs/chains/definitions/marsCredit.js.map +1 -0
  6. package/_cjs/chains/index.js +10 -8
  7. package/_cjs/chains/index.js.map +1 -1
  8. package/_cjs/clients/createClient.js +1 -0
  9. package/_cjs/clients/createClient.js.map +1 -1
  10. package/_cjs/errors/encoding.js +13 -1
  11. package/_cjs/errors/encoding.js.map +1 -1
  12. package/_cjs/errors/version.js +1 -1
  13. package/_cjs/index.js +8 -6
  14. package/_cjs/index.js.map +1 -1
  15. package/_cjs/tempo/Abis.js +49 -1
  16. package/_cjs/tempo/Abis.js.map +1 -1
  17. package/_cjs/tempo/Addresses.js +3 -1
  18. package/_cjs/tempo/Addresses.js.map +1 -1
  19. package/_cjs/tempo/Client.js +3 -1
  20. package/_cjs/tempo/Client.js.map +1 -1
  21. package/_cjs/tempo/Decorator.js.map +1 -1
  22. package/_cjs/tempo/Selectors.js +8 -1
  23. package/_cjs/tempo/Selectors.js.map +1 -1
  24. package/_cjs/tempo/actions/receivePolicy.js +29 -5
  25. package/_cjs/tempo/actions/receivePolicy.js.map +1 -1
  26. package/_cjs/tempo/actions/virtualAddress.js +2 -5
  27. package/_cjs/tempo/actions/virtualAddress.js.map +1 -1
  28. package/_cjs/tempo/actions/zone.js +69 -48
  29. package/_cjs/tempo/actions/zone.js.map +1 -1
  30. package/_cjs/tokens/definitions/cirbtc.js +16 -0
  31. package/_cjs/tokens/definitions/cirbtc.js.map +1 -0
  32. package/_cjs/tokens/definitions/eurc.js +24 -0
  33. package/_cjs/tokens/definitions/eurc.js.map +1 -0
  34. package/_cjs/tokens/definitions/usdc.js +18 -0
  35. package/_cjs/tokens/definitions/usdc.js.map +1 -1
  36. package/_cjs/tokens/definitions/usyc.js +18 -0
  37. package/_cjs/tokens/definitions/usyc.js.map +1 -0
  38. package/_cjs/tokens/index.js +9 -1
  39. package/_cjs/tokens/index.js.map +1 -1
  40. package/_cjs/tokens/sets.js +102 -0
  41. package/_cjs/tokens/sets.js.map +1 -0
  42. package/_cjs/utils/encoding/fromRlp.js +9 -0
  43. package/_cjs/utils/encoding/fromRlp.js.map +1 -1
  44. package/_esm/account-abstraction/actions/bundler/estimateUserOperationGas.js +1 -2
  45. package/_esm/account-abstraction/actions/bundler/estimateUserOperationGas.js.map +1 -1
  46. package/_esm/chains/definitions/marsCredit.js +20 -0
  47. package/_esm/chains/definitions/marsCredit.js.map +1 -0
  48. package/_esm/chains/index.js +1 -0
  49. package/_esm/chains/index.js.map +1 -1
  50. package/_esm/clients/createClient.js +5 -3
  51. package/_esm/clients/createClient.js.map +1 -1
  52. package/_esm/errors/encoding.js +10 -0
  53. package/_esm/errors/encoding.js.map +1 -1
  54. package/_esm/errors/version.js +1 -1
  55. package/_esm/index.js +1 -1
  56. package/_esm/index.js.map +1 -1
  57. package/_esm/tempo/Abis.js +48 -0
  58. package/_esm/tempo/Abis.js.map +1 -1
  59. package/_esm/tempo/Addresses.js +2 -0
  60. package/_esm/tempo/Addresses.js.map +1 -1
  61. package/_esm/tempo/Client.js +3 -1
  62. package/_esm/tempo/Client.js.map +1 -1
  63. package/_esm/tempo/Decorator.js.map +1 -1
  64. package/_esm/tempo/Selectors.js +7 -0
  65. package/_esm/tempo/Selectors.js.map +1 -1
  66. package/_esm/tempo/actions/receivePolicy.js +32 -5
  67. package/_esm/tempo/actions/receivePolicy.js.map +1 -1
  68. package/_esm/tempo/actions/virtualAddress.js +2 -6
  69. package/_esm/tempo/actions/virtualAddress.js.map +1 -1
  70. package/_esm/tempo/actions/zone.js +96 -49
  71. package/_esm/tempo/actions/zone.js.map +1 -1
  72. package/_esm/tokens/definitions/cirbtc.js +21 -0
  73. package/_esm/tokens/definitions/cirbtc.js.map +1 -0
  74. package/_esm/tokens/definitions/eurc.js +29 -0
  75. package/_esm/tokens/definitions/eurc.js.map +1 -0
  76. package/_esm/tokens/definitions/usdc.js +18 -0
  77. package/_esm/tokens/definitions/usdc.js.map +1 -1
  78. package/_esm/tokens/definitions/usyc.js +23 -0
  79. package/_esm/tokens/definitions/usyc.js.map +1 -0
  80. package/_esm/tokens/index.js +4 -0
  81. package/_esm/tokens/index.js.map +1 -1
  82. package/_esm/tokens/sets.js +104 -0
  83. package/_esm/tokens/sets.js.map +1 -0
  84. package/_esm/utils/encoding/fromRlp.js +12 -1
  85. package/_esm/utils/encoding/fromRlp.js.map +1 -1
  86. package/_types/account-abstraction/actions/bundler/estimateUserOperationGas.d.ts.map +1 -1
  87. package/_types/chains/definitions/marsCredit.d.ts +50 -0
  88. package/_types/chains/definitions/marsCredit.d.ts.map +1 -0
  89. package/_types/chains/index.d.ts +1 -0
  90. package/_types/chains/index.d.ts.map +1 -1
  91. package/_types/clients/createClient.d.ts +4 -3
  92. package/_types/clients/createClient.d.ts.map +1 -1
  93. package/_types/errors/encoding.d.ts +17 -0
  94. package/_types/errors/encoding.d.ts.map +1 -1
  95. package/_types/errors/version.d.ts +1 -1
  96. package/_types/index.d.ts +1 -1
  97. package/_types/index.d.ts.map +1 -1
  98. package/_types/tempo/Abis.d.ts +119 -0
  99. package/_types/tempo/Abis.d.ts.map +1 -1
  100. package/_types/tempo/Addresses.d.ts +2 -0
  101. package/_types/tempo/Addresses.d.ts.map +1 -1
  102. package/_types/tempo/Client.d.ts.map +1 -1
  103. package/_types/tempo/Decorator.d.ts +4 -0
  104. package/_types/tempo/Decorator.d.ts.map +1 -1
  105. package/_types/tempo/Selectors.d.ts +7 -0
  106. package/_types/tempo/Selectors.d.ts.map +1 -1
  107. package/_types/tempo/actions/receivePolicy.d.ts +1 -1
  108. package/_types/tempo/actions/receivePolicy.d.ts.map +1 -1
  109. package/_types/tempo/actions/virtualAddress.d.ts.map +1 -1
  110. package/_types/tempo/actions/zone.d.ts +63 -3
  111. package/_types/tempo/actions/zone.d.ts.map +1 -1
  112. package/_types/tokens/definitions/cirbtc.d.ts +21 -0
  113. package/_types/tokens/definitions/cirbtc.d.ts.map +1 -0
  114. package/_types/tokens/definitions/eurc.d.ts +29 -0
  115. package/_types/tokens/definitions/eurc.d.ts.map +1 -0
  116. package/_types/tokens/definitions/usdc.d.ts +18 -0
  117. package/_types/tokens/definitions/usdc.d.ts.map +1 -1
  118. package/_types/tokens/definitions/usyc.d.ts +23 -0
  119. package/_types/tokens/definitions/usyc.d.ts.map +1 -0
  120. package/_types/tokens/index.d.ts +4 -0
  121. package/_types/tokens/index.d.ts.map +1 -1
  122. package/_types/tokens/sets.d.ts +607 -0
  123. package/_types/tokens/sets.d.ts.map +1 -0
  124. package/_types/utils/encoding/fromRlp.d.ts +3 -3
  125. package/_types/utils/encoding/fromRlp.d.ts.map +1 -1
  126. package/account-abstraction/actions/bundler/estimateUserOperationGas.ts +1 -2
  127. package/chains/definitions/marsCredit.ts +20 -0
  128. package/chains/index.ts +1 -0
  129. package/clients/createClient.ts +5 -3
  130. package/errors/encoding.ts +26 -0
  131. package/errors/version.ts +1 -1
  132. package/index.ts +4 -0
  133. package/package.json +2 -2
  134. package/tempo/Abis.ts +49 -0
  135. package/tempo/Addresses.ts +2 -0
  136. package/tempo/Client.ts +3 -1
  137. package/tempo/Decorator.ts +3 -0
  138. package/tempo/Selectors.ts +8 -0
  139. package/tempo/actions/receivePolicy.ts +39 -4
  140. package/tempo/actions/virtualAddress.ts +2 -8
  141. package/tempo/actions/zone.ts +164 -74
  142. package/tokens/definitions/cirbtc.ts +21 -0
  143. package/tokens/definitions/eurc.ts +29 -0
  144. package/tokens/definitions/usdc.ts +18 -0
  145. package/tokens/definitions/usyc.ts +23 -0
  146. package/tokens/index.ts +4 -0
  147. package/tokens/sets.ts +109 -0
  148. package/utils/encoding/fromRlp.ts +18 -1
package/chains/index.ts CHANGED
@@ -392,6 +392,7 @@ export { mantraDuKongEVMTestnet } from './definitions/mantraDuKongEVMTestnet.js'
392
392
  export { mantraEVM } from './definitions/mantraEVM.js'
393
393
  export { mapProtocol } from './definitions/mapProtocol.js'
394
394
  export { marooTestnet } from './definitions/marooTestnet.js'
395
+ export { marsCredit } from './definitions/marsCredit.js'
395
396
  export { matchain } from './definitions/matchain.js'
396
397
  export { matchainTestnet } from './definitions/matchainTestnet.js'
397
398
  export { mchVerse } from './definitions/mchVerse.js'
@@ -349,9 +349,10 @@ function isPlainObject(value: unknown): value is Record<string, unknown> {
349
349
  /**
350
350
  * Binds an action function to a `client`, returning a parameter-only version
351
351
  * along with any helpers the action exposes. Helpers that need a client
352
- * (`.call`, `.calls`, `.callWithPeriod`, `.estimateGas`, `.simulate`) are bound
353
- * to `client`; pure helpers (`.extractEvent`, `.extractEvents`) are copied
354
- * as-is. Used by decorators that attach namespaced actions to a Client.
352
+ * (`.call`, `.calls`, `.callWithPeriod`, `.estimateGas`, `.prepare`,
353
+ * `.simulate`) are bound to `client`; pure helpers (`.extractEvent`,
354
+ * `.extractEvents`) are copied as-is. Used by decorators that attach
355
+ * namespaced actions to a Client.
355
356
  * @internal
356
357
  */
357
358
  export function bindActionDecorators(
@@ -364,6 +365,7 @@ export function bindActionDecorators(
364
365
  'calls',
365
366
  'callWithPeriod',
366
367
  'estimateGas',
368
+ 'prepare',
367
369
  'simulate',
368
370
  ] as const)
369
371
  if (Object.hasOwn(action, key)) {
@@ -77,6 +77,32 @@ export class RlpDepthLimitExceededError extends BaseError {
77
77
  }
78
78
  }
79
79
 
80
+ export type RlpListBoundaryExceededErrorType = RlpListBoundaryExceededError & {
81
+ name: 'RlpListBoundaryExceededError'
82
+ }
83
+ export class RlpListBoundaryExceededError extends BaseError {
84
+ constructor({ consumed, declared }: { consumed: number; declared: number }) {
85
+ super(
86
+ `RLP list items consumed \`${consumed}\` bytes but the list declared a length of \`${declared}\`.`,
87
+ { name: 'RlpListBoundaryExceededError' },
88
+ )
89
+ }
90
+ }
91
+
92
+ export type RlpTrailingBytesErrorType = RlpTrailingBytesError & {
93
+ name: 'RlpTrailingBytesError'
94
+ }
95
+ export class RlpTrailingBytesError extends BaseError {
96
+ constructor({ count }: { count: number }) {
97
+ super(
98
+ `RLP payload encodes a single item, but \`${count}\` trailing ${
99
+ count === 1 ? 'byte remains' : 'bytes remain'
100
+ }.`,
101
+ { name: 'RlpTrailingBytesError' },
102
+ )
103
+ }
104
+ }
105
+
80
106
  export type SizeOverflowErrorType = SizeOverflowError & {
81
107
  name: 'SizeOverflowError'
82
108
  }
package/errors/version.ts CHANGED
@@ -1 +1 @@
1
- export const version = '2.54.0'
1
+ export const version = '2.54.2'
package/index.ts CHANGED
@@ -880,6 +880,10 @@ export {
880
880
  type InvalidHexValueErrorType,
881
881
  RlpDepthLimitExceededError,
882
882
  type RlpDepthLimitExceededErrorType,
883
+ RlpListBoundaryExceededError,
884
+ type RlpListBoundaryExceededErrorType,
885
+ RlpTrailingBytesError,
886
+ type RlpTrailingBytesErrorType,
883
887
  SizeOverflowError,
884
888
  type SizeOverflowErrorType,
885
889
  } from './errors/encoding.js'
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "viem",
3
3
  "description": "TypeScript Interface for Ethereum",
4
- "version": "2.54.0",
4
+ "version": "2.54.2",
5
5
  "main": "./_cjs/index.js",
6
6
  "module": "./_esm/index.js",
7
7
  "types": "./_types/index.d.ts",
@@ -235,7 +235,7 @@
235
235
  "abitype": "1.2.3",
236
236
  "isows": "1.0.7",
237
237
  "ox": "0.14.29",
238
- "ws": "8.20.1"
238
+ "ws": "8.21.0"
239
239
  },
240
240
  "license": "MIT",
241
241
  "homepage": "https://viem.sh",
package/tempo/Abis.ts CHANGED
@@ -2570,6 +2570,54 @@ export const validatorConfig = [
2570
2570
  },
2571
2571
  ] as const
2572
2572
 
2573
+ export const storageCredits = [
2574
+ {
2575
+ type: 'error',
2576
+ name: 'DelegateCallNotAllowed',
2577
+ inputs: [],
2578
+ },
2579
+ {
2580
+ type: 'error',
2581
+ name: 'InvalidMode',
2582
+ inputs: [],
2583
+ },
2584
+ {
2585
+ type: 'function',
2586
+ name: 'balanceOf',
2587
+ stateMutability: 'view',
2588
+ inputs: [{ name: 'account', type: 'address' }],
2589
+ outputs: [{ type: 'uint64' }],
2590
+ },
2591
+ {
2592
+ type: 'function',
2593
+ name: 'modeOf',
2594
+ stateMutability: 'view',
2595
+ inputs: [{ name: 'account', type: 'address' }],
2596
+ outputs: [{ type: 'uint8' }],
2597
+ },
2598
+ {
2599
+ type: 'function',
2600
+ name: 'budgetOf',
2601
+ stateMutability: 'view',
2602
+ inputs: [{ name: 'account', type: 'address' }],
2603
+ outputs: [{ type: 'uint64' }],
2604
+ },
2605
+ {
2606
+ type: 'function',
2607
+ name: 'setMode',
2608
+ stateMutability: 'nonpayable',
2609
+ inputs: [{ name: 'newMode', type: 'uint8' }],
2610
+ outputs: [],
2611
+ },
2612
+ {
2613
+ type: 'function',
2614
+ name: 'setBudget',
2615
+ stateMutability: 'nonpayable',
2616
+ inputs: [{ name: 'creditBudget', type: 'uint64' }],
2617
+ outputs: [],
2618
+ },
2619
+ ] as const
2620
+
2573
2621
  export const validatorConfigV2 = [
2574
2622
  {
2575
2623
  name: 'getActiveValidators',
@@ -2931,6 +2979,7 @@ export const abis = [
2931
2979
  ...receivePolicyGuard,
2932
2980
  ...signatureVerifier,
2933
2981
  ...stablecoinDex,
2982
+ ...storageCredits,
2934
2983
  ...tip20,
2935
2984
  ...tip20ChannelReserve,
2936
2985
  ...tip20Factory,
@@ -9,6 +9,8 @@ export const pathUsd = '0x20c0000000000000000000000000000000000000'
9
9
  export const receivePolicyGuard = '0xB10C000000000000000000000000000000000000'
10
10
  export const signatureVerifier = '0x5165300000000000000000000000000000000000'
11
11
  export const stablecoinDex = '0xdec0000000000000000000000000000000000000'
12
+ export const storageCredits = '0x1060000000000000000000000000000000000000'
13
+ export const tip20ChannelReserve = '0x4d50500000000000000000000000000000000000'
12
14
  export const tip20Factory = '0x20fc000000000000000000000000000000000000'
13
15
  export const tip403Registry = '0x403c000000000000000000000000000000000000'
14
16
  export const validator = '0xcccccccc00000000000000000000000000000000'
package/tempo/Client.ts CHANGED
@@ -18,6 +18,7 @@ import {
18
18
  import type { Transport } from '../clients/transports/createTransport.js'
19
19
  import { http } from '../clients/transports/http.js'
20
20
  import type { ErrorType } from '../errors/utils.js'
21
+ import { tokens as tokenSets } from '../tokens/sets.js'
21
22
  import type { Account } from '../types/account.js'
22
23
  import type { Chain } from '../types/chain.js'
23
24
  import type { RpcSchema } from '../types/eip1193.js'
@@ -155,7 +156,7 @@ export function createClient<
155
156
  : accountOrAddress,
156
157
  rpcSchema
157
158
  > {
158
- const { chain, feeToken, testnet, transport, ...rest } = parameters
159
+ const { chain, feeToken, testnet, tokens, transport, ...rest } = parameters
159
160
  const baseChain = chain ?? (testnet ? tempoTestnet : tempo)
160
161
  const resolvedChain =
161
162
  feeToken && typeof (baseChain as { extend?: unknown }).extend === 'function'
@@ -166,6 +167,7 @@ export function createClient<
166
167
  return createClient_({
167
168
  ...rest,
168
169
  chain: resolvedChain,
170
+ tokens: tokens ?? tokenSets.tempo,
169
171
  transport: transport ?? http(),
170
172
  } as ClientConfig_)
171
173
  .extend(publicActions)
@@ -5129,6 +5129,9 @@ type BoundActionHelpers<action> = (action extends { call: infer helper }
5129
5129
  (action extends { estimateGas: infer helper }
5130
5130
  ? { estimateGas: BoundHelper<helper> }
5131
5131
  : {}) &
5132
+ (action extends { prepare: infer helper }
5133
+ ? { prepare: BoundHelper<helper> }
5134
+ : {}) &
5132
5135
  (action extends { simulate: infer helper }
5133
5136
  ? { simulate: BoundHelper<helper> }
5134
5137
  : {}) &
@@ -94,6 +94,14 @@ export const stablecoinDex = {
94
94
  withdraw: '0x08fab167',
95
95
  } as const satisfies FunctionSelectors<typeof Abis.stablecoinDex>
96
96
 
97
+ export const storageCredits = {
98
+ balanceOf: '0x70a08231',
99
+ budgetOf: '0x7865e71f',
100
+ modeOf: '0x13668995',
101
+ setBudget: '0xffe295c3',
102
+ setMode: '0x21175b4a',
103
+ } as const satisfies FunctionSelectors<typeof Abis.storageCredits>
104
+
97
105
  export const tip20 = {
98
106
  BURN_BLOCKED_ROLE: '0x32ad9be8',
99
107
  DOMAIN_SEPARATOR: '0x3644e515',
@@ -1,5 +1,5 @@
1
1
  import type { Address } from 'abitype'
2
- import type { ReceivePolicyReceipt } from 'ox/tempo'
2
+ import { type ReceivePolicyReceipt, VirtualAddress } from 'ox/tempo'
3
3
  import type { Account } from '../../accounts/types.js'
4
4
  import { parseAccount } from '../../accounts/utils/parseAccount.js'
5
5
  import type { ReadContractReturnType } from '../../actions/public/readContract.js'
@@ -57,6 +57,25 @@ export type BlockedReason = ReceivePolicyReceipt.BlockedReason
57
57
  /** @internal */
58
58
  const blockedReasons = ['none', 'tokenFilter', 'receivePolicy'] as const
59
59
 
60
+ /** @internal 12-byte prefix shared by TIP-20 token addresses. */
61
+ const tip20Prefix = '0x20c000000000000000000000'
62
+
63
+ /** @internal */
64
+ const systemPrecompiles = [
65
+ Addresses.accountKeychain,
66
+ Addresses.accountRegistrar,
67
+ Addresses.addressRegistry,
68
+ Addresses.feeManager,
69
+ Addresses.nonceManager,
70
+ Addresses.receivePolicyGuard,
71
+ Addresses.signatureVerifier,
72
+ Addresses.stablecoinDex,
73
+ Addresses.tip20ChannelReserve,
74
+ Addresses.tip20Factory,
75
+ Addresses.tip403Registry,
76
+ Addresses.validator,
77
+ ] as const
78
+
60
79
  /**
61
80
  * Claimer authorized to reclaim blocked funds.
62
81
  *
@@ -1253,9 +1272,25 @@ function toPolicyRef(id: bigint): PolicyRef {
1253
1272
 
1254
1273
  /** @internal */
1255
1274
  function resolveClaimer(claimer: Claimer, self: Address): Address {
1256
- if (claimer === 'sender') return zeroAddress
1257
- if (claimer === 'self') return self
1258
- return claimer
1275
+ const recoveryAuthority =
1276
+ claimer === 'sender' ? zeroAddress : claimer === 'self' ? self : claimer
1277
+ assertValidRecoveryAuthority(recoveryAuthority)
1278
+ return recoveryAuthority
1279
+ }
1280
+
1281
+ /** @internal */
1282
+ function assertValidRecoveryAuthority(recoveryAuthority: Address) {
1283
+ if (isAddressEqual(recoveryAuthority, zeroAddress)) return
1284
+ if (VirtualAddress.isVirtual(recoveryAuthority))
1285
+ throw new Error('Recovery authority cannot be a TIP-1022 virtual address.')
1286
+ if (recoveryAuthority.toLowerCase().startsWith(tip20Prefix))
1287
+ throw new Error('Recovery authority cannot be a TIP-20 token address.')
1288
+ if (
1289
+ systemPrecompiles.some((address) =>
1290
+ isAddressEqual(address, recoveryAuthority),
1291
+ )
1292
+ )
1293
+ throw new Error('Recovery authority cannot be a Tempo system precompile.')
1259
1294
  }
1260
1295
 
1261
1296
  /** @internal */
@@ -1,5 +1,6 @@
1
1
  import type { Address } from 'abitype'
2
2
  import * as Hex from 'ox/Hex'
3
+ import { VirtualAddress } from 'ox/tempo'
3
4
  import type { Account } from '../../accounts/types.js'
4
5
  import { readContract } from '../../actions/public/readContract.js'
5
6
  import type { WriteContractReturnType } from '../../actions/wallet/writeContract.js'
@@ -121,7 +122,7 @@ export async function resolve<
121
122
  client: Client<Transport, chain, account>,
122
123
  parameters: resolve.Parameters,
123
124
  ): Promise<resolve.ReturnValue> {
124
- if (!isVirtual(parameters.address)) return parameters.address
125
+ if (!VirtualAddress.isVirtual(parameters.address)) return parameters.address
125
126
 
126
127
  const masterId = Hex.slice(parameters.address, 0, 4)
127
128
  return getMasterAddress(client, { ...parameters, masterId })
@@ -328,10 +329,3 @@ export namespace registerMasterSync {
328
329
  // TODO: exhaustive error type
329
330
  export type ErrorType = BaseErrorType
330
331
  }
331
-
332
- const virtualMagic = '0xfdfdfdfdfdfdfdfdfdfd'
333
-
334
- /** @internal */
335
- function isVirtual(address: string): boolean {
336
- return Hex.slice(address as Hex.Hex, 4, 14).toLowerCase() === virtualMagic
337
- }
@@ -26,7 +26,7 @@ import type {
26
26
  ReadParameters,
27
27
  WriteParameters,
28
28
  } from '../internal/types.js'
29
- import { defineCall } from '../internal/utils.js'
29
+ import { defineCall, pickWriteParameters } from '../internal/utils.js'
30
30
  import * as Storage from '../Storage.js'
31
31
  import type { TransactionReceipt } from '../Transaction.js'
32
32
  import * as ZoneAbis from '../zones/Abis.js'
@@ -40,6 +40,23 @@ export type EncryptedPayload = {
40
40
  tag: Hex.Hex
41
41
  }
42
42
 
43
+ export type PreparedEncryptedDeposit = {
44
+ /** Amount of tokens to deposit. */
45
+ amount: bigint
46
+ /** Parent chain ID (e.g. `42431` for moderato). */
47
+ chainId: number
48
+ /** Encrypted deposit payload. */
49
+ encrypted: EncryptedPayload
50
+ /** Encryption key index from the portal contract. */
51
+ keyIndex: bigint
52
+ /** Zone portal address on the parent chain. */
53
+ portalAddress: Address
54
+ /** Token address or ID to deposit. */
55
+ token: TokenId.TokenIdOrAddress
56
+ /** Zone ID (e.g. `7`). */
57
+ zoneId: number
58
+ }
59
+
43
60
  /**
44
61
  * Deposits tokens into a zone on the parent Tempo chain.
45
62
  * Batches approve and deposit into a single transaction.
@@ -269,46 +286,31 @@ export async function encryptedDeposit<
269
286
  const account_ = account ? parseAccount(account) : undefined
270
287
  if (!account) throw new Error('`account` is required.')
271
288
 
272
- const recipient = parameters.recipient ?? account_?.address
273
- if (!recipient) throw new Error('`recipient` is required.')
274
-
275
- const portalAddress = getPortalAddress(chainId, parameters.zoneId)
276
-
277
- const [publicKey, keyIndex] = await Promise.all([
278
- readContract(client, {
279
- address: portalAddress,
280
- abi: ZoneAbis.zonePortal,
281
- functionName: 'sequencerEncryptionKey',
282
- }),
283
- readContract(client, {
284
- address: portalAddress,
285
- abi: ZoneAbis.zonePortal,
286
- functionName: 'encryptionKeyCount',
287
- }),
288
- ])
289
-
290
- if (keyIndex === 0n) {
291
- throw new Error('No sequencer encryption key configured.')
289
+ if ('encrypted' in parameters) {
290
+ if (parameters.chainId !== chainId) {
291
+ throw new Error(
292
+ 'Prepared encrypted deposit chain ID does not match client chain.',
293
+ )
294
+ }
295
+ return sendTransaction(client, {
296
+ ...pickWriteParameters(parameters as never),
297
+ calls: encryptedDeposit.calls(parameters),
298
+ } as never) as never
292
299
  }
293
300
 
294
- const encrypted = await encryptDepositPayload(
295
- { x: publicKey[0], yParity: publicKey[1] },
296
- recipient,
297
- portalAddress,
298
- keyIndex - 1n,
299
- parameters.memo,
300
- )
301
+ const recipient = parameters.recipient ?? account_?.address
302
+ if (!recipient) throw new Error('`recipient` is required.')
301
303
 
302
- const args = {
303
- ...parameters,
304
- chainId,
305
- encrypted,
306
- keyIndex: keyIndex - 1n,
304
+ const prepared = await encryptedDeposit.prepare(client, {
305
+ amount: parameters.amount,
306
+ memo: parameters.memo,
307
307
  recipient,
308
- }
308
+ token: parameters.token,
309
+ zoneId: parameters.zoneId,
310
+ })
309
311
  return sendTransaction(client, {
310
312
  ...rest,
311
- calls: encryptedDeposit.calls(args),
313
+ calls: encryptedDeposit.calls(prepared),
312
314
  } as never) as never
313
315
  }
314
316
 
@@ -317,10 +319,13 @@ export namespace encryptedDeposit {
317
319
  chain extends Chain | undefined = Chain | undefined,
318
320
  account extends Account | undefined = Account | undefined,
319
321
  > = WriteParameters<chain, account> &
320
- Omit<Args, 'chainId' | 'encrypted' | 'keyIndex' | 'recipient'> & {
321
- /** Recipient address in the zone. @default `account.address` */
322
- recipient?: Address | undefined
323
- }
322
+ (
323
+ | (Omit<Args, 'chainId' | 'encrypted' | 'keyIndex' | 'recipient'> & {
324
+ /** Recipient address in the zone. @default `account.address` */
325
+ recipient?: Address | undefined
326
+ })
327
+ | PreparedEncryptedDeposit
328
+ )
324
329
 
325
330
  export type Args = {
326
331
  /** Amount of tokens to deposit. */
@@ -346,13 +351,111 @@ export namespace encryptedDeposit {
346
351
  // TODO: exhaustive error type
347
352
  export type ErrorType = BaseErrorType
348
353
 
354
+ /**
355
+ * Prepares an encrypted deposit instruction without broadcasting it.
356
+ *
357
+ * @example
358
+ * ```ts
359
+ * import { createClient, http } from 'viem'
360
+ * import { tempoModerato } from 'viem/chains'
361
+ * import { Actions } from 'viem/tempo'
362
+ *
363
+ * const client = createClient({
364
+ * chain: tempoModerato,
365
+ * transport: http(),
366
+ * })
367
+ *
368
+ * const prepared = await Actions.zone.encryptedDeposit.prepare(client, {
369
+ * token: '0x20c0...0001',
370
+ * amount: 1_000_000n,
371
+ * recipient: '0x...',
372
+ * zoneId: 7,
373
+ * })
374
+ * ```
375
+ *
376
+ * @param client - Public client connected to the parent Tempo chain.
377
+ * @param parameters - Encrypted deposit preparation parameters.
378
+ * @returns A prepared encrypted deposit instruction.
379
+ */
380
+ export async function prepare<
381
+ chain extends Chain | undefined,
382
+ account extends Account | undefined,
383
+ >(
384
+ client: Client<Transport, chain, account>,
385
+ parameters: prepare.Parameters,
386
+ ): Promise<prepare.ReturnValue> {
387
+ const chainId = client.chain?.id
388
+ if (!chainId) throw new Error('`chain` is required.')
389
+
390
+ const { amount, memo, recipient, token, zoneId, ...rest } = parameters
391
+ const portalAddress = getPortalAddress(chainId, zoneId)
392
+
393
+ const [publicKey, keyIndex] = await Promise.all([
394
+ readContract(client, {
395
+ ...rest,
396
+ address: portalAddress,
397
+ abi: ZoneAbis.zonePortal,
398
+ functionName: 'sequencerEncryptionKey',
399
+ }),
400
+ readContract(client, {
401
+ ...rest,
402
+ address: portalAddress,
403
+ abi: ZoneAbis.zonePortal,
404
+ functionName: 'encryptionKeyCount',
405
+ }),
406
+ ])
407
+
408
+ if (keyIndex === 0n) {
409
+ throw new Error('No sequencer encryption key configured.')
410
+ }
411
+
412
+ const encrypted = await encryptDepositPayload(
413
+ { x: publicKey[0], yParity: publicKey[1] },
414
+ recipient,
415
+ portalAddress,
416
+ keyIndex - 1n,
417
+ memo,
418
+ )
419
+
420
+ return {
421
+ amount,
422
+ chainId,
423
+ encrypted,
424
+ keyIndex: keyIndex - 1n,
425
+ portalAddress,
426
+ token,
427
+ zoneId,
428
+ }
429
+ }
430
+
431
+ export namespace prepare {
432
+ export type Parameters = ReadParameters & Args
433
+
434
+ export type Args = {
435
+ /** Amount of tokens to deposit. */
436
+ amount: bigint
437
+ /** Optional deposit memo. @default `0x00...00` */
438
+ memo?: Hex.Hex | undefined
439
+ /** Recipient address in the zone. */
440
+ recipient: Address
441
+ /** Token address or ID to deposit. */
442
+ token: TokenId.TokenIdOrAddress
443
+ /** Zone ID (e.g. `7`). */
444
+ zoneId: number
445
+ }
446
+
447
+ export type ReturnValue = PreparedEncryptedDeposit
448
+
449
+ export type ErrorType = RequestErrorType | BaseErrorType
450
+ }
451
+
349
452
  /**
350
453
  * Defines the calls to approve and deposit tokens into a zone (encrypted).
351
454
  *
352
455
  * @param args - Arguments.
353
456
  * @returns The calls.
354
457
  */
355
- export function calls(args: Args) {
458
+ export function calls(args: Args | PreparedEncryptedDeposit) {
356
459
  const { amount, chainId, encrypted, keyIndex, token, zoneId } = args
357
460
  const portalAddress = getPortalAddress(chainId, zoneId)
358
461
  return [
@@ -430,47 +533,34 @@ export async function encryptedDepositSync<
430
533
  const account_ = account ? parseAccount(account) : undefined
431
534
  if (!account) throw new Error('`account` is required.')
432
535
 
433
- const recipient = parameters.recipient ?? account_?.address
434
- if (!recipient) throw new Error('`recipient` is required.')
435
-
436
- const portalAddress = getPortalAddress(chainId, parameters.zoneId)
437
-
438
- const [publicKey, keyIndex] = await Promise.all([
439
- readContract(client, {
440
- address: portalAddress,
441
- abi: ZoneAbis.zonePortal,
442
- functionName: 'sequencerEncryptionKey',
443
- }),
444
- readContract(client, {
445
- address: portalAddress,
446
- abi: ZoneAbis.zonePortal,
447
- functionName: 'encryptionKeyCount',
448
- }),
449
- ])
450
-
451
- if (keyIndex === 0n) {
452
- throw new Error('No sequencer encryption key configured.')
536
+ if ('encrypted' in parameters) {
537
+ if (parameters.chainId !== chainId) {
538
+ throw new Error(
539
+ 'Prepared encrypted deposit chain ID does not match client chain.',
540
+ )
541
+ }
542
+ const receipt = await sendTransactionSync(client, {
543
+ ...pickWriteParameters(parameters as never),
544
+ throwOnReceiptRevert,
545
+ calls: encryptedDeposit.calls(parameters),
546
+ } as never)
547
+ return { receipt }
453
548
  }
454
549
 
455
- const encrypted = await encryptDepositPayload(
456
- { x: publicKey[0], yParity: publicKey[1] },
457
- recipient,
458
- portalAddress,
459
- keyIndex - 1n,
460
- parameters.memo,
461
- )
550
+ const recipient = parameters.recipient ?? account_?.address
551
+ if (!recipient) throw new Error('`recipient` is required.')
462
552
 
463
- const args = {
464
- ...parameters,
465
- chainId,
466
- encrypted,
467
- keyIndex: keyIndex - 1n,
553
+ const prepared = await encryptedDeposit.prepare(client, {
554
+ amount: parameters.amount,
555
+ memo: parameters.memo,
468
556
  recipient,
469
- }
557
+ token: parameters.token,
558
+ zoneId: parameters.zoneId,
559
+ })
470
560
  const receipt = await sendTransactionSync(client, {
471
561
  ...rest,
472
562
  throwOnReceiptRevert,
473
- calls: encryptedDeposit.calls(args),
563
+ calls: encryptedDeposit.calls(prepared),
474
564
  } as never)
475
565
  return { receipt }
476
566
  }
@@ -0,0 +1,21 @@
1
+ import { defineToken } from '../defineToken.js'
2
+
3
+ /**
4
+ * [cirBTC](https://www.circle.com/cirbtc) token, with canonical contract
5
+ * addresses across supported EVM chains.
6
+ *
7
+ * Pass to a Client's `tokens` array, call with a chain id to produce a
8
+ * [token config](/docs/chains/tokens), or read the metadata and `addresses`
9
+ * map directly.
10
+ */
11
+ export const cirbtc = /*#__PURE__*/ defineToken({
12
+ addresses: {
13
+ 1: '0x72DFB2E44f59C5AD2bAFE84314E5b99a7cd5075E', // mainnet
14
+ 5042002: '0xf0C4a4CE82A5746AbAAd9425360Ab04fbBA432BF', // arcTestnet
15
+ 11155111: '0x3a3fe695F684Bf9b9e43CF43C2b895Ea5e392bB3', // sepolia
16
+ },
17
+ currency: 'BTC',
18
+ decimals: 8,
19
+ name: 'Circle Wrapped Bitcoin',
20
+ symbol: 'cirBTC',
21
+ })
@@ -0,0 +1,29 @@
1
+ import { defineToken } from '../defineToken.js'
2
+
3
+ /**
4
+ * [EURC](https://www.circle.com/eurc) token, with canonical contract addresses
5
+ * across supported EVM chains.
6
+ *
7
+ * Pass to a Client's `tokens` array, call with a chain id to produce a
8
+ * [token config](/docs/chains/tokens), or read the metadata and `addresses`
9
+ * map directly.
10
+ */
11
+ export const eurc = /*#__PURE__*/ defineToken({
12
+ addresses: {
13
+ 1: '0x1aBaEA1f7C830bD89Acc67eC4af516284b1bC33c', // mainnet
14
+ 25: '0xA6dE01a2d62C6B5f3525d768f34d276652C554c8', // cronos
15
+ 338: '0x31f7538adb53cF16350e6B0c89d03D91b7D12c46', // cronosTestnet
16
+ 480: '0x1C60ba0A0eD1019e8Eb035E6daF4155A5cE2380B', // worldchain
17
+ 4801: '0xe479EcA5740Ac65d6E1823bea2f1C08Bc14e954F', // worldchainSepolia
18
+ 8453: '0x60a3E35Cc302bFA44Cb288Bc5a4F316Fdb1adb42', // base
19
+ 43113: '0x5E44db7996c682E92a960b65AC713a54AD815c6B', // avalancheFuji
20
+ 43114: '0xC891EB4cbdEFf6e073e859e987815Ed1505c2ACD', // avalanche
21
+ 84532: '0x808456652fdb597867f38412077A9182bf77359F', // baseSepolia
22
+ 5042002: '0x89B50855Aa3bE2F677cD6303Cec089B5F319D72a', // arcTestnet
23
+ 11155111: '0x08210F9170F89Ab7658F0B5E3fF39b0E03C594D4', // sepolia
24
+ },
25
+ currency: 'EUR',
26
+ decimals: 6,
27
+ name: 'EURC',
28
+ symbol: 'EURC',
29
+ })