viem 2.55.11 → 2.55.13
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/actions/wallet/prepareTransactionRequest.js +12 -12
- package/_cjs/actions/wallet/prepareTransactionRequest.js.map +1 -1
- package/_cjs/actions/wallet/sendTransaction.js +24 -12
- package/_cjs/actions/wallet/sendTransaction.js.map +1 -1
- package/_cjs/actions/wallet/sendTransactionSync.js +24 -12
- package/_cjs/actions/wallet/sendTransactionSync.js.map +1 -1
- package/_cjs/clients/createClient.js +1 -0
- package/_cjs/clients/createClient.js.map +1 -1
- package/_cjs/errors/version.js +1 -1
- package/_cjs/tempo/Decorator.js +7 -0
- package/_cjs/tempo/Decorator.js.map +1 -1
- package/_cjs/tempo/actions/earn/deployment.js +654 -0
- package/_cjs/tempo/actions/earn/deployment.js.map +1 -0
- package/_cjs/tempo/actions/earn.js +15 -0
- package/_cjs/tempo/actions/earn.js.map +1 -1
- package/_cjs/tempo/actions/zone.js +11 -1
- package/_cjs/tempo/actions/zone.js.map +1 -1
- package/_cjs/tempo/chainConfig.js +24 -23
- package/_cjs/tempo/chainConfig.js.map +1 -1
- package/_cjs/tempo/internal/WithdrawalSenderTag.js +2 -2
- package/_cjs/tempo/internal/WithdrawalSenderTag.js.map +1 -1
- package/_cjs/tempo/zones/Abis.js +17 -0
- package/_cjs/tempo/zones/Abis.js.map +1 -1
- package/_esm/actions/wallet/prepareTransactionRequest.js +14 -13
- package/_esm/actions/wallet/prepareTransactionRequest.js.map +1 -1
- package/_esm/actions/wallet/sendTransaction.js +24 -12
- package/_esm/actions/wallet/sendTransaction.js.map +1 -1
- package/_esm/actions/wallet/sendTransactionSync.js +24 -12
- package/_esm/actions/wallet/sendTransactionSync.js.map +1 -1
- package/_esm/clients/createClient.js +4 -3
- package/_esm/clients/createClient.js.map +1 -1
- package/_esm/errors/version.js +1 -1
- package/_esm/tempo/Decorator.js +7 -0
- package/_esm/tempo/Decorator.js.map +1 -1
- package/_esm/tempo/actions/earn/deployment.js +967 -0
- package/_esm/tempo/actions/earn/deployment.js.map +1 -0
- package/_esm/tempo/actions/earn.js +2 -0
- package/_esm/tempo/actions/earn.js.map +1 -1
- package/_esm/tempo/actions/zone.js +11 -1
- package/_esm/tempo/actions/zone.js.map +1 -1
- package/_esm/tempo/chainConfig.js +26 -28
- package/_esm/tempo/chainConfig.js.map +1 -1
- package/_esm/tempo/internal/WithdrawalSenderTag.js +3 -3
- package/_esm/tempo/internal/WithdrawalSenderTag.js.map +1 -1
- package/_esm/tempo/zones/Abis.js +17 -0
- package/_esm/tempo/zones/Abis.js.map +1 -1
- package/_types/actions/wallet/prepareTransactionRequest.d.ts.map +1 -1
- package/_types/actions/wallet/sendTransaction.d.ts.map +1 -1
- package/_types/actions/wallet/sendTransactionSync.d.ts.map +1 -1
- package/_types/clients/createClient.d.ts +3 -3
- package/_types/clients/createClient.d.ts.map +1 -1
- package/_types/errors/version.d.ts +1 -1
- package/_types/tempo/Decorator.d.ts +18 -0
- package/_types/tempo/Decorator.d.ts.map +1 -1
- package/_types/tempo/actions/earn/deployment.d.ts +1710 -0
- package/_types/tempo/actions/earn/deployment.d.ts.map +1 -0
- package/_types/tempo/actions/earn.d.ts +1 -1
- package/_types/tempo/actions/earn.d.ts.map +1 -1
- package/_types/tempo/actions/zone.d.ts.map +1 -1
- package/_types/tempo/chainConfig.d.ts.map +1 -1
- package/_types/tempo/internal/WithdrawalSenderTag.d.ts +2 -1
- package/_types/tempo/internal/WithdrawalSenderTag.d.ts.map +1 -1
- package/_types/tempo/zones/Abis.d.ts +48 -0
- package/_types/tempo/zones/Abis.d.ts.map +1 -1
- package/actions/wallet/prepareTransactionRequest.ts +18 -17
- package/actions/wallet/sendTransaction.ts +25 -10
- package/actions/wallet/sendTransactionSync.ts +25 -10
- package/clients/createClient.ts +4 -3
- package/errors/version.ts +1 -1
- package/package.json +1 -1
- package/tempo/Decorator.ts +41 -0
- package/tempo/actions/earn/deployment.ts +1427 -0
- package/tempo/actions/earn.ts +3 -0
- package/tempo/actions/zone.ts +10 -1
- package/tempo/chainConfig.ts +30 -27
- package/tempo/internal/WithdrawalSenderTag.ts +5 -4
- package/tempo/zones/Abis.ts +17 -0
|
@@ -0,0 +1,967 @@
|
|
|
1
|
+
import { Hex } from 'ox';
|
|
2
|
+
import { parseAccount } from '../../../accounts/utils/parseAccount.js';
|
|
3
|
+
import { getCode } from '../../../actions/public/getCode.js';
|
|
4
|
+
import { getLogs } from '../../../actions/public/getLogs.js';
|
|
5
|
+
import { readContract } from '../../../actions/public/readContract.js';
|
|
6
|
+
import { simulateContract } from '../../../actions/public/simulateContract.js';
|
|
7
|
+
import { writeContract, } from '../../../actions/wallet/writeContract.js';
|
|
8
|
+
import { writeContractSync } from '../../../actions/wallet/writeContractSync.js';
|
|
9
|
+
import { zeroAddress } from '../../../constants/address.js';
|
|
10
|
+
import { maxUint64, maxUint256 } from '../../../constants/number.js';
|
|
11
|
+
import { AccountNotFoundError } from '../../../errors/account.js';
|
|
12
|
+
import { BaseError } from '../../../errors/base.js';
|
|
13
|
+
import { TransactionReceiptRevertedError } from '../../../errors/transaction.js';
|
|
14
|
+
import { getAbiItem } from '../../../utils/abi/getAbiItem.js';
|
|
15
|
+
import { parseEventLogs } from '../../../utils/abi/parseEventLogs.js';
|
|
16
|
+
import { isAddressEqual } from '../../../utils/address/isAddressEqual.js';
|
|
17
|
+
import * as Abis from '../../Abis.js';
|
|
18
|
+
import * as Addresses from '../../Addresses.js';
|
|
19
|
+
import { defineCall, pickWriteParameters, pickWriteSyncParameters, } from '../../internal/utils.js';
|
|
20
|
+
/**
|
|
21
|
+
* Deploys a deterministic ERC-4626 Earn engine.
|
|
22
|
+
*
|
|
23
|
+
* @experimental
|
|
24
|
+
*
|
|
25
|
+
* @example
|
|
26
|
+
* ```ts
|
|
27
|
+
* import { createClient, http } from 'viem'
|
|
28
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
29
|
+
* import { tempoModerato } from 'viem/chains'
|
|
30
|
+
* import { Actions } from 'viem/tempo'
|
|
31
|
+
*
|
|
32
|
+
* const client = createClient({
|
|
33
|
+
* account: privateKeyToAccount('0x...'),
|
|
34
|
+
* chain: tempoModerato,
|
|
35
|
+
* transport: http(),
|
|
36
|
+
* })
|
|
37
|
+
*
|
|
38
|
+
* const hash = await Actions.earn.createErc4626Engine(client, {
|
|
39
|
+
* deploymentId: '0x...',
|
|
40
|
+
* factory: '0x...',
|
|
41
|
+
* venue: '0x...',
|
|
42
|
+
* })
|
|
43
|
+
* ```
|
|
44
|
+
*
|
|
45
|
+
* @param client - Client.
|
|
46
|
+
* @param parameters - Parameters.
|
|
47
|
+
* @returns The transaction hash.
|
|
48
|
+
*/
|
|
49
|
+
export async function createErc4626Engine(client, parameters) {
|
|
50
|
+
return createErc4626Engine.inner(writeContract, client, parameters);
|
|
51
|
+
}
|
|
52
|
+
(function (createErc4626Engine) {
|
|
53
|
+
/** @internal */
|
|
54
|
+
async function inner(action, client, parameters) {
|
|
55
|
+
const { account = client.account, chain = client.chain, deploymentId, factory, name, owner: owner_ = account, symbol, venue, ...rest } = parameters;
|
|
56
|
+
if (!account)
|
|
57
|
+
throw new AccountNotFoundError();
|
|
58
|
+
if (!owner_)
|
|
59
|
+
throw new Error('`owner` is required.');
|
|
60
|
+
const owner = parseAccount(owner_).address;
|
|
61
|
+
return (await action(client, {
|
|
62
|
+
...rest,
|
|
63
|
+
account,
|
|
64
|
+
chain,
|
|
65
|
+
...createErc4626Engine.call({
|
|
66
|
+
deploymentId,
|
|
67
|
+
factory,
|
|
68
|
+
name,
|
|
69
|
+
owner,
|
|
70
|
+
symbol,
|
|
71
|
+
venue,
|
|
72
|
+
}),
|
|
73
|
+
}));
|
|
74
|
+
}
|
|
75
|
+
createErc4626Engine.inner = inner;
|
|
76
|
+
/**
|
|
77
|
+
* Defines a call to `ERC4626EngineFactory.deploy`.
|
|
78
|
+
*
|
|
79
|
+
* Can be passed as a parameter to:
|
|
80
|
+
* - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate the gas cost of the call
|
|
81
|
+
* - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the call
|
|
82
|
+
* - [`sendCalls`](https://viem.sh/docs/actions/wallet/sendCalls): send multiple calls
|
|
83
|
+
*
|
|
84
|
+
* @example
|
|
85
|
+
* ```ts
|
|
86
|
+
* import { createClient, http, walletActions } from 'viem'
|
|
87
|
+
* import { tempoModerato } from 'viem/chains'
|
|
88
|
+
* import { Actions } from 'viem/tempo'
|
|
89
|
+
*
|
|
90
|
+
* const client = createClient({ chain: tempoModerato, transport: http() })
|
|
91
|
+
* .extend(walletActions)
|
|
92
|
+
* await client.sendTransaction({
|
|
93
|
+
* calls: [Actions.earn.createErc4626Engine.call({
|
|
94
|
+
* deploymentId: '0x...',
|
|
95
|
+
* factory: '0x...',
|
|
96
|
+
* owner: '0x...',
|
|
97
|
+
* venue: '0x...',
|
|
98
|
+
* })],
|
|
99
|
+
* })
|
|
100
|
+
* ```
|
|
101
|
+
*
|
|
102
|
+
* @param args - Arguments.
|
|
103
|
+
* @returns The call.
|
|
104
|
+
*/
|
|
105
|
+
function call(args) {
|
|
106
|
+
validateDeploymentId(args.deploymentId);
|
|
107
|
+
return defineCall({
|
|
108
|
+
address: args.factory,
|
|
109
|
+
abi: Abis.erc4626EngineFactory,
|
|
110
|
+
functionName: 'deploy',
|
|
111
|
+
args: [
|
|
112
|
+
args.deploymentId,
|
|
113
|
+
args.venue,
|
|
114
|
+
args.owner,
|
|
115
|
+
args.name ?? '',
|
|
116
|
+
args.symbol ?? '',
|
|
117
|
+
],
|
|
118
|
+
});
|
|
119
|
+
}
|
|
120
|
+
createErc4626Engine.call = call;
|
|
121
|
+
/**
|
|
122
|
+
* Predicts the deterministic engine address.
|
|
123
|
+
*
|
|
124
|
+
* @param client - Client.
|
|
125
|
+
* @param args - Engine deployment arguments.
|
|
126
|
+
* @returns The predicted engine address.
|
|
127
|
+
*/
|
|
128
|
+
async function predict(client, args) {
|
|
129
|
+
validateDeploymentId(args.deploymentId);
|
|
130
|
+
return readContract(client, {
|
|
131
|
+
address: args.factory,
|
|
132
|
+
abi: Abis.erc4626EngineFactory,
|
|
133
|
+
functionName: 'predictEngine',
|
|
134
|
+
args: [
|
|
135
|
+
args.deploymentId,
|
|
136
|
+
args.venue,
|
|
137
|
+
args.owner,
|
|
138
|
+
args.name ?? '',
|
|
139
|
+
args.symbol ?? '',
|
|
140
|
+
],
|
|
141
|
+
});
|
|
142
|
+
}
|
|
143
|
+
createErc4626Engine.predict = predict;
|
|
144
|
+
/**
|
|
145
|
+
* Extracts the `ERC4626EngineDeployed` event from factory logs.
|
|
146
|
+
*
|
|
147
|
+
* @param logs - The logs.
|
|
148
|
+
* @param parameters - Factory address used to filter the logs.
|
|
149
|
+
* @returns The deployment event.
|
|
150
|
+
*/
|
|
151
|
+
function extractEvent(logs, parameters) {
|
|
152
|
+
const [log] = parseEventLogs({
|
|
153
|
+
abi: Abis.erc4626EngineFactory,
|
|
154
|
+
eventName: 'ERC4626EngineDeployed',
|
|
155
|
+
logs: logs.filter((log) => isAddressEqual(log.address, parameters.factory)),
|
|
156
|
+
strict: true,
|
|
157
|
+
});
|
|
158
|
+
if (!log)
|
|
159
|
+
throw new Error('`ERC4626EngineDeployed` event not found.');
|
|
160
|
+
return log;
|
|
161
|
+
}
|
|
162
|
+
createErc4626Engine.extractEvent = extractEvent;
|
|
163
|
+
})(createErc4626Engine || (createErc4626Engine = {}));
|
|
164
|
+
/**
|
|
165
|
+
* Deploys an ERC-4626 engine and waits for confirmation.
|
|
166
|
+
*
|
|
167
|
+
* @experimental
|
|
168
|
+
*
|
|
169
|
+
* @example
|
|
170
|
+
* ```ts
|
|
171
|
+
* import { createClient, http } from 'viem'
|
|
172
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
173
|
+
* import { tempoModerato } from 'viem/chains'
|
|
174
|
+
* import { Actions } from 'viem/tempo'
|
|
175
|
+
*
|
|
176
|
+
* const client = createClient({
|
|
177
|
+
* account: privateKeyToAccount('0x...'),
|
|
178
|
+
* chain: tempoModerato,
|
|
179
|
+
* transport: http(),
|
|
180
|
+
* })
|
|
181
|
+
* const result = await Actions.earn.createErc4626EngineSync(client, {
|
|
182
|
+
* deploymentId: '0x...',
|
|
183
|
+
* factory: '0x...',
|
|
184
|
+
* venue: '0x...',
|
|
185
|
+
* })
|
|
186
|
+
* ```
|
|
187
|
+
*
|
|
188
|
+
* @param client - Client.
|
|
189
|
+
* @param parameters - Parameters.
|
|
190
|
+
* @returns The receipt and deployed engine metadata.
|
|
191
|
+
*/
|
|
192
|
+
export async function createErc4626EngineSync(client, parameters) {
|
|
193
|
+
const { factory, throwOnReceiptRevert = true } = parameters;
|
|
194
|
+
const receipt = await createErc4626Engine.inner(writeContractSync, client, {
|
|
195
|
+
...parameters,
|
|
196
|
+
throwOnReceiptRevert,
|
|
197
|
+
});
|
|
198
|
+
const { args } = createErc4626Engine.extractEvent(receipt.logs, { factory });
|
|
199
|
+
return { ...args, receipt };
|
|
200
|
+
}
|
|
201
|
+
/**
|
|
202
|
+
* Creates an EarnShare, EarnVault, and EarnFees stack around an unbound engine.
|
|
203
|
+
*
|
|
204
|
+
* @experimental
|
|
205
|
+
*
|
|
206
|
+
* @example
|
|
207
|
+
* ```ts
|
|
208
|
+
* import { createClient, http } from 'viem'
|
|
209
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
210
|
+
* import { tempoModerato } from 'viem/chains'
|
|
211
|
+
* import { Actions } from 'viem/tempo'
|
|
212
|
+
*
|
|
213
|
+
* const client = createClient({
|
|
214
|
+
* account: privateKeyToAccount('0x...'),
|
|
215
|
+
* chain: tempoModerato,
|
|
216
|
+
* transport: http(),
|
|
217
|
+
* })
|
|
218
|
+
* const hash = await Actions.earn.createStack(client, {
|
|
219
|
+
* deploymentId: '0x...',
|
|
220
|
+
* engine: '0x...',
|
|
221
|
+
* factory: '0x...',
|
|
222
|
+
* })
|
|
223
|
+
* ```
|
|
224
|
+
*
|
|
225
|
+
* @param client - Client.
|
|
226
|
+
* @param parameters - Parameters.
|
|
227
|
+
* @returns The transaction hash.
|
|
228
|
+
*/
|
|
229
|
+
export async function createStack(client, parameters) {
|
|
230
|
+
return createStack.inner(writeContract, client, parameters);
|
|
231
|
+
}
|
|
232
|
+
(function (createStack) {
|
|
233
|
+
/** @internal */
|
|
234
|
+
async function inner(action, client, parameters) {
|
|
235
|
+
const { account = client.account, chain = client.chain, controls, deploymentId, distributor, engine, factory, fees, owner: owner_ = account, transferPolicyId, ...rest } = parameters;
|
|
236
|
+
if (!account)
|
|
237
|
+
throw new AccountNotFoundError();
|
|
238
|
+
if (!owner_)
|
|
239
|
+
throw new Error('`owner` is required.');
|
|
240
|
+
return (await action(client, {
|
|
241
|
+
...rest,
|
|
242
|
+
account,
|
|
243
|
+
chain,
|
|
244
|
+
...createStack.call({
|
|
245
|
+
controls,
|
|
246
|
+
deploymentId,
|
|
247
|
+
distributor,
|
|
248
|
+
engine,
|
|
249
|
+
factory,
|
|
250
|
+
fees,
|
|
251
|
+
owner: parseAccount(owner_).address,
|
|
252
|
+
transferPolicyId,
|
|
253
|
+
}),
|
|
254
|
+
}));
|
|
255
|
+
}
|
|
256
|
+
createStack.inner = inner;
|
|
257
|
+
/**
|
|
258
|
+
* Defines a call to `EarnFactory.deploy`.
|
|
259
|
+
*
|
|
260
|
+
* Can be passed as a parameter to:
|
|
261
|
+
* - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate the gas cost of the call
|
|
262
|
+
* - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the call
|
|
263
|
+
* - [`sendCalls`](https://viem.sh/docs/actions/wallet/sendCalls): send multiple calls
|
|
264
|
+
*
|
|
265
|
+
* @example
|
|
266
|
+
* ```ts
|
|
267
|
+
* import { createClient, http, walletActions } from 'viem'
|
|
268
|
+
* import { tempoModerato } from 'viem/chains'
|
|
269
|
+
* import { Actions } from 'viem/tempo'
|
|
270
|
+
*
|
|
271
|
+
* const client = createClient({ chain: tempoModerato, transport: http() })
|
|
272
|
+
* .extend(walletActions)
|
|
273
|
+
* await client.sendTransaction({
|
|
274
|
+
* calls: [Actions.earn.createStack.call({
|
|
275
|
+
* deploymentId: '0x...',
|
|
276
|
+
* engine: '0x...',
|
|
277
|
+
* factory: '0x...',
|
|
278
|
+
* owner: '0x...',
|
|
279
|
+
* })],
|
|
280
|
+
* })
|
|
281
|
+
* ```
|
|
282
|
+
*
|
|
283
|
+
* @param args - Arguments.
|
|
284
|
+
* @returns The call.
|
|
285
|
+
*/
|
|
286
|
+
function call(args) {
|
|
287
|
+
validateDeploymentId(args.deploymentId);
|
|
288
|
+
return defineCall({
|
|
289
|
+
address: args.factory,
|
|
290
|
+
abi: Abis.earnFactory,
|
|
291
|
+
functionName: 'deploy',
|
|
292
|
+
args: [toDeployParameters(args)],
|
|
293
|
+
});
|
|
294
|
+
}
|
|
295
|
+
createStack.call = call;
|
|
296
|
+
/**
|
|
297
|
+
* Predicts the deterministic EarnShare and EarnFees addresses.
|
|
298
|
+
*
|
|
299
|
+
* @param client - Client.
|
|
300
|
+
* @param args - Stack deployment arguments.
|
|
301
|
+
* @returns The predicted EarnShare and EarnFees addresses.
|
|
302
|
+
*/
|
|
303
|
+
async function predict(client, args) {
|
|
304
|
+
validateDeploymentId(args.deploymentId);
|
|
305
|
+
const parameters = toDeployParameters(args);
|
|
306
|
+
const [earnShare, earnFees] = await Promise.all([
|
|
307
|
+
readContract(client, {
|
|
308
|
+
address: args.factory,
|
|
309
|
+
abi: Abis.earnFactory,
|
|
310
|
+
functionName: 'predictEarnShare',
|
|
311
|
+
args: [parameters],
|
|
312
|
+
}),
|
|
313
|
+
readContract(client, {
|
|
314
|
+
address: args.factory,
|
|
315
|
+
abi: Abis.earnFactory,
|
|
316
|
+
functionName: 'predictEarnFees',
|
|
317
|
+
args: [parameters],
|
|
318
|
+
}),
|
|
319
|
+
]);
|
|
320
|
+
return { earnFees, earnShare };
|
|
321
|
+
}
|
|
322
|
+
createStack.predict = predict;
|
|
323
|
+
/**
|
|
324
|
+
* Extracts the `EarnStackDeployed` event from factory logs.
|
|
325
|
+
*
|
|
326
|
+
* @param logs - The logs.
|
|
327
|
+
* @param parameters - Factory address used to filter the logs.
|
|
328
|
+
* @returns The deployment event.
|
|
329
|
+
*/
|
|
330
|
+
function extractEvent(logs, parameters) {
|
|
331
|
+
const [log] = parseEventLogs({
|
|
332
|
+
abi: Abis.earnFactory,
|
|
333
|
+
eventName: 'EarnStackDeployed',
|
|
334
|
+
logs: logs.filter((log) => isAddressEqual(log.address, parameters.factory)),
|
|
335
|
+
strict: true,
|
|
336
|
+
});
|
|
337
|
+
if (!log)
|
|
338
|
+
throw new Error('`EarnStackDeployed` event not found.');
|
|
339
|
+
return log;
|
|
340
|
+
}
|
|
341
|
+
createStack.extractEvent = extractEvent;
|
|
342
|
+
})(createStack || (createStack = {}));
|
|
343
|
+
/**
|
|
344
|
+
* Creates an Earn core stack and waits for confirmation.
|
|
345
|
+
*
|
|
346
|
+
* @experimental
|
|
347
|
+
*
|
|
348
|
+
* @example
|
|
349
|
+
* ```ts
|
|
350
|
+
* import { createClient, http } from 'viem'
|
|
351
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
352
|
+
* import { tempoModerato } from 'viem/chains'
|
|
353
|
+
* import { Actions } from 'viem/tempo'
|
|
354
|
+
*
|
|
355
|
+
* const client = createClient({
|
|
356
|
+
* account: privateKeyToAccount('0x...'),
|
|
357
|
+
* chain: tempoModerato,
|
|
358
|
+
* transport: http(),
|
|
359
|
+
* })
|
|
360
|
+
* const result = await Actions.earn.createStackSync(client, {
|
|
361
|
+
* deploymentId: '0x...',
|
|
362
|
+
* engine: '0x...',
|
|
363
|
+
* factory: '0x...',
|
|
364
|
+
* })
|
|
365
|
+
* ```
|
|
366
|
+
*
|
|
367
|
+
* @param client - Client.
|
|
368
|
+
* @param parameters - Parameters.
|
|
369
|
+
* @returns The receipt and deployed stack addresses.
|
|
370
|
+
*/
|
|
371
|
+
export async function createStackSync(client, parameters) {
|
|
372
|
+
const { factory, throwOnReceiptRevert = true } = parameters;
|
|
373
|
+
const receipt = await createStack.inner(writeContractSync, client, {
|
|
374
|
+
...parameters,
|
|
375
|
+
throwOnReceiptRevert,
|
|
376
|
+
});
|
|
377
|
+
const { args } = createStack.extractEvent(receipt.logs, { factory });
|
|
378
|
+
return { ...args, receipt };
|
|
379
|
+
}
|
|
380
|
+
/**
|
|
381
|
+
* Permanently binds an ERC-4626 engine to its EarnVault.
|
|
382
|
+
*
|
|
383
|
+
* @experimental
|
|
384
|
+
*
|
|
385
|
+
* @example
|
|
386
|
+
* ```ts
|
|
387
|
+
* import { createClient, http } from 'viem'
|
|
388
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
389
|
+
* import { tempoModerato } from 'viem/chains'
|
|
390
|
+
* import { Actions } from 'viem/tempo'
|
|
391
|
+
*
|
|
392
|
+
* const client = createClient({
|
|
393
|
+
* account: privateKeyToAccount('0x...'),
|
|
394
|
+
* chain: tempoModerato,
|
|
395
|
+
* transport: http(),
|
|
396
|
+
* })
|
|
397
|
+
* const hash = await Actions.earn.bindErc4626Engine(client, {
|
|
398
|
+
* engine: '0x...',
|
|
399
|
+
* vault: '0x...',
|
|
400
|
+
* })
|
|
401
|
+
* ```
|
|
402
|
+
*
|
|
403
|
+
* @param client - Client controlled by the final engine owner.
|
|
404
|
+
* @param parameters - Parameters.
|
|
405
|
+
* @returns The transaction hash.
|
|
406
|
+
*/
|
|
407
|
+
export async function bindErc4626Engine(client, parameters) {
|
|
408
|
+
return bindErc4626Engine.inner(writeContract, client, parameters);
|
|
409
|
+
}
|
|
410
|
+
(function (bindErc4626Engine) {
|
|
411
|
+
/** @internal */
|
|
412
|
+
async function inner(action, client, parameters) {
|
|
413
|
+
const { engine, vault, ...rest } = parameters;
|
|
414
|
+
return (await action(client, {
|
|
415
|
+
...rest,
|
|
416
|
+
...bindErc4626Engine.call({ engine, vault }),
|
|
417
|
+
}));
|
|
418
|
+
}
|
|
419
|
+
bindErc4626Engine.inner = inner;
|
|
420
|
+
/**
|
|
421
|
+
* Defines a call to `ERC4626Engine.initializeEarnVault`.
|
|
422
|
+
*
|
|
423
|
+
* Can be passed as a parameter to:
|
|
424
|
+
* - [`estimateContractGas`](https://viem.sh/docs/contract/estimateContractGas): estimate the gas cost of the call
|
|
425
|
+
* - [`simulateContract`](https://viem.sh/docs/contract/simulateContract): simulate the call
|
|
426
|
+
* - [`sendCalls`](https://viem.sh/docs/actions/wallet/sendCalls): send multiple calls
|
|
427
|
+
*
|
|
428
|
+
* @example
|
|
429
|
+
* ```ts
|
|
430
|
+
* import { createClient, http, walletActions } from 'viem'
|
|
431
|
+
* import { tempoModerato } from 'viem/chains'
|
|
432
|
+
* import { Actions } from 'viem/tempo'
|
|
433
|
+
*
|
|
434
|
+
* const client = createClient({ chain: tempoModerato, transport: http() })
|
|
435
|
+
* .extend(walletActions)
|
|
436
|
+
* await client.sendTransaction({
|
|
437
|
+
* calls: [Actions.earn.bindErc4626Engine.call({
|
|
438
|
+
* engine: '0x...',
|
|
439
|
+
* vault: '0x...',
|
|
440
|
+
* })],
|
|
441
|
+
* })
|
|
442
|
+
* ```
|
|
443
|
+
*
|
|
444
|
+
* @param args - Arguments.
|
|
445
|
+
* @returns The call.
|
|
446
|
+
*/
|
|
447
|
+
function call(args) {
|
|
448
|
+
return defineCall({
|
|
449
|
+
address: args.engine,
|
|
450
|
+
abi: Abis.erc4626Engine,
|
|
451
|
+
functionName: 'initializeEarnVault',
|
|
452
|
+
args: [args.vault],
|
|
453
|
+
});
|
|
454
|
+
}
|
|
455
|
+
bindErc4626Engine.call = call;
|
|
456
|
+
/**
|
|
457
|
+
* Extracts the `EarnVaultInitialized` event from engine logs.
|
|
458
|
+
*
|
|
459
|
+
* @param logs - The logs.
|
|
460
|
+
* @param parameters - Engine address used to filter the logs.
|
|
461
|
+
* @returns The initialization event.
|
|
462
|
+
*/
|
|
463
|
+
function extractEvent(logs, parameters) {
|
|
464
|
+
const [log] = parseEventLogs({
|
|
465
|
+
abi: Abis.erc4626Engine,
|
|
466
|
+
eventName: 'EarnVaultInitialized',
|
|
467
|
+
logs: logs.filter((log) => isAddressEqual(log.address, parameters.engine)),
|
|
468
|
+
strict: true,
|
|
469
|
+
});
|
|
470
|
+
if (!log)
|
|
471
|
+
throw new Error('`EarnVaultInitialized` event not found.');
|
|
472
|
+
return log;
|
|
473
|
+
}
|
|
474
|
+
bindErc4626Engine.extractEvent = extractEvent;
|
|
475
|
+
})(bindErc4626Engine || (bindErc4626Engine = {}));
|
|
476
|
+
/**
|
|
477
|
+
* Binds an ERC-4626 engine and waits for confirmation.
|
|
478
|
+
*
|
|
479
|
+
* @experimental
|
|
480
|
+
*
|
|
481
|
+
* @example
|
|
482
|
+
* ```ts
|
|
483
|
+
* import { createClient, http } from 'viem'
|
|
484
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
485
|
+
* import { tempoModerato } from 'viem/chains'
|
|
486
|
+
* import { Actions } from 'viem/tempo'
|
|
487
|
+
*
|
|
488
|
+
* const client = createClient({
|
|
489
|
+
* account: privateKeyToAccount('0x...'),
|
|
490
|
+
* chain: tempoModerato,
|
|
491
|
+
* transport: http(),
|
|
492
|
+
* })
|
|
493
|
+
* const result = await Actions.earn.bindErc4626EngineSync(client, {
|
|
494
|
+
* engine: '0x...',
|
|
495
|
+
* vault: '0x...',
|
|
496
|
+
* })
|
|
497
|
+
* ```
|
|
498
|
+
*
|
|
499
|
+
* @param client - Client controlled by the final engine owner.
|
|
500
|
+
* @param parameters - Parameters.
|
|
501
|
+
* @returns The receipt and bound addresses.
|
|
502
|
+
*/
|
|
503
|
+
export async function bindErc4626EngineSync(client, parameters) {
|
|
504
|
+
const { engine, throwOnReceiptRevert = true } = parameters;
|
|
505
|
+
const receipt = await bindErc4626Engine.inner(writeContractSync, client, {
|
|
506
|
+
...parameters,
|
|
507
|
+
throwOnReceiptRevert,
|
|
508
|
+
});
|
|
509
|
+
const { args } = bindErc4626Engine.extractEvent(receipt.logs, { engine });
|
|
510
|
+
return { engine, vault: args.earnVault, receipt };
|
|
511
|
+
}
|
|
512
|
+
/**
|
|
513
|
+
* Error thrown after a partially completed Earn stack deployment.
|
|
514
|
+
*
|
|
515
|
+
* @experimental
|
|
516
|
+
*/
|
|
517
|
+
export class DeployErc4626StackError extends BaseError {
|
|
518
|
+
constructor(cause, parameters) {
|
|
519
|
+
super(`ERC-4626 Earn deployment failed during ${parameters.stage}.`, {
|
|
520
|
+
cause,
|
|
521
|
+
name: 'DeployErc4626StackError',
|
|
522
|
+
});
|
|
523
|
+
Object.defineProperty(this, "receipts", {
|
|
524
|
+
enumerable: true,
|
|
525
|
+
configurable: true,
|
|
526
|
+
writable: true,
|
|
527
|
+
value: void 0
|
|
528
|
+
});
|
|
529
|
+
Object.defineProperty(this, "stage", {
|
|
530
|
+
enumerable: true,
|
|
531
|
+
configurable: true,
|
|
532
|
+
writable: true,
|
|
533
|
+
value: void 0
|
|
534
|
+
});
|
|
535
|
+
Object.defineProperty(this, "state", {
|
|
536
|
+
enumerable: true,
|
|
537
|
+
configurable: true,
|
|
538
|
+
writable: true,
|
|
539
|
+
value: void 0
|
|
540
|
+
});
|
|
541
|
+
this.receipts = parameters.receipts;
|
|
542
|
+
this.stage = parameters.stage;
|
|
543
|
+
this.state = parameters.state;
|
|
544
|
+
}
|
|
545
|
+
}
|
|
546
|
+
/**
|
|
547
|
+
* Deploys and binds a complete ERC-4626 Earn stack through sequential,
|
|
548
|
+
* resumable transactions.
|
|
549
|
+
*
|
|
550
|
+
* @experimental
|
|
551
|
+
*
|
|
552
|
+
* @example
|
|
553
|
+
* ```ts
|
|
554
|
+
* import { createClient, http } from 'viem'
|
|
555
|
+
* import { privateKeyToAccount } from 'viem/accounts'
|
|
556
|
+
* import { tempoModerato } from 'viem/chains'
|
|
557
|
+
* import { Actions } from 'viem/tempo'
|
|
558
|
+
*
|
|
559
|
+
* const client = createClient({
|
|
560
|
+
* account: privateKeyToAccount('0x...'),
|
|
561
|
+
* chain: tempoModerato,
|
|
562
|
+
* transport: http(),
|
|
563
|
+
* })
|
|
564
|
+
* const result = await Actions.earn.deployErc4626StackSync(client, {
|
|
565
|
+
* deploymentId: '0x...',
|
|
566
|
+
* factories: { earn: '0x...', erc4626Engine: '0x...' },
|
|
567
|
+
* venue: '0x...',
|
|
568
|
+
* })
|
|
569
|
+
* ```
|
|
570
|
+
*
|
|
571
|
+
* @param client - Client.
|
|
572
|
+
* @param parameters - Parameters.
|
|
573
|
+
* @returns The deployed addresses and receipts created by this run.
|
|
574
|
+
*/
|
|
575
|
+
export async function deployErc4626StackSync(client, parameters) {
|
|
576
|
+
const deployer = parameters.account ?? client.account;
|
|
577
|
+
if (!deployer)
|
|
578
|
+
throw new AccountNotFoundError();
|
|
579
|
+
const owner_ = parameters.owner ?? deployer;
|
|
580
|
+
const owner = parseAccount(owner_).address;
|
|
581
|
+
const deployerAddress = parseAccount(deployer).address;
|
|
582
|
+
const bindingAccount = (() => {
|
|
583
|
+
if (parameters.bindingAccount)
|
|
584
|
+
return parameters.bindingAccount;
|
|
585
|
+
if (typeof owner_ !== 'string')
|
|
586
|
+
return owner_;
|
|
587
|
+
if (isAddressEqual(owner, deployerAddress))
|
|
588
|
+
return deployer;
|
|
589
|
+
return undefined;
|
|
590
|
+
})();
|
|
591
|
+
if (bindingAccount &&
|
|
592
|
+
!isAddressEqual(parseAccount(bindingAccount).address, owner))
|
|
593
|
+
throw new Error('`bindingAccount` must match `owner`.');
|
|
594
|
+
validateDeploymentId(parameters.deploymentId);
|
|
595
|
+
if (parameters.resume &&
|
|
596
|
+
parameters.resume.deploymentId.toLowerCase() !==
|
|
597
|
+
parameters.deploymentId.toLowerCase())
|
|
598
|
+
throw new Error('The resumed deployment ID does not match `deploymentId`.');
|
|
599
|
+
await validateContracts(client, {
|
|
600
|
+
factories: parameters.factories,
|
|
601
|
+
venue: parameters.venue,
|
|
602
|
+
});
|
|
603
|
+
const { gas: _, keyAuthorization: __, nonce: ___, ...sharedWriteParameters } = pickWriteParameters(parameters);
|
|
604
|
+
const writeParameters = {
|
|
605
|
+
...sharedWriteParameters,
|
|
606
|
+
...pickWriteSyncParameters(parameters),
|
|
607
|
+
account: deployer,
|
|
608
|
+
throwOnReceiptRevert: true,
|
|
609
|
+
};
|
|
610
|
+
const engineArgs = {
|
|
611
|
+
deploymentId: parameters.deploymentId,
|
|
612
|
+
factory: parameters.factories.erc4626Engine,
|
|
613
|
+
name: parameters.name,
|
|
614
|
+
owner,
|
|
615
|
+
symbol: parameters.symbol,
|
|
616
|
+
venue: parameters.venue,
|
|
617
|
+
};
|
|
618
|
+
const predictedEngine = await createErc4626Engine.predict(client, engineArgs);
|
|
619
|
+
const state = {
|
|
620
|
+
deploymentId: parameters.deploymentId,
|
|
621
|
+
earnShare: parameters.resume?.earnShare,
|
|
622
|
+
engine: predictedEngine,
|
|
623
|
+
fees: parameters.resume?.fees,
|
|
624
|
+
vault: parameters.resume?.vault,
|
|
625
|
+
};
|
|
626
|
+
if (parameters.resume?.engine &&
|
|
627
|
+
!isAddressEqual(parameters.resume.engine, predictedEngine))
|
|
628
|
+
throw new Error('The resumed engine does not match the factory prediction.');
|
|
629
|
+
const engineExists = await hasCode(client, predictedEngine);
|
|
630
|
+
if (!bindingAccount) {
|
|
631
|
+
const boundVault = engineExists
|
|
632
|
+
? await readContract(client, {
|
|
633
|
+
address: predictedEngine,
|
|
634
|
+
abi: Abis.erc4626Engine,
|
|
635
|
+
functionName: 'earnVault',
|
|
636
|
+
})
|
|
637
|
+
: zeroAddress;
|
|
638
|
+
if (isAddressEqual(boundVault, zeroAddress))
|
|
639
|
+
throw new Error('`bindingAccount` is required when the final owner differs from the deployment account.');
|
|
640
|
+
}
|
|
641
|
+
const receipts = {};
|
|
642
|
+
try {
|
|
643
|
+
if (!engineExists) {
|
|
644
|
+
await simulateContract(client, {
|
|
645
|
+
...sharedWriteParameters,
|
|
646
|
+
account: deployer,
|
|
647
|
+
...createErc4626Engine.call(engineArgs),
|
|
648
|
+
});
|
|
649
|
+
const receipt = await createErc4626Engine.inner(writeContractSync, client, {
|
|
650
|
+
...writeParameters,
|
|
651
|
+
...engineArgs,
|
|
652
|
+
});
|
|
653
|
+
receipts.engine = receipt;
|
|
654
|
+
createErc4626Engine.extractEvent(receipt.logs, {
|
|
655
|
+
factory: parameters.factories.erc4626Engine,
|
|
656
|
+
});
|
|
657
|
+
}
|
|
658
|
+
await verifyEngine(client, engineArgs, predictedEngine);
|
|
659
|
+
}
|
|
660
|
+
catch (error) {
|
|
661
|
+
throw deploymentError(error, 'engine', state, receipts);
|
|
662
|
+
}
|
|
663
|
+
const stackArgs = {
|
|
664
|
+
controls: parameters.controls,
|
|
665
|
+
deploymentId: parameters.deploymentId,
|
|
666
|
+
distributor: parameters.distributor,
|
|
667
|
+
engine: predictedEngine,
|
|
668
|
+
factory: parameters.factories.earn,
|
|
669
|
+
fees: parameters.fees,
|
|
670
|
+
owner,
|
|
671
|
+
transferPolicyId: parameters.transferPolicyId,
|
|
672
|
+
};
|
|
673
|
+
try {
|
|
674
|
+
const predicted = await createStack.predict(client, stackArgs);
|
|
675
|
+
if (parameters.resume?.earnShare &&
|
|
676
|
+
!isAddressEqual(parameters.resume.earnShare, predicted.earnShare))
|
|
677
|
+
throw new Error('The resumed EarnShare does not match the factory prediction.');
|
|
678
|
+
if (parameters.resume?.fees &&
|
|
679
|
+
!isAddressEqual(parameters.resume.fees, predicted.earnFees))
|
|
680
|
+
throw new Error('The resumed EarnFees does not match the factory prediction.');
|
|
681
|
+
state.earnShare = predicted.earnShare;
|
|
682
|
+
state.fees = predicted.earnFees;
|
|
683
|
+
if (!(await hasCode(client, predicted.earnShare))) {
|
|
684
|
+
if (await hasCode(client, predicted.earnFees))
|
|
685
|
+
throw new Error('Predicted EarnFees exists without the predicted EarnShare.');
|
|
686
|
+
await simulateContract(client, {
|
|
687
|
+
...sharedWriteParameters,
|
|
688
|
+
account: deployer,
|
|
689
|
+
...createStack.call(stackArgs),
|
|
690
|
+
});
|
|
691
|
+
const receipt = await createStack.inner(writeContractSync, client, {
|
|
692
|
+
...writeParameters,
|
|
693
|
+
...stackArgs,
|
|
694
|
+
});
|
|
695
|
+
receipts.stack = receipt;
|
|
696
|
+
const { args } = createStack.extractEvent(receipt.logs, {
|
|
697
|
+
factory: parameters.factories.earn,
|
|
698
|
+
});
|
|
699
|
+
state.vault = args.earnVault;
|
|
700
|
+
if (parameters.resume?.vault &&
|
|
701
|
+
!isAddressEqual(parameters.resume.vault, args.earnVault))
|
|
702
|
+
throw new Error('The resumed EarnVault does not match the factory deployment.');
|
|
703
|
+
}
|
|
704
|
+
else {
|
|
705
|
+
if (!(await hasCode(client, predicted.earnFees)))
|
|
706
|
+
throw new Error('Predicted EarnShare exists without the predicted EarnFees.');
|
|
707
|
+
state.vault = parameters.resume?.vault;
|
|
708
|
+
if (!state.vault) {
|
|
709
|
+
const event = await findStackDeployment(client, {
|
|
710
|
+
earnShare: predicted.earnShare,
|
|
711
|
+
factory: parameters.factories.earn,
|
|
712
|
+
fromBlock: parameters.fromBlock,
|
|
713
|
+
});
|
|
714
|
+
state.vault = event.args.earnVault;
|
|
715
|
+
if (!isAddressEqual(event.args.earnFees, predicted.earnFees))
|
|
716
|
+
throw new Error('Recovered EarnFees does not match the factory prediction.');
|
|
717
|
+
}
|
|
718
|
+
}
|
|
719
|
+
if (!state.vault)
|
|
720
|
+
throw new Error('EarnVault address was not recovered.');
|
|
721
|
+
await verifyStack(client, {
|
|
722
|
+
engine: predictedEngine,
|
|
723
|
+
fees: predicted.earnFees,
|
|
724
|
+
owner,
|
|
725
|
+
share: predicted.earnShare,
|
|
726
|
+
vault: state.vault,
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
catch (error) {
|
|
730
|
+
throw deploymentError(error, 'stack', state, receipts);
|
|
731
|
+
}
|
|
732
|
+
try {
|
|
733
|
+
const vault = state.vault;
|
|
734
|
+
const boundVault = await readContract(client, {
|
|
735
|
+
address: predictedEngine,
|
|
736
|
+
abi: Abis.erc4626Engine,
|
|
737
|
+
functionName: 'earnVault',
|
|
738
|
+
});
|
|
739
|
+
if (isAddressEqual(boundVault, zeroAddress)) {
|
|
740
|
+
if (!bindingAccount)
|
|
741
|
+
throw new Error('`bindingAccount` is required when the final owner differs from the deployment account.');
|
|
742
|
+
const bindingParameters = {
|
|
743
|
+
...writeParameters,
|
|
744
|
+
account: bindingAccount,
|
|
745
|
+
engine: predictedEngine,
|
|
746
|
+
vault,
|
|
747
|
+
};
|
|
748
|
+
await simulateContract(client, {
|
|
749
|
+
...sharedWriteParameters,
|
|
750
|
+
account: bindingAccount,
|
|
751
|
+
...bindErc4626Engine.call({ engine: predictedEngine, vault }),
|
|
752
|
+
});
|
|
753
|
+
const receipt = await bindErc4626Engine.inner(writeContractSync, client, bindingParameters);
|
|
754
|
+
receipts.binding = receipt;
|
|
755
|
+
bindErc4626Engine.extractEvent(receipt.logs, { engine: predictedEngine });
|
|
756
|
+
}
|
|
757
|
+
else if (!isAddressEqual(boundVault, vault)) {
|
|
758
|
+
throw new Error(`Engine is already bound to ${boundVault}.`);
|
|
759
|
+
}
|
|
760
|
+
const verified = await readContract(client, {
|
|
761
|
+
address: predictedEngine,
|
|
762
|
+
abi: Abis.erc4626Engine,
|
|
763
|
+
functionName: 'earnVault',
|
|
764
|
+
});
|
|
765
|
+
if (!isAddressEqual(verified, vault))
|
|
766
|
+
throw new Error('Engine binding verification failed.');
|
|
767
|
+
}
|
|
768
|
+
catch (error) {
|
|
769
|
+
throw deploymentError(error, 'binding', state, receipts);
|
|
770
|
+
}
|
|
771
|
+
return {
|
|
772
|
+
deploymentId: parameters.deploymentId,
|
|
773
|
+
earnShare: state.earnShare,
|
|
774
|
+
engine: predictedEngine,
|
|
775
|
+
fees: state.fees,
|
|
776
|
+
receipts,
|
|
777
|
+
vault: state.vault,
|
|
778
|
+
};
|
|
779
|
+
}
|
|
780
|
+
function validateDeploymentId(deploymentId) {
|
|
781
|
+
if (Hex.size(deploymentId) !== 32 || BigInt(deploymentId) === 0n)
|
|
782
|
+
throw new Error('`deploymentId` must be a nonzero 32-byte hex value.');
|
|
783
|
+
}
|
|
784
|
+
function toDeployParameters(args) {
|
|
785
|
+
const fixedFees = args.fees?.fixedFees ?? [];
|
|
786
|
+
if (fixedFees.length > 4)
|
|
787
|
+
throw new Error('Earn supports at most four fixed fee recipients.');
|
|
788
|
+
const seen = new Set();
|
|
789
|
+
let totalRateBps = 0;
|
|
790
|
+
for (const fee of fixedFees) {
|
|
791
|
+
validateFeeRate(fee.rateBps, '`fixedFees[].rateBps`', false);
|
|
792
|
+
if (isAddressEqual(fee.account, zeroAddress))
|
|
793
|
+
throw new Error('Fixed fee recipients cannot be the zero address.');
|
|
794
|
+
const account = fee.account.toLowerCase();
|
|
795
|
+
if (seen.has(account))
|
|
796
|
+
throw new Error('Fixed fee recipients must be unique.');
|
|
797
|
+
seen.add(account);
|
|
798
|
+
totalRateBps += fee.rateBps;
|
|
799
|
+
}
|
|
800
|
+
if (totalRateBps > 10_000)
|
|
801
|
+
throw new Error('The total fixed fee rate cannot exceed 10,000 bps.');
|
|
802
|
+
const excess = args.fees?.excess;
|
|
803
|
+
if (excess) {
|
|
804
|
+
validateFeeRate(excess.annualTargetRateBps, '`annualTargetRateBps`', true);
|
|
805
|
+
validateFeeRate(excess.rateBps, '`excess.rateBps`', false);
|
|
806
|
+
if (isAddressEqual(excess.account, zeroAddress))
|
|
807
|
+
throw new Error('The excess fee recipient cannot be the zero address.');
|
|
808
|
+
}
|
|
809
|
+
const zeroFee = { account: zeroAddress, rateBps: 0 };
|
|
810
|
+
const distributor = args.distributor?.distributor ?? zeroAddress;
|
|
811
|
+
const updateDelay = args.distributor?.updateDelay ?? 0;
|
|
812
|
+
if (args.distributor) {
|
|
813
|
+
if (isAddressEqual(distributor, zeroAddress))
|
|
814
|
+
throw new Error('An enabled distributor cannot be the zero address.');
|
|
815
|
+
if (updateDelay <= 0)
|
|
816
|
+
throw new Error('An enabled distributor requires a positive update delay.');
|
|
817
|
+
if (fixedFees.length === 0)
|
|
818
|
+
throw new Error('An enabled distributor requires at least one fixed fee.');
|
|
819
|
+
}
|
|
820
|
+
if (!Number.isSafeInteger(updateDelay) || updateDelay > 0xffffffffff)
|
|
821
|
+
throw new Error('`updateDelay` must fit into uint40 seconds.');
|
|
822
|
+
const transferPolicyId = args.transferPolicyId ?? 0n;
|
|
823
|
+
if (transferPolicyId < 0n || transferPolicyId > maxUint64)
|
|
824
|
+
throw new Error('`transferPolicyId` must fit into uint64.');
|
|
825
|
+
const maxManagedAssets = args.controls?.maxManagedAssets ?? 0n;
|
|
826
|
+
if (maxManagedAssets < 0n || maxManagedAssets > maxUint256)
|
|
827
|
+
throw new Error('`maxManagedAssets` must fit into uint256.');
|
|
828
|
+
return {
|
|
829
|
+
controls: {
|
|
830
|
+
asyncJanitor: args.controls?.asyncJanitor ?? zeroAddress,
|
|
831
|
+
emergencyGuardian: args.controls?.emergencyGuardian ?? zeroAddress,
|
|
832
|
+
maxManagedAssets,
|
|
833
|
+
migrationMode: (args.controls?.migrationMode ?? 'userOnly') === 'userOnly' ? 0 : 1,
|
|
834
|
+
},
|
|
835
|
+
deploymentId: args.deploymentId,
|
|
836
|
+
distributorConfig: { distributor, updateDelay },
|
|
837
|
+
engine: args.engine,
|
|
838
|
+
fees: {
|
|
839
|
+
excess: excess
|
|
840
|
+
? {
|
|
841
|
+
account: excess.account,
|
|
842
|
+
annualTargetRateBps: excess.annualTargetRateBps,
|
|
843
|
+
enabled: true,
|
|
844
|
+
excessFeeRateBps: excess.rateBps,
|
|
845
|
+
}
|
|
846
|
+
: {
|
|
847
|
+
account: zeroAddress,
|
|
848
|
+
annualTargetRateBps: 0,
|
|
849
|
+
enabled: false,
|
|
850
|
+
excessFeeRateBps: 0,
|
|
851
|
+
},
|
|
852
|
+
fixedFeeCount: fixedFees.length,
|
|
853
|
+
fixedFees: [
|
|
854
|
+
fixedFees[0] ?? zeroFee,
|
|
855
|
+
fixedFees[1] ?? zeroFee,
|
|
856
|
+
fixedFees[2] ?? zeroFee,
|
|
857
|
+
fixedFees[3] ?? zeroFee,
|
|
858
|
+
],
|
|
859
|
+
},
|
|
860
|
+
owner: args.owner,
|
|
861
|
+
transferPolicyId,
|
|
862
|
+
};
|
|
863
|
+
}
|
|
864
|
+
function validateFeeRate(rate, name, allowZero) {
|
|
865
|
+
if (!Number.isInteger(rate) || rate < (allowZero ? 0 : 1) || rate > 10_000)
|
|
866
|
+
throw new Error(`${name} must be an integer between ${allowZero ? 0 : 1} and 10,000.`);
|
|
867
|
+
}
|
|
868
|
+
async function hasCode(client, address) {
|
|
869
|
+
const code = await getCode(client, { address });
|
|
870
|
+
return code !== undefined && code !== '0x';
|
|
871
|
+
}
|
|
872
|
+
async function validateContracts(client, parameters) {
|
|
873
|
+
const [engineFactory, earnFactory, venue] = await Promise.all([
|
|
874
|
+
hasCode(client, parameters.factories.erc4626Engine),
|
|
875
|
+
hasCode(client, parameters.factories.earn),
|
|
876
|
+
hasCode(client, parameters.venue),
|
|
877
|
+
]);
|
|
878
|
+
if (!engineFactory)
|
|
879
|
+
throw new Error('ERC4626EngineFactory has no code.');
|
|
880
|
+
if (!earnFactory)
|
|
881
|
+
throw new Error('EarnFactory has no code.');
|
|
882
|
+
if (!venue)
|
|
883
|
+
throw new Error('ERC-4626 venue has no code.');
|
|
884
|
+
const tip20Factory = await readContract(client, {
|
|
885
|
+
address: parameters.factories.earn,
|
|
886
|
+
abi: Abis.earnFactory,
|
|
887
|
+
functionName: 'tip20Factory',
|
|
888
|
+
});
|
|
889
|
+
if (!isAddressEqual(tip20Factory, Addresses.tip20Factory))
|
|
890
|
+
throw new Error('EarnFactory uses an unexpected TIP-20 factory.');
|
|
891
|
+
}
|
|
892
|
+
async function verifyEngine(client, args, engine) {
|
|
893
|
+
const [venue, owner] = await Promise.all([
|
|
894
|
+
readContract(client, {
|
|
895
|
+
address: engine,
|
|
896
|
+
abi: Abis.erc4626Engine,
|
|
897
|
+
functionName: 'vault',
|
|
898
|
+
}),
|
|
899
|
+
readContract(client, {
|
|
900
|
+
address: engine,
|
|
901
|
+
abi: Abis.erc4626Engine,
|
|
902
|
+
functionName: 'owner',
|
|
903
|
+
}),
|
|
904
|
+
]);
|
|
905
|
+
if (!isAddressEqual(venue, args.venue))
|
|
906
|
+
throw new Error('Engine venue verification failed.');
|
|
907
|
+
if (!isAddressEqual(owner, args.owner))
|
|
908
|
+
throw new Error('Engine owner verification failed.');
|
|
909
|
+
}
|
|
910
|
+
async function verifyStack(client, parameters) {
|
|
911
|
+
const [engine, fees, operator, share] = await Promise.all([
|
|
912
|
+
readContract(client, {
|
|
913
|
+
address: parameters.vault,
|
|
914
|
+
abi: Abis.earnVault,
|
|
915
|
+
functionName: 'engine',
|
|
916
|
+
}),
|
|
917
|
+
readContract(client, {
|
|
918
|
+
address: parameters.vault,
|
|
919
|
+
abi: Abis.earnVault,
|
|
920
|
+
functionName: 'earnFees',
|
|
921
|
+
}),
|
|
922
|
+
readContract(client, {
|
|
923
|
+
address: parameters.vault,
|
|
924
|
+
abi: Abis.earnVault,
|
|
925
|
+
functionName: 'operator',
|
|
926
|
+
}),
|
|
927
|
+
readContract(client, {
|
|
928
|
+
address: parameters.vault,
|
|
929
|
+
abi: Abis.earnVault,
|
|
930
|
+
functionName: 'earnShare',
|
|
931
|
+
}),
|
|
932
|
+
]);
|
|
933
|
+
if (!isAddressEqual(engine, parameters.engine))
|
|
934
|
+
throw new Error('EarnVault engine verification failed.');
|
|
935
|
+
if (!isAddressEqual(fees, parameters.fees))
|
|
936
|
+
throw new Error('EarnVault fees verification failed.');
|
|
937
|
+
if (!isAddressEqual(operator, parameters.owner))
|
|
938
|
+
throw new Error('EarnVault operator verification failed.');
|
|
939
|
+
if (!isAddressEqual(share, parameters.share))
|
|
940
|
+
throw new Error('EarnVault share verification failed.');
|
|
941
|
+
}
|
|
942
|
+
async function findStackDeployment(client, parameters) {
|
|
943
|
+
const event = getAbiItem({
|
|
944
|
+
abi: Abis.earnFactory,
|
|
945
|
+
name: 'EarnStackDeployed',
|
|
946
|
+
});
|
|
947
|
+
const logs = await getLogs(client, {
|
|
948
|
+
address: parameters.factory,
|
|
949
|
+
args: { earnShare: parameters.earnShare },
|
|
950
|
+
event,
|
|
951
|
+
fromBlock: parameters.fromBlock ?? 0n,
|
|
952
|
+
strict: true,
|
|
953
|
+
toBlock: 'latest',
|
|
954
|
+
});
|
|
955
|
+
const log = logs.at(-1);
|
|
956
|
+
if (!log)
|
|
957
|
+
throw new Error('Prior `EarnStackDeployed` event not found.');
|
|
958
|
+
return log;
|
|
959
|
+
}
|
|
960
|
+
function deploymentError(error, stage, state, receipts) {
|
|
961
|
+
if (error instanceof DeployErc4626StackError)
|
|
962
|
+
return error;
|
|
963
|
+
if (error instanceof TransactionReceiptRevertedError)
|
|
964
|
+
receipts[stage] = error.receipt;
|
|
965
|
+
return new DeployErc4626StackError(error instanceof Error ? error : new Error(String(error)), { receipts: { ...receipts }, stage, state: { ...state } });
|
|
966
|
+
}
|
|
967
|
+
//# sourceMappingURL=deployment.js.map
|