viem 2.55.5 → 2.55.8

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 (107) hide show
  1. package/CHANGELOG.md +32 -0
  2. package/_cjs/errors/version.js +1 -1
  3. package/_cjs/tempo/Abis.js +1809 -1
  4. package/_cjs/tempo/Abis.js.map +1 -1
  5. package/_cjs/tempo/Decorator.js +25 -2
  6. package/_cjs/tempo/Decorator.js.map +1 -1
  7. package/_cjs/tempo/actions/earn.js +1368 -0
  8. package/_cjs/tempo/actions/earn.js.map +1 -0
  9. package/_cjs/tempo/actions/index.js +2 -1
  10. package/_cjs/tempo/actions/index.js.map +1 -1
  11. package/_cjs/tempo/actions/zone.js +47 -61
  12. package/_cjs/tempo/actions/zone.js.map +1 -1
  13. package/_cjs/tempo/chainConfig.js +3 -1
  14. package/_cjs/tempo/chainConfig.js.map +1 -1
  15. package/_cjs/tempo/errors.js +28 -4
  16. package/_cjs/tempo/errors.js.map +1 -1
  17. package/_cjs/tempo/index.js +2 -1
  18. package/_cjs/tempo/index.js.map +1 -1
  19. package/_cjs/tempo/internal/WithdrawalSenderTag.js +9 -0
  20. package/_cjs/tempo/internal/WithdrawalSenderTag.js.map +1 -0
  21. package/_cjs/tempo/zones/Abis.js +46 -0
  22. package/_cjs/tempo/zones/Abis.js.map +1 -1
  23. package/_cjs/tempo/zones/Addresses.js +17 -0
  24. package/_cjs/tempo/zones/Addresses.js.map +1 -0
  25. package/_cjs/tempo/zones/index.js +2 -2
  26. package/_cjs/tempo/zones/index.js.map +1 -1
  27. package/_cjs/tempo/zones/zone.js +30 -16
  28. package/_cjs/tempo/zones/zone.js.map +1 -1
  29. package/_cjs/utils/blob/fromBlobs.js +5 -2
  30. package/_cjs/utils/blob/fromBlobs.js.map +1 -1
  31. package/_cjs/utils/encoding/fromBytes.js +2 -2
  32. package/_cjs/utils/encoding/fromBytes.js.map +1 -1
  33. package/_esm/errors/version.js +1 -1
  34. package/_esm/tempo/Abis.js +1810 -0
  35. package/_esm/tempo/Abis.js.map +1 -1
  36. package/_esm/tempo/Decorator.js +25 -2
  37. package/_esm/tempo/Decorator.js.map +1 -1
  38. package/_esm/tempo/actions/earn.js +2100 -0
  39. package/_esm/tempo/actions/earn.js.map +1 -0
  40. package/_esm/tempo/actions/index.js +1 -0
  41. package/_esm/tempo/actions/index.js.map +1 -1
  42. package/_esm/tempo/actions/zone.js +104 -119
  43. package/_esm/tempo/actions/zone.js.map +1 -1
  44. package/_esm/tempo/chainConfig.js +4 -1
  45. package/_esm/tempo/chainConfig.js.map +1 -1
  46. package/_esm/tempo/errors.js +25 -2
  47. package/_esm/tempo/errors.js.map +1 -1
  48. package/_esm/tempo/index.js +1 -1
  49. package/_esm/tempo/index.js.map +1 -1
  50. package/_esm/tempo/internal/WithdrawalSenderTag.js +7 -0
  51. package/_esm/tempo/internal/WithdrawalSenderTag.js.map +1 -0
  52. package/_esm/tempo/zones/Abis.js +46 -0
  53. package/_esm/tempo/zones/Abis.js.map +1 -1
  54. package/_esm/tempo/zones/Addresses.js +14 -0
  55. package/_esm/tempo/zones/Addresses.js.map +1 -0
  56. package/_esm/tempo/zones/index.js +2 -1
  57. package/_esm/tempo/zones/index.js.map +1 -1
  58. package/_esm/tempo/zones/zone.js +29 -15
  59. package/_esm/tempo/zones/zone.js.map +1 -1
  60. package/_esm/utils/blob/fromBlobs.js +6 -2
  61. package/_esm/utils/blob/fromBlobs.js.map +1 -1
  62. package/_esm/utils/encoding/fromBytes.js +2 -2
  63. package/_esm/utils/encoding/fromBytes.js.map +1 -1
  64. package/_types/errors/version.d.ts +1 -1
  65. package/_types/tempo/Abis.d.ts +2880 -0
  66. package/_types/tempo/Abis.d.ts.map +1 -1
  67. package/_types/tempo/Decorator.d.ts +499 -41
  68. package/_types/tempo/Decorator.d.ts.map +1 -1
  69. package/_types/tempo/actions/earn.d.ts +7549 -0
  70. package/_types/tempo/actions/earn.d.ts.map +1 -0
  71. package/_types/tempo/actions/index.d.ts +1 -0
  72. package/_types/tempo/actions/index.d.ts.map +1 -1
  73. package/_types/tempo/actions/zone.d.ts +86 -109
  74. package/_types/tempo/actions/zone.d.ts.map +1 -1
  75. package/_types/tempo/chainConfig.d.ts.map +1 -1
  76. package/_types/tempo/errors.d.ts +33 -3
  77. package/_types/tempo/errors.d.ts.map +1 -1
  78. package/_types/tempo/index.d.ts +1 -1
  79. package/_types/tempo/index.d.ts.map +1 -1
  80. package/_types/tempo/internal/WithdrawalSenderTag.d.ts +11 -0
  81. package/_types/tempo/internal/WithdrawalSenderTag.d.ts.map +1 -0
  82. package/_types/tempo/zones/Abis.d.ts +87 -0
  83. package/_types/tempo/zones/Abis.d.ts.map +1 -1
  84. package/_types/tempo/zones/Addresses.d.ts +13 -0
  85. package/_types/tempo/zones/Addresses.d.ts.map +1 -0
  86. package/_types/tempo/zones/index.d.ts +2 -1
  87. package/_types/tempo/zones/index.d.ts.map +1 -1
  88. package/_types/tempo/zones/zone.d.ts +14 -33
  89. package/_types/tempo/zones/zone.d.ts.map +1 -1
  90. package/_types/utils/blob/fromBlobs.d.ts.map +1 -1
  91. package/errors/version.ts +1 -1
  92. package/package.json +2 -2
  93. package/tempo/Abis.ts +1822 -0
  94. package/tempo/Decorator.ts +572 -47
  95. package/tempo/actions/earn.ts +3319 -0
  96. package/tempo/actions/index.ts +1 -0
  97. package/tempo/actions/zone.ts +183 -230
  98. package/tempo/chainConfig.ts +7 -1
  99. package/tempo/errors.ts +54 -6
  100. package/tempo/index.ts +1 -0
  101. package/tempo/internal/WithdrawalSenderTag.ts +20 -0
  102. package/tempo/zones/Abis.ts +46 -0
  103. package/tempo/zones/Addresses.ts +15 -0
  104. package/tempo/zones/index.ts +1 -1
  105. package/tempo/zones/zone.ts +38 -17
  106. package/utils/blob/fromBlobs.ts +6 -2
  107. package/utils/encoding/fromBytes.ts +2 -2
@@ -0,0 +1,2100 @@
1
+ import { Hex } from 'ox';
2
+ import { EarnShares, TokenId } from 'ox/tempo';
3
+ import { parseAccount } from '../../accounts/utils/parseAccount.js';
4
+ import { estimateContractGas } from '../../actions/public/estimateContractGas.js';
5
+ import { getBlockNumber } from '../../actions/public/getBlockNumber.js';
6
+ import { getLogs } from '../../actions/public/getLogs.js';
7
+ import { multicall } from '../../actions/public/multicall.js';
8
+ import { readContract, } from '../../actions/public/readContract.js';
9
+ import { simulateContract, } from '../../actions/public/simulateContract.js';
10
+ import * as internal_Token from '../../actions/token/internal.js';
11
+ import { sendTransaction, } from '../../actions/wallet/sendTransaction.js';
12
+ import { sendTransactionSync } from '../../actions/wallet/sendTransactionSync.js';
13
+ import { writeContractSync } from '../../actions/wallet/writeContractSync.js';
14
+ import { AccountNotFoundError } from '../../errors/account.js';
15
+ import { encodeAbiParameters } from '../../utils/abi/encodeAbiParameters.js';
16
+ import { getAbiItem } from '../../utils/abi/getAbiItem.js';
17
+ import { parseEventLogs } from '../../utils/abi/parseEventLogs.js';
18
+ import { getAddress } from '../../utils/address/getAddress.js';
19
+ import { isAddressEqual } from '../../utils/address/isAddressEqual.js';
20
+ import { observe } from '../../utils/observe.js';
21
+ import { poll } from '../../utils/poll.js';
22
+ import { withResolvers } from '../../utils/promise/withResolvers.js';
23
+ import { stringify } from '../../utils/stringify.js';
24
+ import * as Abis from '../Abis.js';
25
+ import * as Addresses from '../Addresses.js';
26
+ import { GetVaultEngineChangedError, WaitForPrivateDepositTimeoutError, WaitForPrivateRedeemTimeoutError, } from '../errors.js';
27
+ import { defineCall, pickWriteParameters, resolveCallParameters, resolveTokenWithDecimals, } from '../internal/utils.js';
28
+ import * as policyActions from './policy.js';
29
+ import * as tokenActions from './token.js';
30
+ import * as zoneActions from './zone.js';
31
+ /** TIP-403 policy ID that allows every sender, recipient, and mint recipient. */
32
+ export const alwaysAllowPolicyId = 1n;
33
+ /**
34
+ * Creates and attaches an admission-only TIP-403 policy to an Earn vault share
35
+ * token. Existing holders remain able to send shares while recipients and mint
36
+ * recipients must belong to the same whitelist.
37
+ *
38
+ * The action submits three or four sequential transactions and is not atomic.
39
+ * Use {@link validateExitSafePolicy} to verify the final state.
40
+ *
41
+ * @example
42
+ * ```ts
43
+ * import { createClient, http } from 'viem'
44
+ * import { privateKeyToAccount } from 'viem/accounts'
45
+ * import { tempoModerato } from 'viem/chains'
46
+ * import { Actions } from 'viem/tempo'
47
+ *
48
+ * const account = privateKeyToAccount('0x...')
49
+ * const client = createClient({
50
+ * account,
51
+ * chain: tempoModerato,
52
+ * transport: http(),
53
+ * })
54
+ *
55
+ * const { policy, receipts } =
56
+ * await Actions.earn.configureExitSafePolicy(client, {
57
+ * accessAdministrator: '0x...',
58
+ * initialMembers: ['0x...', '0x...'],
59
+ * shareToken: '0x...',
60
+ * })
61
+ * ```
62
+ *
63
+ * @param client - Client authorized to change the vault share token policy.
64
+ * @param parameters - Share token, administrator, and initial members.
65
+ * @returns The configured policy IDs and transaction receipts.
66
+ */
67
+ export async function configureExitSafePolicy(client, parameters) {
68
+ const account_ = parameters.account ?? client.account;
69
+ if (!account_)
70
+ throw new AccountNotFoundError();
71
+ const account = parseAccount(account_);
72
+ const initialMembers = [
73
+ ...new Set(parameters.initialMembers.map((member) => getAddress(member))),
74
+ ];
75
+ if (initialMembers.length === 0)
76
+ throw new Error('At least one initial policy member is required.');
77
+ const eligibility = await policyActions.createSync(client, {
78
+ account,
79
+ addresses: initialMembers,
80
+ chain: client.chain,
81
+ type: 'whitelist',
82
+ });
83
+ const compoundPolicy = await writeContractSync(client, {
84
+ account,
85
+ abi: Abis.tip403Registry,
86
+ address: Addresses.tip403Registry,
87
+ args: [alwaysAllowPolicyId, eligibility.policyId, eligibility.policyId],
88
+ chain: client.chain,
89
+ functionName: 'createCompoundPolicy',
90
+ throwOnReceiptRevert: true,
91
+ });
92
+ const [compoundEvent] = parseEventLogs({
93
+ abi: Abis.tip403Registry,
94
+ eventName: 'CompoundPolicyCreated',
95
+ logs: compoundPolicy.logs,
96
+ strict: true,
97
+ });
98
+ if (!compoundEvent)
99
+ throw new Error('`CompoundPolicyCreated` event not found.');
100
+ const tokenPolicy = await tokenActions.changeTransferPolicySync(client, {
101
+ account,
102
+ chain: client.chain,
103
+ policyId: compoundEvent.args.policyId,
104
+ token: parameters.shareToken,
105
+ });
106
+ const policyAdmin = isAddressEqual(parameters.accessAdministrator, account.address)
107
+ ? undefined
108
+ : await policyActions.setAdminSync(client, {
109
+ account,
110
+ admin: parameters.accessAdministrator,
111
+ chain: client.chain,
112
+ policyId: eligibility.policyId,
113
+ });
114
+ return {
115
+ policy: {
116
+ transferPolicyId: compoundEvent.args.policyId,
117
+ senderPolicyId: alwaysAllowPolicyId,
118
+ recipientPolicyId: eligibility.policyId,
119
+ mintRecipientPolicyId: eligibility.policyId,
120
+ },
121
+ receipts: {
122
+ eligibilityPolicy: eligibility.receipt,
123
+ compoundPolicy,
124
+ tokenPolicy: tokenPolicy.receipt,
125
+ policyAdmin: policyAdmin?.receipt,
126
+ },
127
+ };
128
+ }
129
+ /**
130
+ * Verifies that an Earn vault share token uses the expected exit-safe TIP-403
131
+ * policy and that every required member can receive transfers and mints.
132
+ *
133
+ * @example
134
+ * ```ts
135
+ * import { createClient, http } from 'viem'
136
+ * import { tempoModerato } from 'viem/chains'
137
+ * import { Actions } from 'viem/tempo'
138
+ *
139
+ * const client = createClient({
140
+ * chain: tempoModerato,
141
+ * transport: http(),
142
+ * })
143
+ *
144
+ * await Actions.earn.validateExitSafePolicy(client, {
145
+ * accessAdministrator: '0x...',
146
+ * policy: {
147
+ * transferPolicyId: 3n,
148
+ * senderPolicyId: 1n,
149
+ * recipientPolicyId: 2n,
150
+ * mintRecipientPolicyId: 2n,
151
+ * },
152
+ * requiredMembers: ['0x...', '0x...'],
153
+ * shareToken: '0x...',
154
+ * })
155
+ * ```
156
+ *
157
+ * @param client - Client.
158
+ * @param parameters - Expected policy, administrator, and required members.
159
+ * @returns Nothing when the policy is valid.
160
+ */
161
+ export async function validateExitSafePolicy(client, parameters) {
162
+ const { accessAdministrator, policy, requiredMembers, shareToken, ...rest } = parameters;
163
+ const [tokenPolicyId, compound, simplePolicy, memberResults] = await Promise.all([
164
+ readContract(client, {
165
+ ...rest,
166
+ abi: Abis.tip20,
167
+ address: shareToken,
168
+ functionName: 'transferPolicyId',
169
+ }),
170
+ readContract(client, {
171
+ ...rest,
172
+ abi: Abis.tip403Registry,
173
+ address: Addresses.tip403Registry,
174
+ args: [policy.transferPolicyId],
175
+ functionName: 'compoundPolicyData',
176
+ }),
177
+ readContract(client, {
178
+ ...rest,
179
+ abi: Abis.tip403Registry,
180
+ address: Addresses.tip403Registry,
181
+ args: [policy.recipientPolicyId],
182
+ functionName: 'policyData',
183
+ }),
184
+ Promise.all(requiredMembers.map(async (member) => {
185
+ const [recipient, mintRecipient] = await Promise.all([
186
+ readContract(client, {
187
+ ...rest,
188
+ abi: Abis.tip403Registry,
189
+ address: Addresses.tip403Registry,
190
+ args: [policy.transferPolicyId, member],
191
+ functionName: 'isAuthorizedRecipient',
192
+ }),
193
+ readContract(client, {
194
+ ...rest,
195
+ abi: Abis.tip403Registry,
196
+ address: Addresses.tip403Registry,
197
+ args: [policy.transferPolicyId, member],
198
+ functionName: 'isAuthorizedMintRecipient',
199
+ }),
200
+ ]);
201
+ return { member, mintRecipient, recipient };
202
+ })),
203
+ ]);
204
+ if (tokenPolicyId !== policy.transferPolicyId)
205
+ throw new Error('Earn vault share token transfer policy mismatch.');
206
+ if (compound[0] !== policy.senderPolicyId ||
207
+ compound[1] !== policy.recipientPolicyId ||
208
+ compound[2] !== policy.mintRecipientPolicyId)
209
+ throw new Error('TIP-403 compound policy components mismatch.');
210
+ if (policy.senderPolicyId !== alwaysAllowPolicyId)
211
+ throw new Error('TIP-403 sender policy is not always allow.');
212
+ if (policy.recipientPolicyId !== policy.mintRecipientPolicyId)
213
+ throw new Error('TIP-403 recipient and mint-recipient policies must match.');
214
+ if (simplePolicy[0] !== 0)
215
+ throw new Error('TIP-403 eligibility policy is not a whitelist.');
216
+ if (!isAddressEqual(simplePolicy[1], accessAdministrator))
217
+ throw new Error('TIP-403 access administrator mismatch.');
218
+ const unauthorized = memberResults.find((result) => !result.recipient || !result.mintRecipient);
219
+ if (unauthorized)
220
+ throw new Error(`Required TIP-403 member is unauthorized: ${unauthorized.member}`);
221
+ }
222
+ /**
223
+ * Deposits assets into a vault and mints vault shares to `recipient`. The
224
+ * transaction includes the required asset approval.
225
+ *
226
+ * @example
227
+ * ```ts
228
+ * import { createClient, http } from 'viem'
229
+ * import { privateKeyToAccount } from 'viem/accounts'
230
+ * import { tempoModerato } from 'viem/chains'
231
+ * import { Actions } from 'viem/tempo'
232
+ *
233
+ * const client = createClient({
234
+ * account: privateKeyToAccount('0x...'),
235
+ * chain: tempoModerato,
236
+ * transport: http(),
237
+ * })
238
+ *
239
+ * const hash = await Actions.earn.deposit(client, {
240
+ * assetAmount: 100_000_000n,
241
+ * shareAmount: 99_900_000n,
242
+ * slippageBps: 50,
243
+ * vault: '0x...',
244
+ * })
245
+ * ```
246
+ *
247
+ * @param client - Client.
248
+ * @param parameters - Parameters.
249
+ * @returns The transaction hash.
250
+ */
251
+ export async function deposit(client, parameters) {
252
+ return deposit.inner(sendTransaction, client, parameters);
253
+ }
254
+ (function (deposit) {
255
+ /** @internal Shared dispatch; reads the asset for the approval. */
256
+ async function inner(action, client, parameters) {
257
+ const [args, assetToken] = await Promise.all([
258
+ toDepositArgs(client, parameters),
259
+ readContract(client, {
260
+ abi: Abis.vaultAdapter,
261
+ address: parameters.vault,
262
+ functionName: 'asset',
263
+ }),
264
+ ]);
265
+ return (await action(client, {
266
+ ...parameters,
267
+ calls: deposit.calls({ ...args, assetToken }),
268
+ }));
269
+ }
270
+ deposit.inner = inner;
271
+ /**
272
+ * Defines a deposit call without an approval. Provide token decimals for
273
+ * formatted inputs and an explicit output bound because this builder performs no reads.
274
+ *
275
+ * @param parameters - Client (optional), followed by the call arguments.
276
+ * @returns The call.
277
+ */
278
+ function call(...parameters) {
279
+ const [, args] = resolveCallParameters(parameters);
280
+ const { recipient, vault } = args;
281
+ const shareAmountMin = (() => {
282
+ if (args.shareAmountMin !== undefined)
283
+ return args.shareAmountMin;
284
+ return EarnShares.minimumOutput(args.shareAmount, args.slippageBps);
285
+ })();
286
+ return defineCall({
287
+ address: vault,
288
+ abi: Abis.vaultAdapter,
289
+ functionName: 'deposit',
290
+ args: [
291
+ internal_Token.toBaseUnits(args.assetAmount, undefined),
292
+ recipient,
293
+ shareAmountMin,
294
+ ],
295
+ });
296
+ }
297
+ deposit.call = call;
298
+ /**
299
+ * Defines the asset approval and deposit calls for atomic execution. Pass
300
+ * `assetToken` and token decimals explicitly because this builder performs no reads.
301
+ *
302
+ * @param args - Arguments.
303
+ * @returns The calls.
304
+ */
305
+ function calls(args) {
306
+ const { assetToken, vault } = args;
307
+ const assetAmount = internal_Token.toBaseUnits(args.assetAmount, undefined);
308
+ return [
309
+ defineCall({
310
+ address: TokenId.toAddress(assetToken),
311
+ abi: Abis.tip20,
312
+ functionName: 'approve',
313
+ args: [vault, assetAmount],
314
+ }),
315
+ deposit.call({ ...args, assetAmount }),
316
+ ];
317
+ }
318
+ deposit.calls = calls;
319
+ /**
320
+ * Extracts a `Deposited` event from the vault's logs.
321
+ *
322
+ * @param logs - Logs.
323
+ * @param parameters - Parameters.
324
+ * @returns The `Deposited` event.
325
+ */
326
+ function extractEvent(logs, parameters) {
327
+ const { vault } = parameters;
328
+ // Earn contracts are user-deployed: several adapters can emit the same
329
+ // signature in one receipt, so filter by emitting address before decode.
330
+ const [log] = parseEventLogs({
331
+ abi: Abis.vaultAdapter,
332
+ eventName: 'Deposited',
333
+ logs: logs.filter((log) => isAddressEqual(log.address, vault)),
334
+ });
335
+ if (!log)
336
+ throw new Error('`Deposited` event not found.');
337
+ return log;
338
+ }
339
+ deposit.extractEvent = extractEvent;
340
+ /**
341
+ * Estimates gas for a deposit, assuming the vault has enough asset allowance.
342
+ *
343
+ * @param client - Client.
344
+ * @param parameters - Parameters.
345
+ * @returns The gas estimate.
346
+ */
347
+ async function estimateGas(client, parameters) {
348
+ return estimateContractGas(client, {
349
+ ...pickWriteParameters(parameters),
350
+ ...deposit.call(await toDepositArgs(client, parameters)),
351
+ });
352
+ }
353
+ deposit.estimateGas = estimateGas;
354
+ /**
355
+ * Simulates a deposit, assuming the vault has enough asset allowance.
356
+ *
357
+ * @param client - Client.
358
+ * @param parameters - Parameters.
359
+ * @returns The simulation result and write request.
360
+ */
361
+ async function simulate(client, parameters) {
362
+ return simulateContract(client, {
363
+ ...pickWriteParameters(parameters),
364
+ ...deposit.call(await toDepositArgs(client, parameters)),
365
+ });
366
+ }
367
+ deposit.simulate = simulate;
368
+ })(deposit || (deposit = {}));
369
+ /**
370
+ * Deposits assets and returns the confirmed receipt and event data.
371
+ *
372
+ * @example
373
+ * ```ts
374
+ * import { createClient, http } from 'viem'
375
+ * import { privateKeyToAccount } from 'viem/accounts'
376
+ * import { tempoModerato } from 'viem/chains'
377
+ * import { Actions, EarnShares } from 'viem/tempo'
378
+ *
379
+ * const client = createClient({
380
+ * account: privateKeyToAccount('0x...'),
381
+ * chain: tempoModerato,
382
+ * transport: http(),
383
+ * })
384
+ *
385
+ * const { shareAmount } = await Actions.earn.depositSync(client, {
386
+ * assetAmount: 100_000_000n,
387
+ * shareAmountMin: EarnShares.minimumOutput(99_900_000n, 50),
388
+ * vault: '0x...',
389
+ * })
390
+ * ```
391
+ *
392
+ * @param client - Client.
393
+ * @param parameters - Parameters.
394
+ * @returns The transaction receipt and event data.
395
+ */
396
+ export async function depositSync(client, parameters) {
397
+ const { throwOnReceiptRevert = true, vault } = parameters;
398
+ const receipt = await deposit.inner(sendTransactionSync, client, {
399
+ ...parameters,
400
+ throwOnReceiptRevert,
401
+ });
402
+ const { args } = deposit.extractEvent(receipt.logs, { vault });
403
+ return {
404
+ assetAmount: args.assets,
405
+ caller: args.caller,
406
+ receipt,
407
+ recipient: args.receiver,
408
+ shareAmount: args.shares,
409
+ };
410
+ }
411
+ /**
412
+ * Deposits venue shares into a vault and mints vault shares to `recipient`.
413
+ * The transaction includes the required venue share approval.
414
+ *
415
+ * @example
416
+ * ```ts
417
+ * import { createClient, http } from 'viem'
418
+ * import { privateKeyToAccount } from 'viem/accounts'
419
+ * import { tempoModerato } from 'viem/chains'
420
+ * import { Actions } from 'viem/tempo'
421
+ *
422
+ * const client = createClient({
423
+ * account: privateKeyToAccount('0x...'),
424
+ * chain: tempoModerato,
425
+ * transport: http(),
426
+ * })
427
+ *
428
+ * const hash = await Actions.earn.depositShares(client, {
429
+ * earnShareAmount: 499_000_000n,
430
+ * slippageBps: 30,
431
+ * vault: '0x...',
432
+ * venueShareAmount: 500_000_000n,
433
+ * venueShareToken: '0x...',
434
+ * })
435
+ * ```
436
+ *
437
+ * @param client - Client.
438
+ * @param parameters - Parameters.
439
+ * @returns The transaction hash.
440
+ */
441
+ export async function depositShares(client, parameters) {
442
+ return depositShares.inner(sendTransaction, client, parameters);
443
+ }
444
+ (function (depositShares) {
445
+ /** @internal Shared dispatch; reads the engine for the approval. */
446
+ async function inner(action, client, parameters) {
447
+ const engine = await readContract(client, {
448
+ abi: Abis.vaultAdapter,
449
+ address: parameters.vault,
450
+ functionName: 'engine',
451
+ });
452
+ return (await action(client, {
453
+ ...parameters,
454
+ calls: depositShares.calls({
455
+ ...toDepositSharesArgs(client, parameters),
456
+ engine,
457
+ venueShareToken: parameters.venueShareToken,
458
+ }),
459
+ }));
460
+ }
461
+ depositShares.inner = inner;
462
+ /**
463
+ * Defines a venue share deposit call without an approval. Provide an
464
+ * explicit output bound because this builder performs no reads.
465
+ *
466
+ * @param parameters - Client (optional), followed by the call arguments.
467
+ * @returns The call.
468
+ */
469
+ function call(...parameters) {
470
+ const [, args] = resolveCallParameters(parameters);
471
+ const { recipient, vault, venueShareAmount } = args;
472
+ const earnShareAmountMin = (() => {
473
+ if (args.earnShareAmountMin !== undefined)
474
+ return args.earnShareAmountMin;
475
+ return EarnShares.minimumOutput(args.earnShareAmount, args.slippageBps);
476
+ })();
477
+ return defineCall({
478
+ address: vault,
479
+ abi: Abis.vaultAdapter,
480
+ functionName: 'depositShares',
481
+ args: [venueShareAmount, recipient, earnShareAmountMin],
482
+ });
483
+ }
484
+ depositShares.call = call;
485
+ /**
486
+ * Defines the venue share approval and deposit calls for atomic execution.
487
+ * Pass the vault's current `engine` and `venueShareToken` explicitly.
488
+ *
489
+ * @param args - Arguments.
490
+ * @returns The calls.
491
+ */
492
+ function calls(args) {
493
+ const { engine, venueShareAmount, venueShareToken } = args;
494
+ return [
495
+ defineCall({
496
+ address: venueShareToken,
497
+ abi: Abis.tip20,
498
+ functionName: 'approve',
499
+ args: [engine, venueShareAmount],
500
+ }),
501
+ depositShares.call(args),
502
+ ];
503
+ }
504
+ depositShares.calls = calls;
505
+ /**
506
+ * Extracts a `VenueSharesDeposited` event from the vault's logs.
507
+ *
508
+ * @param logs - Logs.
509
+ * @param parameters - Parameters.
510
+ * @returns The `VenueSharesDeposited` event.
511
+ */
512
+ function extractEvent(logs, parameters) {
513
+ const { vault } = parameters;
514
+ // Earn contracts are user-deployed: several adapters can emit the same
515
+ // signature in one receipt, so filter by emitting address before decode.
516
+ const [log] = parseEventLogs({
517
+ abi: Abis.vaultAdapter,
518
+ eventName: 'VenueSharesDeposited',
519
+ logs: logs.filter((log) => isAddressEqual(log.address, vault)),
520
+ });
521
+ if (!log)
522
+ throw new Error('`VenueSharesDeposited` event not found.');
523
+ return log;
524
+ }
525
+ depositShares.extractEvent = extractEvent;
526
+ /**
527
+ * Estimates gas for a venue share deposit, assuming enough allowance.
528
+ *
529
+ * @param client - Client.
530
+ * @param parameters - Parameters.
531
+ * @returns The gas estimate.
532
+ */
533
+ async function estimateGas(client, parameters) {
534
+ return estimateContractGas(client, {
535
+ ...pickWriteParameters(parameters),
536
+ ...depositShares.call(toDepositSharesArgs(client, parameters)),
537
+ });
538
+ }
539
+ depositShares.estimateGas = estimateGas;
540
+ /**
541
+ * Simulates a venue share deposit, assuming enough allowance.
542
+ *
543
+ * @param client - Client.
544
+ * @param parameters - Parameters.
545
+ * @returns The simulation result and write request.
546
+ */
547
+ async function simulate(client, parameters) {
548
+ return simulateContract(client, {
549
+ ...pickWriteParameters(parameters),
550
+ ...depositShares.call(toDepositSharesArgs(client, parameters)),
551
+ });
552
+ }
553
+ depositShares.simulate = simulate;
554
+ })(depositShares || (depositShares = {}));
555
+ /**
556
+ * Deposits venue shares and returns the confirmed receipt and event data.
557
+ *
558
+ * @example
559
+ * ```ts
560
+ * import { createClient, http } from 'viem'
561
+ * import { privateKeyToAccount } from 'viem/accounts'
562
+ * import { tempoModerato } from 'viem/chains'
563
+ * import { Actions, EarnShares } from 'viem/tempo'
564
+ *
565
+ * const client = createClient({
566
+ * account: privateKeyToAccount('0x...'),
567
+ * chain: tempoModerato,
568
+ * transport: http(),
569
+ * })
570
+ *
571
+ * const { earnShareAmount } = await Actions.earn.depositSharesSync(client, {
572
+ * earnShareAmount: 499_000_000n,
573
+ * slippageBps: 30,
574
+ * vault: '0x...',
575
+ * venueShareAmount: 500_000_000n,
576
+ * venueShareToken: '0x...',
577
+ * })
578
+ * ```
579
+ *
580
+ * @param client - Client.
581
+ * @param parameters - Parameters.
582
+ * @returns The transaction receipt and event data.
583
+ */
584
+ export async function depositSharesSync(client, parameters) {
585
+ const { throwOnReceiptRevert = true, vault } = parameters;
586
+ const receipt = await depositShares.inner(sendTransactionSync, client, {
587
+ ...parameters,
588
+ throwOnReceiptRevert,
589
+ });
590
+ const { args } = depositShares.extractEvent(receipt.logs, { vault });
591
+ return {
592
+ caller: args.caller,
593
+ earnShareAmount: args.earnShares,
594
+ receipt,
595
+ receivedVenueShareAmount: args.receivedVenueShares,
596
+ recipient: args.receiver,
597
+ venueShareAmount: args.requestedVenueShares,
598
+ };
599
+ }
600
+ /**
601
+ * Withdraws assets from a Zone and deposits them into a vault on the parent
602
+ * chain. Use {@link privateDeposit.prepare} to build the encrypted callback.
603
+ *
604
+ * @example
605
+ * ```ts
606
+ * const prepared = await Actions.earn.privateDeposit.prepare(parentClient, {
607
+ * assetAmount: 100_000_000n,
608
+ * assetToken: '0x...',
609
+ * gateway: '0x...',
610
+ * recipient: '0x...',
611
+ * recoveryRecipient: '0x...',
612
+ * shareAmountMin: 99_500_000n,
613
+ * vaultAssetAmountMin: 99_000_000n,
614
+ * })
615
+ * const hash = await Actions.earn.privateDeposit(zoneClient, prepared)
616
+ * ```
617
+ *
618
+ * @param client - Zone client.
619
+ * @param parameters - Prepared deposit and transaction parameters.
620
+ * @returns The transaction hash.
621
+ */
622
+ export async function privateDeposit(client, parameters) {
623
+ await assertPreparedZoneRequestChain(client, parameters);
624
+ return zoneActions.requestWithdrawal(client, parameters);
625
+ }
626
+ (function (privateDeposit) {
627
+ /**
628
+ * Builds an encrypted Zone withdrawal that deposits into the gateway's
629
+ * vault and returns the resulting shares to the Zone.
630
+ *
631
+ * @param client - Parent-chain client.
632
+ * @param parameters - Deposit intent and recovery parameters.
633
+ * @returns The prepared withdrawal and correlation data.
634
+ */
635
+ async function prepare(client, parameters) {
636
+ const chainId = client.chain?.id;
637
+ if (!chainId)
638
+ throw new Error('`chain` is required.');
639
+ const { actionId = Hex.random(32), assetAmount, callbackGas = zoneGatewayCallbackGas, fallbackRecipient = parameters.recoveryRecipient, gateway, recipient, recoveryRecipient, returnMemo, withdrawalMemo, } = parameters;
640
+ const readParameters = pickReadParameters(parameters);
641
+ const [fromBlock, config] = await Promise.all([
642
+ getBlockNumber(client, { cacheTime: 0 }),
643
+ getZoneGatewayConfig(client, { ...readParameters, gateway }),
644
+ ]);
645
+ const assetToken = parameters.assetToken ?? config.vaultAsset;
646
+ const { encrypted, keyIndex } = await zoneActions.encryptedDeposit.prepareRecipient(client, {
647
+ ...readParameters,
648
+ memo: returnMemo,
649
+ portalAddress: config.zonePortal,
650
+ recipient,
651
+ zoneId: config.zoneId,
652
+ });
653
+ const shareAmountMin = resolveMinimumShareAmount(parameters);
654
+ const direct = isAddressEqual(assetToken, config.vaultAsset);
655
+ const data = encodeAbiParameters(Abis.zoneGatewayCallbackData, [
656
+ {
657
+ flow: 0,
658
+ outputToken: config.shareToken,
659
+ keyIndex,
660
+ encrypted,
661
+ minVaultAssets: direct
662
+ ? assetAmount
663
+ : (parameters.vaultAssetAmountMin ?? 0n),
664
+ minVaultShares: shareAmountMin,
665
+ minOutputAmount: 0n,
666
+ actionId,
667
+ refundRecipient: recoveryRecipient,
668
+ },
669
+ ]);
670
+ return {
671
+ actionId,
672
+ amount: assetAmount,
673
+ callbackGas,
674
+ chainId,
675
+ data,
676
+ fallbackRecipient,
677
+ fromBlock,
678
+ memo: withdrawalMemo,
679
+ to: gateway,
680
+ token: assetToken,
681
+ zoneId: config.zoneId,
682
+ };
683
+ }
684
+ privateDeposit.prepare = prepare;
685
+ /**
686
+ * Defines the approval and Zone withdrawal calls for a prepared deposit.
687
+ *
688
+ * @param args - Prepared deposit arguments.
689
+ * @returns The Zone withdrawal calls.
690
+ */
691
+ function calls(args) {
692
+ return zoneActions.requestWithdrawal.calls(args);
693
+ }
694
+ privateDeposit.calls = calls;
695
+ })(privateDeposit || (privateDeposit = {}));
696
+ /**
697
+ * Requests a private Zone deposit and waits for the Zone transaction receipt.
698
+ * The receipt confirms withdrawal acceptance, not the parent-chain deposit.
699
+ *
700
+ * @param client - Zone client.
701
+ * @param parameters - Prepared deposit and transaction parameters.
702
+ * @returns The Zone transaction receipt and parent-chain withdrawal sender tag.
703
+ */
704
+ export async function privateDepositSync(client, parameters) {
705
+ await assertPreparedZoneRequestChain(client, parameters);
706
+ return zoneActions.requestWithdrawalSync(client, parameters);
707
+ }
708
+ /**
709
+ * Waits for a Zone gateway deposit to complete on the parent chain.
710
+ *
711
+ * @example
712
+ * ```ts
713
+ * const result = await Actions.earn.waitForPrivateDeposit(parentClient, {
714
+ * actionId: prepared.actionId,
715
+ * fromBlock: prepared.fromBlock,
716
+ * gateway: '0x...',
717
+ * })
718
+ * ```
719
+ *
720
+ * @param client - Parent-chain client.
721
+ * @param parameters - Prepared action correlation and polling parameters.
722
+ * @returns The completed gateway deposit.
723
+ */
724
+ export async function waitForPrivateDeposit(client, parameters) {
725
+ const { actionId, fromBlock, gateway, pollingInterval = client.pollingInterval, timeout = 60_000, } = parameters;
726
+ const event = getAbiItem({
727
+ abi: Abis.zoneGateway,
728
+ name: 'EarnDeposit',
729
+ });
730
+ const observerId = stringify([
731
+ 'waitForPrivateDeposit',
732
+ client.uid,
733
+ gateway,
734
+ actionId,
735
+ fromBlock,
736
+ ]);
737
+ const { promise, reject, resolve } = withResolvers();
738
+ let timer;
739
+ let unobserve;
740
+ const cleanup = () => {
741
+ clearTimeout(timer);
742
+ unobserve();
743
+ };
744
+ const resolve_ = (result) => {
745
+ cleanup();
746
+ resolve(result);
747
+ };
748
+ const reject_ = (error) => {
749
+ cleanup();
750
+ reject(error);
751
+ };
752
+ unobserve = observe(observerId, { reject: reject_, resolve: resolve_ }, (emit) => {
753
+ const unpoll = poll(async () => {
754
+ try {
755
+ const [log] = await getLogs(client, {
756
+ address: gateway,
757
+ args: { actionId },
758
+ event,
759
+ fromBlock,
760
+ strict: true,
761
+ toBlock: 'latest',
762
+ });
763
+ if (!log)
764
+ return;
765
+ unpoll();
766
+ emit.resolve({
767
+ actionId: log.args.actionId,
768
+ inputAmount: log.args.inputAmount,
769
+ inputToken: log.args.inputToken,
770
+ shares: log.args.shares,
771
+ tempoBlockNumber: log.blockNumber,
772
+ vaultAssets: log.args.vaultAssets,
773
+ zoneDepositHash: log.args.zoneDepositHash,
774
+ });
775
+ }
776
+ catch (error) {
777
+ unpoll();
778
+ emit.reject(error);
779
+ }
780
+ }, { emitOnBegin: true, interval: pollingInterval });
781
+ return unpoll;
782
+ });
783
+ timer = timeout
784
+ ? setTimeout(() => {
785
+ reject_(new WaitForPrivateDepositTimeoutError({ actionId, gateway }));
786
+ }, timeout)
787
+ : undefined;
788
+ return await promise;
789
+ }
790
+ /**
791
+ * Gets the vault's active fee configuration, pending fees, and fee baselines.
792
+ *
793
+ * @example
794
+ * ```ts
795
+ * import { createClient, http } from 'viem'
796
+ * import { tempoModerato } from 'viem/chains'
797
+ * import { Actions } from 'viem/tempo'
798
+ *
799
+ * const client = createClient({
800
+ * chain: tempoModerato,
801
+ * transport: http(),
802
+ * })
803
+ *
804
+ * const feeState = await Actions.earn.getFeeState(client, {
805
+ * vault: '0x...',
806
+ * })
807
+ * ```
808
+ *
809
+ * @param client - Client.
810
+ * @param parameters - Parameters.
811
+ * @returns The active fee configuration, pending fees, and baselines.
812
+ */
813
+ export async function getFeeState(client, parameters) {
814
+ const { recipient, vault, ...rest } = parameters;
815
+ const contracts = [
816
+ defineCall({
817
+ address: vault,
818
+ abi: Abis.vaultAdapter,
819
+ functionName: 'currentFeeConfigId',
820
+ }),
821
+ defineCall({
822
+ address: vault,
823
+ abi: Abis.vaultAdapter,
824
+ functionName: 'feesActive',
825
+ }),
826
+ defineCall({
827
+ address: vault,
828
+ abi: Abis.vaultAdapter,
829
+ functionName: 'highWaterMark',
830
+ }),
831
+ defineCall({
832
+ address: vault,
833
+ abi: Abis.vaultAdapter,
834
+ functionName: 'previewAccruedFees',
835
+ }),
836
+ defineCall({
837
+ address: vault,
838
+ abi: Abis.vaultAdapter,
839
+ functionName: 'targetBase',
840
+ }),
841
+ ];
842
+ // Stored configs are immutable per id, so a follow-up `feeConfig` read stays
843
+ // consistent with the batched id.
844
+ const feeConfig = async (configId) => toFeeConfig(await readContract(client, {
845
+ ...rest,
846
+ abi: Abis.vaultAdapter,
847
+ address: vault,
848
+ functionName: 'feeConfig',
849
+ args: [configId],
850
+ }));
851
+ if (recipient !== undefined) {
852
+ const [configId, feesActive, highWaterMark, preview, targetBase, shares] = await multicall(client, {
853
+ ...rest,
854
+ allowFailure: false,
855
+ contracts: [
856
+ ...contracts,
857
+ defineCall({
858
+ address: vault,
859
+ abi: Abis.vaultAdapter,
860
+ functionName: 'claimableFeeShares',
861
+ args: [recipient],
862
+ }),
863
+ ],
864
+ deployless: true,
865
+ });
866
+ return {
867
+ claimableShares: shares,
868
+ config: await feeConfig(configId),
869
+ configId,
870
+ feesActive,
871
+ highWaterMark,
872
+ preview: toFeePreview(preview),
873
+ targetBase,
874
+ };
875
+ }
876
+ const [configId, feesActive, highWaterMark, preview, targetBase] = await multicall(client, {
877
+ ...rest,
878
+ allowFailure: false,
879
+ contracts,
880
+ deployless: true,
881
+ });
882
+ return {
883
+ config: await feeConfig(configId),
884
+ configId,
885
+ feesActive,
886
+ highWaterMark,
887
+ preview: toFeePreview(preview),
888
+ targetBase,
889
+ };
890
+ }
891
+ /**
892
+ * Gets an account's asset and vault share balances, allowances, and current
893
+ * share value. The value includes fees.
894
+ *
895
+ * @example
896
+ * ```ts
897
+ * import { createClient, http } from 'viem'
898
+ * import { tempoModerato } from 'viem/chains'
899
+ * import { Actions } from 'viem/tempo'
900
+ *
901
+ * const client = createClient({
902
+ * chain: tempoModerato,
903
+ * transport: http(),
904
+ * })
905
+ *
906
+ * const position = await Actions.earn.getPosition(client, {
907
+ * account: '0x...',
908
+ * vault: '0x...',
909
+ * })
910
+ * ```
911
+ *
912
+ * @param client - Client.
913
+ * @param parameters - Parameters.
914
+ * @returns The asset and vault share balances, allowances, and value.
915
+ */
916
+ export async function getPosition(client, parameters) {
917
+ const { account: account_ = client.account, vault, ...rest } = parameters;
918
+ if (!account_)
919
+ throw new AccountNotFoundError();
920
+ const account = parseAccount(account_).address;
921
+ const [assetToken, shareToken] = await multicall(client, {
922
+ ...rest,
923
+ allowFailure: false,
924
+ contracts: [
925
+ defineCall({
926
+ address: vault,
927
+ abi: Abis.vaultAdapter,
928
+ functionName: 'asset',
929
+ }),
930
+ defineCall({
931
+ address: vault,
932
+ abi: Abis.vaultAdapter,
933
+ functionName: 'shareToken',
934
+ }),
935
+ ],
936
+ deployless: true,
937
+ });
938
+ const [assetAllowance, assetBalance, shareAllowance, shareBalance] = await multicall(client, {
939
+ ...rest,
940
+ allowFailure: false,
941
+ contracts: [
942
+ defineCall({
943
+ address: assetToken,
944
+ abi: Abis.tip20,
945
+ functionName: 'allowance',
946
+ args: [account, vault],
947
+ }),
948
+ defineCall({
949
+ address: assetToken,
950
+ abi: Abis.tip20,
951
+ functionName: 'balanceOf',
952
+ args: [account],
953
+ }),
954
+ defineCall({
955
+ address: shareToken,
956
+ abi: Abis.tip20,
957
+ functionName: 'allowance',
958
+ args: [account, vault],
959
+ }),
960
+ defineCall({
961
+ address: shareToken,
962
+ abi: Abis.tip20,
963
+ functionName: 'balanceOf',
964
+ args: [account],
965
+ }),
966
+ ],
967
+ deployless: true,
968
+ });
969
+ const value = await readContract(client, {
970
+ ...rest,
971
+ abi: Abis.vaultAdapter,
972
+ address: vault,
973
+ args: [shareBalance],
974
+ functionName: 'previewRedeem',
975
+ });
976
+ return {
977
+ assetAllowance,
978
+ assetBalance,
979
+ assetToken,
980
+ shareAllowance,
981
+ shareBalance,
982
+ shareToken,
983
+ value,
984
+ };
985
+ }
986
+ /**
987
+ * Gets the asset output for an exact vault share input, including fees.
988
+ *
989
+ * @example
990
+ * ```ts
991
+ * import { createClient, http } from 'viem'
992
+ * import { tempoModerato } from 'viem/chains'
993
+ * import { Actions } from 'viem/tempo'
994
+ *
995
+ * const client = createClient({
996
+ * chain: tempoModerato,
997
+ * transport: http(),
998
+ * })
999
+ *
1000
+ * const assetAmount = await Actions.earn.getRedeemQuote(client, {
1001
+ * shareAmount: 100_000_000n,
1002
+ * vault: '0x...',
1003
+ * })
1004
+ * ```
1005
+ *
1006
+ * @param client - Client.
1007
+ * @param parameters - Parameters.
1008
+ * @returns The asset output, including fees.
1009
+ */
1010
+ export async function getRedeemQuote(client, parameters) {
1011
+ const { shareAmount, vault, ...rest } = parameters;
1012
+ return readContract(client, {
1013
+ ...rest,
1014
+ ...getRedeemQuote.call({ shareAmount, vault }),
1015
+ });
1016
+ }
1017
+ (function (getRedeemQuote) {
1018
+ /**
1019
+ * Defines a call to the vault's `previewRedeem` function.
1020
+ *
1021
+ * Can be passed as a parameter to:
1022
+ * - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate the gas cost of the call
1023
+ * - [`multicall`](https://viem.sh/docs/contract/multicall): batch the call with other contract reads
1024
+ * - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the call
1025
+ *
1026
+ * @example
1027
+ * ```ts
1028
+ * import { Actions } from 'viem/tempo'
1029
+ *
1030
+ * const call = Actions.earn.getRedeemQuote.call({
1031
+ * shareAmount: 100_000_000n,
1032
+ * vault: '0x...',
1033
+ * })
1034
+ * ```
1035
+ *
1036
+ * @param args - Arguments.
1037
+ * @returns The call.
1038
+ */
1039
+ function call(args) {
1040
+ const { shareAmount, vault } = args;
1041
+ return defineCall({
1042
+ address: vault,
1043
+ abi: Abis.vaultAdapter,
1044
+ args: [shareAmount],
1045
+ functionName: 'previewRedeem',
1046
+ });
1047
+ }
1048
+ getRedeemQuote.call = call;
1049
+ })(getRedeemQuote || (getRedeemQuote = {}));
1050
+ /**
1051
+ * Gets the vault's addresses, configuration, accounting state, and supported
1052
+ * actions. Throws {@link GetVaultEngineChangedError} if its engine changes mid-read.
1053
+ *
1054
+ * @example
1055
+ * ```ts
1056
+ * import { createClient, http } from 'viem'
1057
+ * import { tempoModerato } from 'viem/chains'
1058
+ * import { Actions } from 'viem/tempo'
1059
+ *
1060
+ * const client = createClient({
1061
+ * chain: tempoModerato,
1062
+ * transport: http(),
1063
+ * })
1064
+ *
1065
+ * const vault = await Actions.earn.getVault(client, {
1066
+ * vault: '0x...',
1067
+ * })
1068
+ * ```
1069
+ *
1070
+ * @param client - Client.
1071
+ * @param parameters - Parameters.
1072
+ * @returns The vault state and metadata.
1073
+ */
1074
+ export async function getVault(client, parameters) {
1075
+ const { vault, ...rest } = parameters;
1076
+ const engine = await readContract(client, {
1077
+ ...rest,
1078
+ abi: Abis.vaultAdapter,
1079
+ address: vault,
1080
+ functionName: 'engine',
1081
+ });
1082
+ const [assetToken, engine_, shareToken, operator, emergencyGuardian, asyncJanitor, engineMigrationMode, depositsPaused, engineShares, shareSupply, isSynced, pendingRedeemCount, feesActive, totalAssets, name, symbol, asyncRedeem, exactWithdraw, inKindDeposit, syncRedeem,] = await multicall(client, {
1083
+ ...rest,
1084
+ allowFailure: false,
1085
+ contracts: getVault.calls({ engine, vault }),
1086
+ deployless: true,
1087
+ });
1088
+ if (!isAddressEqual(engine, engine_))
1089
+ throw new GetVaultEngineChangedError({ vault });
1090
+ return {
1091
+ assetToken,
1092
+ asyncJanitor,
1093
+ capabilities: { asyncRedeem, exactWithdraw, inKindDeposit, syncRedeem },
1094
+ depositsPaused,
1095
+ emergencyGuardian,
1096
+ engine: { address: engine_, name, symbol, totalAssets },
1097
+ // `EngineMigrationMode`: 0 = UserOnly, 1 = OperatorEnabled.
1098
+ engineMigrationMode: engineMigrationMode === 0 ? 'userOnly' : 'operatorEnabled',
1099
+ engineShares,
1100
+ feesActive,
1101
+ isSynced,
1102
+ operator,
1103
+ pendingRedeemCount,
1104
+ shareSupply,
1105
+ shareToken,
1106
+ };
1107
+ }
1108
+ (function (getVault) {
1109
+ /**
1110
+ * Defines the reads used by {@link getVault}. Pass the vault's current
1111
+ * engine address.
1112
+ *
1113
+ * @param args - Arguments.
1114
+ * @returns The calls.
1115
+ */
1116
+ function calls(args) {
1117
+ const { engine, vault } = args;
1118
+ return [
1119
+ defineCall({
1120
+ address: vault,
1121
+ abi: Abis.vaultAdapter,
1122
+ functionName: 'asset',
1123
+ }),
1124
+ defineCall({
1125
+ address: vault,
1126
+ abi: Abis.vaultAdapter,
1127
+ functionName: 'engine',
1128
+ }),
1129
+ defineCall({
1130
+ address: vault,
1131
+ abi: Abis.vaultAdapter,
1132
+ functionName: 'shareToken',
1133
+ }),
1134
+ defineCall({
1135
+ address: vault,
1136
+ abi: Abis.vaultAdapter,
1137
+ functionName: 'operator',
1138
+ }),
1139
+ defineCall({
1140
+ address: vault,
1141
+ abi: Abis.vaultAdapter,
1142
+ functionName: 'emergencyGuardian',
1143
+ }),
1144
+ defineCall({
1145
+ address: vault,
1146
+ abi: Abis.vaultAdapter,
1147
+ functionName: 'asyncJanitor',
1148
+ }),
1149
+ defineCall({
1150
+ address: vault,
1151
+ abi: Abis.vaultAdapter,
1152
+ functionName: 'engineMigrationMode',
1153
+ }),
1154
+ defineCall({
1155
+ address: vault,
1156
+ abi: Abis.vaultAdapter,
1157
+ functionName: 'depositsPaused',
1158
+ }),
1159
+ defineCall({
1160
+ address: vault,
1161
+ abi: Abis.vaultAdapter,
1162
+ functionName: 'engineShares',
1163
+ }),
1164
+ defineCall({
1165
+ address: vault,
1166
+ abi: Abis.vaultAdapter,
1167
+ functionName: 'shareSupply',
1168
+ }),
1169
+ defineCall({
1170
+ address: vault,
1171
+ abi: Abis.vaultAdapter,
1172
+ functionName: 'isSynced',
1173
+ }),
1174
+ defineCall({
1175
+ address: vault,
1176
+ abi: Abis.vaultAdapter,
1177
+ functionName: 'pendingRedeemCount',
1178
+ }),
1179
+ defineCall({
1180
+ address: vault,
1181
+ abi: Abis.vaultAdapter,
1182
+ functionName: 'feesActive',
1183
+ }),
1184
+ defineCall({
1185
+ address: engine,
1186
+ abi: Abis.vaultEngine,
1187
+ functionName: 'totalAssets',
1188
+ }),
1189
+ defineCall({
1190
+ address: engine,
1191
+ abi: Abis.vaultEngine,
1192
+ functionName: 'name',
1193
+ }),
1194
+ defineCall({
1195
+ address: engine,
1196
+ abi: Abis.vaultEngine,
1197
+ functionName: 'symbol',
1198
+ }),
1199
+ defineCall({
1200
+ address: engine,
1201
+ abi: Abis.vaultEngine,
1202
+ functionName: 'supportsInterface',
1203
+ args: [interfaceIds.asyncRedeem],
1204
+ }),
1205
+ defineCall({
1206
+ address: engine,
1207
+ abi: Abis.vaultEngine,
1208
+ functionName: 'supportsInterface',
1209
+ args: [interfaceIds.exactWithdraw],
1210
+ }),
1211
+ defineCall({
1212
+ address: engine,
1213
+ abi: Abis.vaultEngine,
1214
+ functionName: 'supportsInterface',
1215
+ args: [interfaceIds.inKindDeposit],
1216
+ }),
1217
+ defineCall({
1218
+ address: engine,
1219
+ abi: Abis.vaultEngine,
1220
+ functionName: 'supportsInterface',
1221
+ args: [interfaceIds.syncRedeem],
1222
+ }),
1223
+ ];
1224
+ }
1225
+ getVault.calls = calls;
1226
+ })(getVault || (getVault = {}));
1227
+ /**
1228
+ * Gets the vault shares required for an exact asset output, including fees
1229
+ * and ceiling rounding.
1230
+ *
1231
+ * @example
1232
+ * ```ts
1233
+ * import { createClient, http } from 'viem'
1234
+ * import { tempoModerato } from 'viem/chains'
1235
+ * import { Actions } from 'viem/tempo'
1236
+ *
1237
+ * const client = createClient({
1238
+ * chain: tempoModerato,
1239
+ * transport: http(),
1240
+ * })
1241
+ *
1242
+ * const shareAmount = await Actions.earn.getWithdrawQuote(client, {
1243
+ * assetAmount: 250_000_000n,
1244
+ * vault: '0x...',
1245
+ * })
1246
+ * ```
1247
+ *
1248
+ * @param client - Client.
1249
+ * @param parameters - Parameters.
1250
+ * @returns The required vault share input, ceiling-rounded.
1251
+ */
1252
+ export async function getWithdrawQuote(client, parameters) {
1253
+ const { assetAmount, vault, ...rest } = parameters;
1254
+ return readContract(client, {
1255
+ ...rest,
1256
+ ...getWithdrawQuote.call({ assetAmount, vault }),
1257
+ });
1258
+ }
1259
+ (function (getWithdrawQuote) {
1260
+ /**
1261
+ * Defines a call to the vault's `previewWithdraw` function.
1262
+ *
1263
+ * Can be passed as a parameter to:
1264
+ * - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate the gas cost of the call
1265
+ * - [`multicall`](https://viem.sh/docs/contract/multicall): batch the call with other contract reads
1266
+ * - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the call
1267
+ *
1268
+ * @example
1269
+ * ```ts
1270
+ * import { Actions } from 'viem/tempo'
1271
+ *
1272
+ * const call = Actions.earn.getWithdrawQuote.call({
1273
+ * assetAmount: 250_000_000n,
1274
+ * vault: '0x...',
1275
+ * })
1276
+ * ```
1277
+ *
1278
+ * @param args - Arguments.
1279
+ * @returns The call.
1280
+ */
1281
+ function call(args) {
1282
+ const { assetAmount, vault } = args;
1283
+ return defineCall({
1284
+ address: vault,
1285
+ abi: Abis.vaultAdapter,
1286
+ args: [assetAmount],
1287
+ functionName: 'previewWithdraw',
1288
+ });
1289
+ }
1290
+ getWithdrawQuote.call = call;
1291
+ })(getWithdrawQuote || (getWithdrawQuote = {}));
1292
+ /**
1293
+ * Redeems vault shares for assets sent to `recipient`. The transaction
1294
+ * includes the required vault share approval.
1295
+ *
1296
+ * @example
1297
+ * ```ts
1298
+ * import { createClient, http } from 'viem'
1299
+ * import { privateKeyToAccount } from 'viem/accounts'
1300
+ * import { tempoModerato } from 'viem/chains'
1301
+ * import { Actions } from 'viem/tempo'
1302
+ *
1303
+ * const client = createClient({
1304
+ * account: privateKeyToAccount('0x...'),
1305
+ * chain: tempoModerato,
1306
+ * transport: http(),
1307
+ * })
1308
+ *
1309
+ * const hash = await Actions.earn.redeem(client, {
1310
+ * shareAmount: 100_000_000n,
1311
+ * slippageBps: 50,
1312
+ * vault: '0x...',
1313
+ * })
1314
+ * ```
1315
+ *
1316
+ * @param client - Client.
1317
+ * @param parameters - Parameters.
1318
+ * @returns The transaction hash.
1319
+ */
1320
+ export async function redeem(client, parameters) {
1321
+ return redeem.inner(sendTransaction, client, parameters);
1322
+ }
1323
+ (function (redeem) {
1324
+ /** @internal Shared dispatch; reads the vault share token for the approval. */
1325
+ async function inner(action, client, parameters) {
1326
+ const [args, shareToken] = await Promise.all([
1327
+ toRedeemArgs(client, parameters),
1328
+ readContract(client, {
1329
+ abi: Abis.vaultAdapter,
1330
+ address: parameters.vault,
1331
+ functionName: 'shareToken',
1332
+ }),
1333
+ ]);
1334
+ return (await action(client, {
1335
+ ...parameters,
1336
+ calls: redeem.calls({ ...args, shareToken }),
1337
+ }));
1338
+ }
1339
+ redeem.inner = inner;
1340
+ /**
1341
+ * Defines a redeem call without an approval. Provide vault share decimals
1342
+ * for formatted inputs and an explicit output bound because this builder performs no reads.
1343
+ *
1344
+ * @param parameters - Client (optional), followed by the call arguments.
1345
+ * @returns The call.
1346
+ */
1347
+ function call(...parameters) {
1348
+ const [, args] = resolveCallParameters(parameters);
1349
+ const { recipient, vault } = args;
1350
+ const assetAmountMin = (() => {
1351
+ if (args.assetAmountMin !== undefined)
1352
+ return args.assetAmountMin;
1353
+ return EarnShares.minimumOutput(args.assetAmount, args.slippageBps);
1354
+ })();
1355
+ return defineCall({
1356
+ address: vault,
1357
+ abi: Abis.vaultAdapter,
1358
+ functionName: 'redeem',
1359
+ args: [
1360
+ internal_Token.toBaseUnits(args.shareAmount, undefined),
1361
+ recipient,
1362
+ assetAmountMin,
1363
+ ],
1364
+ });
1365
+ }
1366
+ redeem.call = call;
1367
+ /**
1368
+ * Defines the vault share approval and redeem calls for atomic execution.
1369
+ * Pass `shareToken` explicitly because this builder performs no reads.
1370
+ *
1371
+ * @param args - Arguments.
1372
+ * @returns The calls.
1373
+ */
1374
+ function calls(args) {
1375
+ const { shareToken, vault } = args;
1376
+ const shareAmount = internal_Token.toBaseUnits(args.shareAmount, undefined);
1377
+ return [
1378
+ defineCall({
1379
+ address: shareToken,
1380
+ abi: Abis.tip20,
1381
+ functionName: 'approve',
1382
+ args: [vault, shareAmount],
1383
+ }),
1384
+ redeem.call({ ...args, shareAmount }),
1385
+ ];
1386
+ }
1387
+ redeem.calls = calls;
1388
+ /**
1389
+ * Extracts a `Redeemed` event from the vault's logs.
1390
+ *
1391
+ * @param logs - Logs.
1392
+ * @param parameters - Parameters.
1393
+ * @returns The `Redeemed` event.
1394
+ */
1395
+ function extractEvent(logs, parameters) {
1396
+ const { vault } = parameters;
1397
+ // Earn contracts are user-deployed: several adapters can emit the same
1398
+ // signature in one receipt, so filter by emitting address before decode.
1399
+ const [log] = parseEventLogs({
1400
+ abi: Abis.vaultAdapter,
1401
+ eventName: 'Redeemed',
1402
+ logs: logs.filter((log) => isAddressEqual(log.address, vault)),
1403
+ });
1404
+ if (!log)
1405
+ throw new Error('`Redeemed` event not found.');
1406
+ return log;
1407
+ }
1408
+ redeem.extractEvent = extractEvent;
1409
+ /**
1410
+ * Estimates gas for a redemption, assuming enough vault share allowance.
1411
+ *
1412
+ * @param client - Client.
1413
+ * @param parameters - Parameters.
1414
+ * @returns The gas estimate.
1415
+ */
1416
+ async function estimateGas(client, parameters) {
1417
+ return estimateContractGas(client, {
1418
+ ...pickWriteParameters(parameters),
1419
+ ...redeem.call(await toRedeemArgs(client, parameters)),
1420
+ });
1421
+ }
1422
+ redeem.estimateGas = estimateGas;
1423
+ /**
1424
+ * Simulates a redemption, assuming enough vault share allowance.
1425
+ *
1426
+ * @param client - Client.
1427
+ * @param parameters - Parameters.
1428
+ * @returns The simulation result and write request.
1429
+ */
1430
+ async function simulate(client, parameters) {
1431
+ return simulateContract(client, {
1432
+ ...pickWriteParameters(parameters),
1433
+ ...redeem.call(await toRedeemArgs(client, parameters)),
1434
+ });
1435
+ }
1436
+ redeem.simulate = simulate;
1437
+ })(redeem || (redeem = {}));
1438
+ /**
1439
+ * Redeems vault shares and returns the confirmed receipt and event data.
1440
+ *
1441
+ * @example
1442
+ * ```ts
1443
+ * import { createClient, http } from 'viem'
1444
+ * import { privateKeyToAccount } from 'viem/accounts'
1445
+ * import { tempoModerato } from 'viem/chains'
1446
+ * import { Actions } from 'viem/tempo'
1447
+ *
1448
+ * const client = createClient({
1449
+ * account: privateKeyToAccount('0x...'),
1450
+ * chain: tempoModerato,
1451
+ * transport: http(),
1452
+ * })
1453
+ *
1454
+ * const { assetAmount } = await Actions.earn.redeemSync(client, {
1455
+ * assetAmountMin: 99_500_000n,
1456
+ * shareAmount: 100_000_000n,
1457
+ * vault: '0x...',
1458
+ * })
1459
+ * ```
1460
+ *
1461
+ * @param client - Client.
1462
+ * @param parameters - Parameters.
1463
+ * @returns The transaction receipt and event data.
1464
+ */
1465
+ export async function redeemSync(client, parameters) {
1466
+ const { throwOnReceiptRevert = true, vault } = parameters;
1467
+ const receipt = await redeem.inner(sendTransactionSync, client, {
1468
+ ...parameters,
1469
+ throwOnReceiptRevert,
1470
+ });
1471
+ const { args } = redeem.extractEvent(receipt.logs, { vault });
1472
+ return {
1473
+ assetAmount: args.assets,
1474
+ caller: args.caller,
1475
+ receipt,
1476
+ recipient: args.receiver,
1477
+ shareAmount: args.shares,
1478
+ };
1479
+ }
1480
+ /**
1481
+ * Withdraws vault shares from a Zone and redeems them on the parent chain. Use
1482
+ * {@link privateRedeem.prepare} to build the encrypted callback.
1483
+ *
1484
+ * @example
1485
+ * ```ts
1486
+ * const prepared = await Actions.earn.privateRedeem.prepare(parentClient, {
1487
+ * gateway: '0x...',
1488
+ * recipient: '0x...',
1489
+ * recoveryRecipient: '0x...',
1490
+ * shareAmount: 100_000_000n,
1491
+ * slippageBps: 50,
1492
+ * })
1493
+ * const hash = await Actions.earn.privateRedeem(zoneClient, prepared)
1494
+ * ```
1495
+ *
1496
+ * @param client - Zone client.
1497
+ * @param parameters - Prepared redemption and transaction parameters.
1498
+ * @returns The transaction hash.
1499
+ */
1500
+ export async function privateRedeem(client, parameters) {
1501
+ await assertPreparedZoneRequestChain(client, parameters);
1502
+ return zoneActions.requestWithdrawal(client, parameters);
1503
+ }
1504
+ (function (privateRedeem) {
1505
+ /**
1506
+ * Builds an encrypted Zone withdrawal that redeems vault shares and returns
1507
+ * the resulting assets to the Zone.
1508
+ *
1509
+ * @param client - Parent-chain client.
1510
+ * @param parameters - Redemption intent and recovery parameters.
1511
+ * @returns The prepared withdrawal and correlation data.
1512
+ */
1513
+ async function prepare(client, parameters) {
1514
+ const chainId = client.chain?.id;
1515
+ if (!chainId)
1516
+ throw new Error('`chain` is required.');
1517
+ const { actionId = Hex.random(32), callbackGas = zoneGatewayCallbackGas, fallbackRecipient = parameters.recoveryRecipient, gateway, recipient, recoveryRecipient, returnMemo, shareAmount, withdrawalMemo, } = parameters;
1518
+ const readParameters = pickReadParameters(parameters);
1519
+ const [fromBlock, config] = await Promise.all([
1520
+ getBlockNumber(client, { cacheTime: 0 }),
1521
+ getZoneGatewayConfig(client, { ...readParameters, gateway }),
1522
+ ]);
1523
+ const assetToken = parameters.assetToken ?? config.vaultAsset;
1524
+ if (isAddressEqual(assetToken, config.shareToken))
1525
+ throw new Error('`assetToken` cannot be the gateway vault share token.');
1526
+ const [{ encrypted, keyIndex }, assetAmountMin] = await Promise.all([
1527
+ zoneActions.encryptedDeposit.prepareRecipient(client, {
1528
+ ...readParameters,
1529
+ memo: returnMemo,
1530
+ portalAddress: config.zonePortal,
1531
+ recipient,
1532
+ zoneId: config.zoneId,
1533
+ }),
1534
+ (async () => {
1535
+ if (parameters.assetAmountMin !== undefined)
1536
+ return EarnShares.minimumOutput(parameters.assetAmountMin, 0);
1537
+ if (parameters.assetAmount !== undefined)
1538
+ return EarnShares.minimumOutput(parameters.assetAmount, parameters.slippageBps);
1539
+ const assetAmount = await getRedeemQuote(client, {
1540
+ ...readParameters,
1541
+ shareAmount,
1542
+ vault: config.vaultAdapter,
1543
+ });
1544
+ return EarnShares.minimumOutput(assetAmount, parameters.slippageBps);
1545
+ })(),
1546
+ ]);
1547
+ const direct = isAddressEqual(assetToken, config.vaultAsset);
1548
+ const data = encodeAbiParameters(Abis.zoneGatewayCallbackData, [
1549
+ {
1550
+ flow: 1,
1551
+ outputToken: assetToken,
1552
+ keyIndex,
1553
+ encrypted,
1554
+ minVaultAssets: direct ? assetAmountMin : 1n,
1555
+ minVaultShares: 0n,
1556
+ minOutputAmount: direct ? 0n : assetAmountMin,
1557
+ actionId,
1558
+ refundRecipient: recoveryRecipient,
1559
+ },
1560
+ ]);
1561
+ return {
1562
+ actionId,
1563
+ amount: shareAmount,
1564
+ callbackGas,
1565
+ chainId,
1566
+ data,
1567
+ fallbackRecipient,
1568
+ fromBlock,
1569
+ memo: withdrawalMemo,
1570
+ to: gateway,
1571
+ token: config.shareToken,
1572
+ zoneId: config.zoneId,
1573
+ };
1574
+ }
1575
+ privateRedeem.prepare = prepare;
1576
+ /**
1577
+ * Defines the approval and Zone withdrawal calls for a prepared redemption.
1578
+ *
1579
+ * @param args - Prepared redemption arguments.
1580
+ * @returns The Zone withdrawal calls.
1581
+ */
1582
+ function calls(args) {
1583
+ return zoneActions.requestWithdrawal.calls(args);
1584
+ }
1585
+ privateRedeem.calls = calls;
1586
+ })(privateRedeem || (privateRedeem = {}));
1587
+ /**
1588
+ * Requests a private Zone redemption and waits for the Zone transaction
1589
+ * receipt. The receipt confirms withdrawal acceptance, not redemption.
1590
+ *
1591
+ * @param client - Zone client.
1592
+ * @param parameters - Prepared redemption and transaction parameters.
1593
+ * @returns The Zone transaction receipt and parent-chain withdrawal sender tag.
1594
+ */
1595
+ export async function privateRedeemSync(client, parameters) {
1596
+ await assertPreparedZoneRequestChain(client, parameters);
1597
+ return zoneActions.requestWithdrawalSync(client, parameters);
1598
+ }
1599
+ /**
1600
+ * Waits for a Zone gateway redemption to complete on the parent chain.
1601
+ *
1602
+ * @example
1603
+ * ```ts
1604
+ * const result = await Actions.earn.waitForPrivateRedeem(parentClient, {
1605
+ * actionId: prepared.actionId,
1606
+ * fromBlock: prepared.fromBlock,
1607
+ * gateway: '0x...',
1608
+ * })
1609
+ * ```
1610
+ *
1611
+ * @param client - Parent-chain client.
1612
+ * @param parameters - Prepared action correlation and polling parameters.
1613
+ * @returns The completed gateway redemption.
1614
+ */
1615
+ export async function waitForPrivateRedeem(client, parameters) {
1616
+ const { actionId, fromBlock, gateway, pollingInterval = client.pollingInterval, timeout = 60_000, } = parameters;
1617
+ const event = getAbiItem({
1618
+ abi: Abis.zoneGateway,
1619
+ name: 'EarnRedeem',
1620
+ });
1621
+ const observerId = stringify([
1622
+ 'waitForPrivateRedeem',
1623
+ client.uid,
1624
+ gateway,
1625
+ actionId,
1626
+ fromBlock,
1627
+ ]);
1628
+ const { promise, reject, resolve } = withResolvers();
1629
+ let timer;
1630
+ let unobserve;
1631
+ const cleanup = () => {
1632
+ clearTimeout(timer);
1633
+ unobserve();
1634
+ };
1635
+ const resolve_ = (result) => {
1636
+ cleanup();
1637
+ resolve(result);
1638
+ };
1639
+ const reject_ = (error) => {
1640
+ cleanup();
1641
+ reject(error);
1642
+ };
1643
+ unobserve = observe(observerId, { reject: reject_, resolve: resolve_ }, (emit) => {
1644
+ const unpoll = poll(async () => {
1645
+ try {
1646
+ const [log] = await getLogs(client, {
1647
+ address: gateway,
1648
+ args: { actionId },
1649
+ event,
1650
+ fromBlock,
1651
+ strict: true,
1652
+ toBlock: 'latest',
1653
+ });
1654
+ if (!log)
1655
+ return;
1656
+ unpoll();
1657
+ emit.resolve({
1658
+ actionId: log.args.actionId,
1659
+ outputAmount: log.args.outputAmount,
1660
+ outputToken: log.args.outputToken,
1661
+ shares: log.args.shares,
1662
+ tempoBlockNumber: log.blockNumber,
1663
+ vaultAssets: log.args.vaultAssets,
1664
+ zoneDepositHash: log.args.zoneDepositHash,
1665
+ });
1666
+ }
1667
+ catch (error) {
1668
+ unpoll();
1669
+ emit.reject(error);
1670
+ }
1671
+ }, { emitOnBegin: true, interval: pollingInterval });
1672
+ return unpoll;
1673
+ });
1674
+ timer = timeout
1675
+ ? setTimeout(() => {
1676
+ reject_(new WaitForPrivateRedeemTimeoutError({ actionId, gateway }));
1677
+ }, timeout)
1678
+ : undefined;
1679
+ return await promise;
1680
+ }
1681
+ /**
1682
+ * Withdraws an exact asset amount to `recipient`, up to the specified vault
1683
+ * share limit. The transaction includes the required vault share approval;
1684
+ * use {@link redeem} for a full exit.
1685
+ *
1686
+ * @example
1687
+ * ```ts
1688
+ * import { createClient, http } from 'viem'
1689
+ * import { privateKeyToAccount } from 'viem/accounts'
1690
+ * import { tempoModerato } from 'viem/chains'
1691
+ * import { Actions } from 'viem/tempo'
1692
+ *
1693
+ * const client = createClient({
1694
+ * account: privateKeyToAccount('0x...'),
1695
+ * chain: tempoModerato,
1696
+ * transport: http(),
1697
+ * })
1698
+ *
1699
+ * const hash = await Actions.earn.withdrawExact(client, {
1700
+ * assetAmount: 40_000_000n,
1701
+ * slippageBps: 50,
1702
+ * vault: '0x...',
1703
+ * })
1704
+ * ```
1705
+ *
1706
+ * @param client - Client.
1707
+ * @param parameters - Parameters.
1708
+ * @returns The transaction hash.
1709
+ */
1710
+ export async function withdrawExact(client, parameters) {
1711
+ return withdrawExact.inner(sendTransaction, client, parameters);
1712
+ }
1713
+ (function (withdrawExact) {
1714
+ /** @internal Shared dispatch; reads the vault share token for the approval. */
1715
+ async function inner(action, client, parameters) {
1716
+ const [args, shareToken] = await Promise.all([
1717
+ toWithdrawExactArgs(client, parameters),
1718
+ readContract(client, {
1719
+ abi: Abis.vaultAdapter,
1720
+ address: parameters.vault,
1721
+ functionName: 'shareToken',
1722
+ }),
1723
+ ]);
1724
+ return (await action(client, {
1725
+ ...parameters,
1726
+ calls: withdrawExact.calls({ ...args, shareToken }),
1727
+ }));
1728
+ }
1729
+ withdrawExact.inner = inner;
1730
+ /**
1731
+ * Defines an exact withdrawal call without an approval. Provide asset
1732
+ * decimals and an explicit input limit because this builder performs no reads.
1733
+ *
1734
+ * @param parameters - Client (optional), followed by the call arguments.
1735
+ * @returns The call.
1736
+ */
1737
+ function call(...parameters) {
1738
+ const [, args] = resolveCallParameters(parameters);
1739
+ const { recipient, vault } = args;
1740
+ const shareAmountMax = (() => {
1741
+ if (args.shareAmountMax !== undefined)
1742
+ return args.shareAmountMax;
1743
+ return maximumInput(args.shareAmount, args.slippageBps);
1744
+ })();
1745
+ return defineCall({
1746
+ address: vault,
1747
+ abi: Abis.vaultAdapter,
1748
+ functionName: 'withdrawExact',
1749
+ args: [
1750
+ internal_Token.toBaseUnits(args.assetAmount, undefined),
1751
+ recipient,
1752
+ shareAmountMax,
1753
+ ],
1754
+ });
1755
+ }
1756
+ withdrawExact.call = call;
1757
+ /**
1758
+ * Defines the vault share approval and withdrawal calls for atomic
1759
+ * execution. Pass `shareToken` explicitly because this builder performs no reads.
1760
+ *
1761
+ * @param args - Arguments.
1762
+ * @returns The calls.
1763
+ */
1764
+ function calls(args) {
1765
+ const { shareToken, vault } = args;
1766
+ const assetAmount = internal_Token.toBaseUnits(args.assetAmount, undefined);
1767
+ const call = withdrawExact.call({ ...args, assetAmount });
1768
+ const [, , shareAmountMax] = call.args;
1769
+ return [
1770
+ defineCall({
1771
+ address: shareToken,
1772
+ abi: Abis.tip20,
1773
+ functionName: 'approve',
1774
+ args: [vault, shareAmountMax],
1775
+ }),
1776
+ call,
1777
+ ];
1778
+ }
1779
+ withdrawExact.calls = calls;
1780
+ /**
1781
+ * Extracts a `WithdrewExact` event from the vault's logs.
1782
+ *
1783
+ * @param logs - Logs.
1784
+ * @param parameters - Parameters.
1785
+ * @returns The `WithdrewExact` event.
1786
+ */
1787
+ function extractEvent(logs, parameters) {
1788
+ const { vault } = parameters;
1789
+ // Earn contracts are user-deployed: several adapters can emit the same
1790
+ // signature in one receipt, so filter by emitting address before decode.
1791
+ const [log] = parseEventLogs({
1792
+ abi: Abis.vaultAdapter,
1793
+ eventName: 'WithdrewExact',
1794
+ logs: logs.filter((log) => isAddressEqual(log.address, vault)),
1795
+ });
1796
+ if (!log)
1797
+ throw new Error('`WithdrewExact` event not found.');
1798
+ return log;
1799
+ }
1800
+ withdrawExact.extractEvent = extractEvent;
1801
+ /**
1802
+ * Estimates gas for an exact withdrawal, assuming enough vault share allowance.
1803
+ *
1804
+ * @param client - Client.
1805
+ * @param parameters - Parameters.
1806
+ * @returns The gas estimate.
1807
+ */
1808
+ async function estimateGas(client, parameters) {
1809
+ return estimateContractGas(client, {
1810
+ ...pickWriteParameters(parameters),
1811
+ ...withdrawExact.call(await toWithdrawExactArgs(client, parameters)),
1812
+ });
1813
+ }
1814
+ withdrawExact.estimateGas = estimateGas;
1815
+ /**
1816
+ * Simulates an exact withdrawal, assuming enough vault share allowance.
1817
+ *
1818
+ * @param client - Client.
1819
+ * @param parameters - Parameters.
1820
+ * @returns The simulation result and write request.
1821
+ */
1822
+ async function simulate(client, parameters) {
1823
+ return simulateContract(client, {
1824
+ ...pickWriteParameters(parameters),
1825
+ ...withdrawExact.call(await toWithdrawExactArgs(client, parameters)),
1826
+ });
1827
+ }
1828
+ withdrawExact.simulate = simulate;
1829
+ })(withdrawExact || (withdrawExact = {}));
1830
+ /**
1831
+ * Withdraws an exact asset amount and returns the confirmed receipt and event data.
1832
+ *
1833
+ * @example
1834
+ * ```ts
1835
+ * import { createClient, http } from 'viem'
1836
+ * import { privateKeyToAccount } from 'viem/accounts'
1837
+ * import { tempoModerato } from 'viem/chains'
1838
+ * import { Actions } from 'viem/tempo'
1839
+ *
1840
+ * const client = createClient({
1841
+ * account: privateKeyToAccount('0x...'),
1842
+ * chain: tempoModerato,
1843
+ * transport: http(),
1844
+ * })
1845
+ *
1846
+ * const { shareAmount } = await Actions.earn.withdrawExactSync(client, {
1847
+ * assetAmount: 40_000_000n,
1848
+ * shareAmountMax: 40_200_000n,
1849
+ * vault: '0x...',
1850
+ * })
1851
+ * ```
1852
+ *
1853
+ * @param client - Client.
1854
+ * @param parameters - Parameters.
1855
+ * @returns The transaction receipt and event data.
1856
+ */
1857
+ export async function withdrawExactSync(client, parameters) {
1858
+ const { throwOnReceiptRevert = true, vault } = parameters;
1859
+ const receipt = await withdrawExact.inner(sendTransactionSync, client, {
1860
+ ...parameters,
1861
+ throwOnReceiptRevert,
1862
+ });
1863
+ const { args } = withdrawExact.extractEvent(receipt.logs, { vault });
1864
+ return {
1865
+ assetAmount: args.assets,
1866
+ caller: args.caller,
1867
+ receipt,
1868
+ recipient: args.receiver,
1869
+ shareAmount: args.sharesBurned,
1870
+ };
1871
+ }
1872
+ const zoneGatewayCallbackGas = 10000000n;
1873
+ function resolveMinimumShareAmount(parameters) {
1874
+ if (parameters.shareAmountMin !== undefined)
1875
+ return EarnShares.minimumOutput(parameters.shareAmountMin, 0);
1876
+ return EarnShares.minimumOutput(parameters.shareAmount, parameters.slippageBps);
1877
+ }
1878
+ async function assertPreparedZoneRequestChain(client, parameters) {
1879
+ const chain = client.chain;
1880
+ if (!chain)
1881
+ throw new Error('`chain` is required.');
1882
+ if (chain.sourceId !== parameters.chainId)
1883
+ throw new Error('Prepared Zone request parent chain ID does not match client chain.');
1884
+ const { zoneId } = await zoneActions.getZoneInfo(client);
1885
+ if (zoneId !== parameters.zoneId)
1886
+ throw new Error('Prepared Zone request Zone ID does not match client chain.');
1887
+ }
1888
+ function pickReadParameters(parameters) {
1889
+ const { blockOverrides, stateOverride } = parameters;
1890
+ if (parameters.blockNumber !== undefined)
1891
+ return {
1892
+ blockNumber: parameters.blockNumber,
1893
+ blockOverrides,
1894
+ stateOverride,
1895
+ };
1896
+ return { blockOverrides, blockTag: parameters.blockTag, stateOverride };
1897
+ }
1898
+ async function getZoneGatewayConfig(client, parameters) {
1899
+ const { gateway, ...rest } = parameters;
1900
+ const [vaultAdapter, vaultAsset, shareToken, zoneId, zonePortal, supportsAsyncFlow,] = await multicall(client, {
1901
+ ...rest,
1902
+ allowFailure: false,
1903
+ contracts: [
1904
+ {
1905
+ abi: Abis.zoneGateway,
1906
+ address: gateway,
1907
+ functionName: 'vaultAdapter',
1908
+ },
1909
+ {
1910
+ abi: Abis.zoneGateway,
1911
+ address: gateway,
1912
+ functionName: 'vaultAsset',
1913
+ },
1914
+ {
1915
+ abi: Abis.zoneGateway,
1916
+ address: gateway,
1917
+ functionName: 'shareToken',
1918
+ },
1919
+ {
1920
+ abi: Abis.zoneGateway,
1921
+ address: gateway,
1922
+ functionName: 'zoneId',
1923
+ },
1924
+ {
1925
+ abi: Abis.zoneGateway,
1926
+ address: gateway,
1927
+ functionName: 'zonePortal',
1928
+ },
1929
+ {
1930
+ abi: Abis.zoneGateway,
1931
+ address: gateway,
1932
+ args: [2],
1933
+ functionName: 'supportsFlow',
1934
+ },
1935
+ ],
1936
+ deployless: true,
1937
+ });
1938
+ if (supportsAsyncFlow)
1939
+ throw new Error('Async Zone gateways are not supported.');
1940
+ return { shareToken, vaultAdapter, vaultAsset, zoneId, zonePortal };
1941
+ }
1942
+ // ERC-165 ids of the optional engine capability interfaces (XOR of each
1943
+ // interface's function selectors).
1944
+ const interfaceIds = {
1945
+ /** `IVaultEngineAsync`. */
1946
+ asyncRedeem: '0xa1a6a1d7',
1947
+ /** `IVaultEngineExactWithdraw`. */
1948
+ exactWithdraw: '0x0adfb0b9',
1949
+ /** `IVaultEngineShares`. */
1950
+ inKindDeposit: '0x7d28a2f2',
1951
+ /** `IVaultEngineSync`. */
1952
+ syncRedeem: '0x370457f4',
1953
+ };
1954
+ /** Trims the decoded `IVaultFees.FeeConfig` to its active fixed-fee count. */
1955
+ function toFeeConfig(config) {
1956
+ return {
1957
+ excess: config.excess,
1958
+ fixedFees: config.fixedFees.slice(0, config.fixedFeeCount),
1959
+ };
1960
+ }
1961
+ /** Trims the decoded `IVaultFees.FeePreview` to its active allocation count. */
1962
+ function toFeePreview(preview) {
1963
+ const { allocationCount, allocations, ...rest } = preview;
1964
+ return { ...rest, allocations: allocations.slice(0, allocationCount) };
1965
+ }
1966
+ /** Resolves `deposit` parameters into the adapter call args. @internal */
1967
+ async function toDepositArgs(client, parameters) {
1968
+ const { vault } = parameters;
1969
+ const assetAmount = await toBaseUnitsLive(client, {
1970
+ amount: parameters.assetAmount,
1971
+ token: 'asset',
1972
+ vault,
1973
+ });
1974
+ const args = {
1975
+ assetAmount,
1976
+ recipient: resolveRecipient(client, parameters),
1977
+ vault,
1978
+ };
1979
+ if (parameters.shareAmountMin !== undefined)
1980
+ return { ...args, shareAmountMin: parameters.shareAmountMin };
1981
+ return {
1982
+ ...args,
1983
+ shareAmount: parameters.shareAmount,
1984
+ slippageBps: parameters.slippageBps,
1985
+ };
1986
+ }
1987
+ /** Resolves `depositShares` parameters into the adapter call args. @internal */
1988
+ function toDepositSharesArgs(client, parameters) {
1989
+ const { vault, venueShareAmount } = parameters;
1990
+ const args = {
1991
+ recipient: resolveRecipient(client, parameters),
1992
+ vault,
1993
+ venueShareAmount,
1994
+ };
1995
+ if (parameters.earnShareAmountMin !== undefined)
1996
+ return { ...args, earnShareAmountMin: parameters.earnShareAmountMin };
1997
+ return {
1998
+ ...args,
1999
+ earnShareAmount: parameters.earnShareAmount,
2000
+ slippageBps: parameters.slippageBps,
2001
+ };
2002
+ }
2003
+ /** Resolves `redeem` parameters into the adapter call args. @internal */
2004
+ async function toRedeemArgs(client, parameters) {
2005
+ const { vault } = parameters;
2006
+ const shareAmount = await toBaseUnitsLive(client, {
2007
+ amount: parameters.shareAmount,
2008
+ token: 'shareToken',
2009
+ vault,
2010
+ });
2011
+ const args = {
2012
+ recipient: resolveRecipient(client, parameters),
2013
+ shareAmount,
2014
+ vault,
2015
+ };
2016
+ if (parameters.assetAmountMin !== undefined)
2017
+ return { ...args, assetAmountMin: parameters.assetAmountMin };
2018
+ const assetAmount = await (async () => {
2019
+ if (parameters.assetAmount !== undefined)
2020
+ return parameters.assetAmount;
2021
+ return getRedeemQuote(client, { shareAmount, vault });
2022
+ })();
2023
+ return {
2024
+ ...args,
2025
+ assetAmount,
2026
+ slippageBps: parameters.slippageBps,
2027
+ };
2028
+ }
2029
+ /** Resolves `withdrawExact` parameters into the adapter call args. @internal */
2030
+ async function toWithdrawExactArgs(client, parameters) {
2031
+ const { vault } = parameters;
2032
+ const assetAmount = await toBaseUnitsLive(client, {
2033
+ amount: parameters.assetAmount,
2034
+ token: 'asset',
2035
+ vault,
2036
+ });
2037
+ const args = {
2038
+ assetAmount,
2039
+ recipient: resolveRecipient(client, parameters),
2040
+ vault,
2041
+ };
2042
+ if (parameters.shareAmountMax !== undefined)
2043
+ return { ...args, shareAmountMax: parameters.shareAmountMax };
2044
+ const shareAmount = await (async () => {
2045
+ if (parameters.shareAmount !== undefined)
2046
+ return parameters.shareAmount;
2047
+ return getWithdrawQuote(client, { assetAmount, vault });
2048
+ })();
2049
+ return {
2050
+ ...args,
2051
+ shareAmount,
2052
+ slippageBps: parameters.slippageBps,
2053
+ };
2054
+ }
2055
+ /** Raises a quoted input by basis points with ceiling rounding. @internal */
2056
+ function maximumInput(shareAmount, slippageBps) {
2057
+ if (shareAmount <= 0n)
2058
+ throw new EarnShares.InvalidExpectedOutputError({
2059
+ expectedAmount: shareAmount,
2060
+ });
2061
+ if (!Number.isInteger(slippageBps) ||
2062
+ slippageBps < 0 ||
2063
+ slippageBps >= EarnShares.basisPointScale)
2064
+ throw new EarnShares.InvalidSlippageError({ slippageBps });
2065
+ const scale = BigInt(EarnShares.basisPointScale);
2066
+ const numerator = shareAmount * (scale + BigInt(slippageBps));
2067
+ // Adding the denominator minus one converts floor division to ceiling.
2068
+ return (numerator + scale - 1n) / scale;
2069
+ }
2070
+ /**
2071
+ * Converts an amount to base units, resolving missing decimals with live
2072
+ * reads of the vault's asset or share token. Vault share tokens are not
2073
+ * genesis-declared, so nothing is cached. @internal
2074
+ */
2075
+ async function toBaseUnitsLive(client, options) {
2076
+ const { amount, token, vault } = options;
2077
+ if (typeof amount === 'bigint')
2078
+ return amount;
2079
+ if (amount.decimals !== undefined)
2080
+ return internal_Token.toBaseUnits(amount, amount.decimals);
2081
+ const address = await readContract(client, {
2082
+ abi: Abis.vaultAdapter,
2083
+ address: vault,
2084
+ functionName: token,
2085
+ });
2086
+ const { decimals } = await resolveTokenWithDecimals(client, {
2087
+ token: address,
2088
+ });
2089
+ return internal_Token.toBaseUnits(amount, decimals);
2090
+ }
2091
+ /** Defaults a write's `recipient` to the sending account's address. @internal */
2092
+ function resolveRecipient(client, parameters) {
2093
+ if (parameters.recipient)
2094
+ return parameters.recipient;
2095
+ const account = parameters.account ?? client.account;
2096
+ if (!account)
2097
+ throw new AccountNotFoundError();
2098
+ return parseAccount(account).address;
2099
+ }
2100
+ //# sourceMappingURL=earn.js.map