viem 2.55.5 → 2.55.7
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.
- package/CHANGELOG.md +22 -0
- package/_cjs/errors/version.js +1 -1
- package/_cjs/tempo/Abis.js +1809 -1
- package/_cjs/tempo/Abis.js.map +1 -1
- package/_cjs/tempo/Decorator.js +25 -2
- package/_cjs/tempo/Decorator.js.map +1 -1
- package/_cjs/tempo/actions/earn.js +1368 -0
- package/_cjs/tempo/actions/earn.js.map +1 -0
- package/_cjs/tempo/actions/index.js +2 -1
- package/_cjs/tempo/actions/index.js.map +1 -1
- package/_cjs/tempo/actions/zone.js +47 -61
- package/_cjs/tempo/actions/zone.js.map +1 -1
- package/_cjs/tempo/chainConfig.js +1 -1
- package/_cjs/tempo/chainConfig.js.map +1 -1
- package/_cjs/tempo/errors.js +28 -4
- package/_cjs/tempo/errors.js.map +1 -1
- package/_cjs/tempo/index.js +2 -1
- package/_cjs/tempo/index.js.map +1 -1
- package/_cjs/tempo/internal/WithdrawalSenderTag.js +9 -0
- package/_cjs/tempo/internal/WithdrawalSenderTag.js.map +1 -0
- package/_cjs/tempo/zones/Abis.js +46 -0
- package/_cjs/tempo/zones/Abis.js.map +1 -1
- package/_cjs/utils/blob/fromBlobs.js +5 -2
- package/_cjs/utils/blob/fromBlobs.js.map +1 -1
- package/_cjs/utils/encoding/fromBytes.js +2 -2
- package/_cjs/utils/encoding/fromBytes.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/tempo/Abis.js +1810 -0
- package/_esm/tempo/Abis.js.map +1 -1
- package/_esm/tempo/Decorator.js +25 -2
- package/_esm/tempo/Decorator.js.map +1 -1
- package/_esm/tempo/actions/earn.js +2100 -0
- package/_esm/tempo/actions/earn.js.map +1 -0
- package/_esm/tempo/actions/index.js +1 -0
- package/_esm/tempo/actions/index.js.map +1 -1
- package/_esm/tempo/actions/zone.js +104 -119
- package/_esm/tempo/actions/zone.js.map +1 -1
- package/_esm/tempo/chainConfig.js +2 -1
- package/_esm/tempo/chainConfig.js.map +1 -1
- package/_esm/tempo/errors.js +25 -2
- package/_esm/tempo/errors.js.map +1 -1
- package/_esm/tempo/index.js +1 -1
- package/_esm/tempo/index.js.map +1 -1
- package/_esm/tempo/internal/WithdrawalSenderTag.js +7 -0
- package/_esm/tempo/internal/WithdrawalSenderTag.js.map +1 -0
- package/_esm/tempo/zones/Abis.js +46 -0
- package/_esm/tempo/zones/Abis.js.map +1 -1
- package/_esm/utils/blob/fromBlobs.js +6 -2
- package/_esm/utils/blob/fromBlobs.js.map +1 -1
- package/_esm/utils/encoding/fromBytes.js +2 -2
- package/_esm/utils/encoding/fromBytes.js.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/tempo/Abis.d.ts +2880 -0
- package/_types/tempo/Abis.d.ts.map +1 -1
- package/_types/tempo/Decorator.d.ts +499 -41
- package/_types/tempo/Decorator.d.ts.map +1 -1
- package/_types/tempo/actions/earn.d.ts +7549 -0
- package/_types/tempo/actions/earn.d.ts.map +1 -0
- package/_types/tempo/actions/index.d.ts +1 -0
- package/_types/tempo/actions/index.d.ts.map +1 -1
- package/_types/tempo/actions/zone.d.ts +86 -109
- package/_types/tempo/actions/zone.d.ts.map +1 -1
- package/_types/tempo/chainConfig.d.ts.map +1 -1
- package/_types/tempo/errors.d.ts +33 -3
- package/_types/tempo/errors.d.ts.map +1 -1
- package/_types/tempo/index.d.ts +1 -1
- package/_types/tempo/index.d.ts.map +1 -1
- package/_types/tempo/internal/WithdrawalSenderTag.d.ts +11 -0
- package/_types/tempo/internal/WithdrawalSenderTag.d.ts.map +1 -0
- package/_types/tempo/zones/Abis.d.ts +87 -0
- package/_types/tempo/zones/Abis.d.ts.map +1 -1
- package/_types/utils/blob/fromBlobs.d.ts.map +1 -1
- package/errors/version.ts +1 -1
- package/package.json +2 -2
- package/tempo/Abis.ts +1822 -0
- package/tempo/Decorator.ts +572 -47
- package/tempo/actions/earn.ts +3319 -0
- package/tempo/actions/index.ts +1 -0
- package/tempo/actions/zone.ts +183 -230
- package/tempo/chainConfig.ts +3 -1
- package/tempo/errors.ts +54 -6
- package/tempo/index.ts +1 -0
- package/tempo/internal/WithdrawalSenderTag.ts +20 -0
- package/tempo/zones/Abis.ts +46 -0
- package/utils/blob/fromBlobs.ts +6 -2
- 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
|