@aztec/bot 0.0.1-commit.b6e433891 → 0.0.1-commit.b9865e97
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/dest/amm_bot.d.ts +2 -2
- package/dest/amm_bot.d.ts.map +1 -1
- package/dest/amm_bot.js +1 -1
- package/dest/base_bot.d.ts +4 -4
- package/dest/base_bot.d.ts.map +1 -1
- package/dest/base_bot.js +12 -22
- package/dest/bot.d.ts +1 -1
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +1 -5
- package/dest/config.d.ts +30 -81
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +2 -2
- package/dest/cross_chain_bot.d.ts +2 -2
- package/dest/cross_chain_bot.d.ts.map +1 -1
- package/dest/cross_chain_bot.js +7 -4
- package/dest/factory.d.ts +6 -1
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +224 -146
- package/dest/interface.d.ts +2 -6
- package/dest/interface.d.ts.map +1 -1
- package/dest/interface.js +30 -7
- package/package.json +16 -16
- package/src/amm_bot.ts +2 -2
- package/src/base_bot.ts +10 -17
- package/src/bot.ts +1 -4
- package/src/config.ts +4 -4
- package/src/cross_chain_bot.ts +6 -5
- package/src/factory.ts +218 -88
- package/src/interface.ts +7 -7
package/src/factory.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { getInitialTestAccountsData } from '@aztec/accounts/testing';
|
|
2
|
+
import { NO_FROM } from '@aztec/aztec.js/account';
|
|
2
3
|
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
3
4
|
import {
|
|
4
5
|
BatchCall,
|
|
@@ -15,6 +16,8 @@ import { deriveKeys } from '@aztec/aztec.js/keys';
|
|
|
15
16
|
import { createLogger } from '@aztec/aztec.js/log';
|
|
16
17
|
import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
17
18
|
import { waitForTx } from '@aztec/aztec.js/node';
|
|
19
|
+
import { getFeeJuiceBalance } from '@aztec/aztec.js/utils';
|
|
20
|
+
import { ContractInitializationStatus } from '@aztec/aztec.js/wallet';
|
|
18
21
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
19
22
|
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
20
23
|
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
@@ -27,7 +30,7 @@ import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
|
27
30
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
28
31
|
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
|
|
29
32
|
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
30
|
-
import { GasFees, GasSettings } from '@aztec/stdlib/gas';
|
|
33
|
+
import { GasFees, GasSettings, ManaUsageEstimate } from '@aztec/stdlib/gas';
|
|
31
34
|
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
32
35
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
33
36
|
import { EmbeddedWallet } from '@aztec/wallets/embedded';
|
|
@@ -39,6 +42,8 @@ import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils
|
|
|
39
42
|
|
|
40
43
|
const MINT_BALANCE = 1e12;
|
|
41
44
|
const MIN_BALANCE = 1e3;
|
|
45
|
+
const FEE_JUICE_TOP_UP_THRESHOLD = 100n * 10n ** 18n;
|
|
46
|
+
const FEE_JUICE_TOP_UP_TARGET = 10_000n * 10n ** 18n;
|
|
42
47
|
|
|
43
48
|
export class BotFactory {
|
|
44
49
|
private log = createLogger('bot');
|
|
@@ -68,7 +73,8 @@ export class BotFactory {
|
|
|
68
73
|
}> {
|
|
69
74
|
const defaultAccountAddress = await this.setupAccount();
|
|
70
75
|
const recipient = (await this.wallet.createSchnorrAccount(Fr.random(), Fr.random())).address;
|
|
71
|
-
const token = await this.
|
|
76
|
+
const token = await this.setupTokenWithOptionalEarlyRefuel(defaultAccountAddress);
|
|
77
|
+
await this.ensureFeeJuiceBalance(defaultAccountAddress, token);
|
|
72
78
|
await this.mintTokens(token, defaultAccountAddress);
|
|
73
79
|
return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
|
|
74
80
|
}
|
|
@@ -82,7 +88,13 @@ export class BotFactory {
|
|
|
82
88
|
node: AztecNode;
|
|
83
89
|
}> {
|
|
84
90
|
const defaultAccountAddress = await this.setupAccount();
|
|
85
|
-
const token0 = await this.
|
|
91
|
+
const token0 = await this.setupTokenContractWithOptionalEarlyRefuel(
|
|
92
|
+
defaultAccountAddress,
|
|
93
|
+
this.config.tokenSalt,
|
|
94
|
+
'BotToken0',
|
|
95
|
+
'BOT0',
|
|
96
|
+
);
|
|
97
|
+
await this.ensureFeeJuiceBalance(defaultAccountAddress, token0);
|
|
86
98
|
const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
|
|
87
99
|
const liquidityToken = await this.setupTokenContract(
|
|
88
100
|
defaultAccountAddress,
|
|
@@ -177,12 +189,8 @@ export class BotFactory {
|
|
|
177
189
|
}
|
|
178
190
|
|
|
179
191
|
private async setupTestContract(deployer: AztecAddress): Promise<TestContract> {
|
|
180
|
-
const deployOpts: DeployOptions = {
|
|
181
|
-
|
|
182
|
-
contractAddressSalt: this.config.tokenSalt,
|
|
183
|
-
universalDeploy: true,
|
|
184
|
-
};
|
|
185
|
-
const deploy = TestContract.deploy(this.wallet);
|
|
192
|
+
const deployOpts: DeployOptions = { from: deployer };
|
|
193
|
+
const deploy = TestContract.deploy(this.wallet, { salt: this.config.tokenSalt, universalDeploy: true });
|
|
186
194
|
const instance = await this.registerOrDeployContract('TestContract', deploy, deployOpts);
|
|
187
195
|
return TestContract.at(instance.address, this.wallet);
|
|
188
196
|
}
|
|
@@ -207,7 +215,7 @@ export class BotFactory {
|
|
|
207
215
|
const signingKey = deriveSigningKey(secret);
|
|
208
216
|
const accountManager = await this.wallet.createSchnorrAccount(secret, salt, signingKey);
|
|
209
217
|
const metadata = await this.wallet.getContractMetadata(accountManager.address);
|
|
210
|
-
if (metadata.
|
|
218
|
+
if (metadata.initializationStatus === ContractInitializationStatus.INITIALIZED) {
|
|
211
219
|
this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
|
|
212
220
|
const timer = new Timer();
|
|
213
221
|
const address = accountManager.address;
|
|
@@ -222,21 +230,14 @@ export class BotFactory {
|
|
|
222
230
|
|
|
223
231
|
const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
|
|
224
232
|
const deployMethod = await accountManager.getDeployMethod();
|
|
225
|
-
const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
|
|
226
|
-
|
|
227
|
-
const { estimatedGas } = await deployMethod.simulate({
|
|
228
|
-
from: AztecAddress.ZERO,
|
|
229
|
-
fee: { estimateGas: true, paymentMethod },
|
|
230
|
-
});
|
|
231
|
-
const gasSettings = GasSettings.from({ ...estimatedGas!, maxFeesPerGas, maxPriorityFeesPerGas: GasFees.empty() });
|
|
232
233
|
|
|
233
234
|
await this.withNoMinTxsPerBlock(async () => {
|
|
234
235
|
const { txHash } = await deployMethod.send({
|
|
235
|
-
from:
|
|
236
|
-
fee: {
|
|
236
|
+
from: NO_FROM,
|
|
237
|
+
fee: { paymentMethod },
|
|
237
238
|
wait: NO_WAIT,
|
|
238
239
|
});
|
|
239
|
-
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}
|
|
240
|
+
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
|
|
240
241
|
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
241
242
|
});
|
|
242
243
|
this.log.info(`Account deployed at ${address}`);
|
|
@@ -258,35 +259,95 @@ export class BotFactory {
|
|
|
258
259
|
return accountManager.address;
|
|
259
260
|
}
|
|
260
261
|
|
|
262
|
+
/**
|
|
263
|
+
* Setup token and refuel first: if the token already exists (restart scenario),
|
|
264
|
+
* run ensureFeeJuiceBalance before any step that might need fee juice. When deploying,
|
|
265
|
+
* use a bridge claim if balance is below threshold.
|
|
266
|
+
*/
|
|
267
|
+
private async setupTokenWithOptionalEarlyRefuel(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
268
|
+
const token = await this.getTokenInstance(sender);
|
|
269
|
+
const address = token.address;
|
|
270
|
+
const metadata = await this.wallet.getContractMetadata(address);
|
|
271
|
+
if (metadata.isContractPublished) {
|
|
272
|
+
this.log.info(`Token at ${address.toString()} already deployed, refueling before setup`);
|
|
273
|
+
await this.ensureFeeJuiceBalance(sender, token);
|
|
274
|
+
}
|
|
275
|
+
return this.setupToken(sender);
|
|
276
|
+
}
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Setup token0 for AMM with refuel-first behaviour when token already exists.
|
|
280
|
+
*/
|
|
281
|
+
private async setupTokenContractWithOptionalEarlyRefuel(
|
|
282
|
+
deployer: AztecAddress,
|
|
283
|
+
salt: Fr,
|
|
284
|
+
name: string,
|
|
285
|
+
ticker: string,
|
|
286
|
+
decimals = 18,
|
|
287
|
+
): Promise<TokenContract> {
|
|
288
|
+
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals, { salt, universalDeploy: true });
|
|
289
|
+
const instance = await deploy.getInstance();
|
|
290
|
+
const metadata = await this.wallet.getContractMetadata(instance.address);
|
|
291
|
+
if (metadata.isContractPublished) {
|
|
292
|
+
this.log.info(`Token ${name} at ${instance.address.toString()} already deployed, refueling before setup`);
|
|
293
|
+
const token = TokenContract.at(instance.address, this.wallet);
|
|
294
|
+
await this.ensureFeeJuiceBalance(deployer, token);
|
|
295
|
+
}
|
|
296
|
+
return this.setupTokenContract(deployer, salt, name, ticker, decimals);
|
|
297
|
+
}
|
|
298
|
+
|
|
299
|
+
private async getTokenInstance(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
300
|
+
const salt = this.config.tokenSalt;
|
|
301
|
+
if (this.config.contract === SupportedTokenContracts.TokenContract) {
|
|
302
|
+
const deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18, { salt, universalDeploy: true });
|
|
303
|
+
const instance = await deploy.getInstance();
|
|
304
|
+
return TokenContract.at(instance.address, this.wallet);
|
|
305
|
+
}
|
|
306
|
+
if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
|
|
307
|
+
const tokenSecretKey = Fr.random();
|
|
308
|
+
const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
|
|
309
|
+
const deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender, {
|
|
310
|
+
salt,
|
|
311
|
+
universalDeploy: true,
|
|
312
|
+
publicKeys: tokenPublicKeys,
|
|
313
|
+
});
|
|
314
|
+
const instance = await deploy.getInstance();
|
|
315
|
+
return PrivateTokenContract.at(instance.address, this.wallet);
|
|
316
|
+
}
|
|
317
|
+
throw new Error(`Unsupported token contract type: ${this.config.contract}`);
|
|
318
|
+
}
|
|
319
|
+
|
|
261
320
|
/**
|
|
262
321
|
* Checks if the token contract is deployed and deploys it if necessary.
|
|
263
|
-
*
|
|
264
|
-
* @
|
|
322
|
+
* Uses a bridge claim for deploy when balance is below threshold to avoid failing before refuel.
|
|
323
|
+
* @param sender - Aztec address to deploy the token contract from.
|
|
324
|
+
* @param existingToken - Optional token instance when called from setupTokenWithOptionalEarlyRefuel.
|
|
325
|
+
* @returns The TokenContract or PrivateTokenContract instance.
|
|
265
326
|
*/
|
|
266
327
|
private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
267
328
|
let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
|
|
268
|
-
|
|
269
|
-
const deployOpts: DeployOptions = {
|
|
270
|
-
from: sender,
|
|
271
|
-
contractAddressSalt: this.config.tokenSalt,
|
|
272
|
-
universalDeploy: true,
|
|
273
|
-
};
|
|
329
|
+
const salt = this.config.tokenSalt;
|
|
330
|
+
const deployOpts: DeployOptions = { from: sender };
|
|
274
331
|
let token: TokenContract | PrivateTokenContract;
|
|
275
332
|
if (this.config.contract === SupportedTokenContracts.TokenContract) {
|
|
276
|
-
deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
|
|
277
|
-
|
|
278
|
-
token = TokenContract.at(
|
|
333
|
+
deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18, { salt, universalDeploy: true });
|
|
334
|
+
const instance = await deploy.getInstance();
|
|
335
|
+
token = TokenContract.at(instance.address, this.wallet);
|
|
279
336
|
} else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
|
|
280
337
|
// Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
|
|
281
338
|
const tokenSecretKey = Fr.random();
|
|
282
339
|
const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
|
|
283
|
-
deploy = PrivateTokenContract.
|
|
340
|
+
deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender, {
|
|
341
|
+
salt,
|
|
342
|
+
universalDeploy: true,
|
|
343
|
+
publicKeys: tokenPublicKeys,
|
|
344
|
+
});
|
|
284
345
|
deployOpts.skipInstancePublication = true;
|
|
285
346
|
deployOpts.skipClassPublication = true;
|
|
286
347
|
deployOpts.skipInitialization = false;
|
|
287
348
|
|
|
288
349
|
// Register the contract with the secret key before deployment
|
|
289
|
-
tokenInstance = await deploy.getInstance(
|
|
350
|
+
const tokenInstance = await deploy.getInstance();
|
|
290
351
|
token = PrivateTokenContract.at(tokenInstance.address, this.wallet);
|
|
291
352
|
await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
|
|
292
353
|
// The contract constructor initializes private storage vars that need the contract's own nullifier key.
|
|
@@ -295,21 +356,7 @@ export class BotFactory {
|
|
|
295
356
|
throw new Error(`Unsupported token contract type: ${this.config.contract}`);
|
|
296
357
|
}
|
|
297
358
|
|
|
298
|
-
|
|
299
|
-
const metadata = await this.wallet.getContractMetadata(address);
|
|
300
|
-
if (metadata.isContractPublished) {
|
|
301
|
-
this.log.info(`Token at ${address.toString()} already deployed`);
|
|
302
|
-
await deploy.register();
|
|
303
|
-
} else {
|
|
304
|
-
this.log.info(`Deploying token contract at ${address.toString()}`);
|
|
305
|
-
const { estimatedGas } = await deploy.simulate({ ...deployOpts, fee: { estimateGas: true } });
|
|
306
|
-
const { txHash } = await deploy.send({ ...deployOpts, fee: { gasSettings: estimatedGas }, wait: NO_WAIT });
|
|
307
|
-
this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`, { estimatedGas });
|
|
308
|
-
await this.withNoMinTxsPerBlock(async () => {
|
|
309
|
-
await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
310
|
-
return token;
|
|
311
|
-
});
|
|
312
|
-
}
|
|
359
|
+
await this.registerOrDeployContract('token', deploy, deployOpts);
|
|
313
360
|
return token;
|
|
314
361
|
}
|
|
315
362
|
|
|
@@ -320,43 +367,39 @@ export class BotFactory {
|
|
|
320
367
|
*/
|
|
321
368
|
private async setupTokenContract(
|
|
322
369
|
deployer: AztecAddress,
|
|
323
|
-
|
|
370
|
+
salt: Fr,
|
|
324
371
|
name: string,
|
|
325
372
|
ticker: string,
|
|
326
373
|
decimals = 18,
|
|
327
374
|
): Promise<TokenContract> {
|
|
328
|
-
const deployOpts: DeployOptions = { from: deployer
|
|
329
|
-
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
|
|
375
|
+
const deployOpts: DeployOptions = { from: deployer };
|
|
376
|
+
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals, { salt, universalDeploy: true });
|
|
330
377
|
const instance = await this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
|
|
331
378
|
return TokenContract.at(instance.address, this.wallet);
|
|
332
379
|
}
|
|
333
380
|
|
|
334
381
|
private async setupAmmContract(
|
|
335
382
|
deployer: AztecAddress,
|
|
336
|
-
|
|
383
|
+
salt: Fr,
|
|
337
384
|
token0: TokenContract,
|
|
338
385
|
token1: TokenContract,
|
|
339
386
|
lpToken: TokenContract,
|
|
340
387
|
): Promise<AMMContract> {
|
|
341
|
-
const deployOpts: DeployOptions = { from: deployer
|
|
342
|
-
const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address
|
|
388
|
+
const deployOpts: DeployOptions = { from: deployer };
|
|
389
|
+
const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address, {
|
|
390
|
+
salt,
|
|
391
|
+
universalDeploy: true,
|
|
392
|
+
});
|
|
343
393
|
const instance = await this.registerOrDeployContract('AMM', deploy, deployOpts);
|
|
344
394
|
const amm = AMMContract.at(instance.address, this.wallet);
|
|
345
395
|
|
|
346
396
|
this.log.info(`AMM deployed at ${amm.address}`);
|
|
347
397
|
const setMinterInteraction = lpToken.methods.set_minter(amm.address, true);
|
|
348
|
-
const { estimatedGas: setMinterGas } = await setMinterInteraction.simulate({
|
|
349
|
-
from: deployer,
|
|
350
|
-
fee: { estimateGas: true },
|
|
351
|
-
});
|
|
352
398
|
const { receipt: minterReceipt } = await setMinterInteraction.send({
|
|
353
399
|
from: deployer,
|
|
354
|
-
fee: { gasSettings: setMinterGas },
|
|
355
400
|
wait: { timeout: this.config.txMinedWaitSeconds },
|
|
356
401
|
});
|
|
357
|
-
this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}
|
|
358
|
-
estimatedGas: setMinterGas,
|
|
359
|
-
});
|
|
402
|
+
this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`);
|
|
360
403
|
this.log.info(`Liquidity token initialized`);
|
|
361
404
|
|
|
362
405
|
return amm;
|
|
@@ -428,17 +471,12 @@ export class BotFactory {
|
|
|
428
471
|
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
429
472
|
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
430
473
|
]);
|
|
431
|
-
const { estimatedGas: mintGas } = await mintBatch.simulate({
|
|
432
|
-
from: liquidityProvider,
|
|
433
|
-
fee: { estimateGas: true },
|
|
434
|
-
});
|
|
435
474
|
const { receipt: mintReceipt } = await mintBatch.send({
|
|
436
475
|
from: liquidityProvider,
|
|
437
|
-
fee: { gasSettings: mintGas },
|
|
438
476
|
wait: { timeout: this.config.txMinedWaitSeconds },
|
|
439
477
|
});
|
|
440
478
|
|
|
441
|
-
this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}
|
|
479
|
+
this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
|
|
442
480
|
|
|
443
481
|
const addLiquidityInteraction = amm.methods.add_liquidity(
|
|
444
482
|
amount0Max,
|
|
@@ -447,21 +485,13 @@ export class BotFactory {
|
|
|
447
485
|
amount1Min,
|
|
448
486
|
authwitNonce,
|
|
449
487
|
);
|
|
450
|
-
const { estimatedGas: addLiquidityGas } = await addLiquidityInteraction.simulate({
|
|
451
|
-
from: liquidityProvider,
|
|
452
|
-
fee: { estimateGas: true },
|
|
453
|
-
authWitnesses: [token0Authwit, token1Authwit],
|
|
454
|
-
});
|
|
455
488
|
const { receipt: addLiquidityReceipt } = await addLiquidityInteraction.send({
|
|
456
489
|
from: liquidityProvider,
|
|
457
|
-
fee: { gasSettings: addLiquidityGas },
|
|
458
490
|
authWitnesses: [token0Authwit, token1Authwit],
|
|
459
491
|
wait: { timeout: this.config.txMinedWaitSeconds },
|
|
460
492
|
});
|
|
461
493
|
|
|
462
|
-
this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}
|
|
463
|
-
estimatedGas: addLiquidityGas,
|
|
464
|
-
});
|
|
494
|
+
this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
|
|
465
495
|
this.log.info(`Liquidity added`);
|
|
466
496
|
|
|
467
497
|
const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
|
|
@@ -475,20 +505,49 @@ export class BotFactory {
|
|
|
475
505
|
deploy: DeployMethod<T>,
|
|
476
506
|
deployOpts: DeployOptions,
|
|
477
507
|
): Promise<ContractInstanceWithAddress> {
|
|
478
|
-
const instance = await deploy.getInstance(
|
|
508
|
+
const instance = await deploy.getInstance();
|
|
479
509
|
const address = instance.address;
|
|
480
510
|
const metadata = await this.wallet.getContractMetadata(address);
|
|
481
511
|
if (metadata.isContractPublished) {
|
|
482
512
|
this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
|
|
483
513
|
await deploy.register();
|
|
484
514
|
} else {
|
|
485
|
-
const
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
515
|
+
const sender = deployOpts.from === NO_FROM ? undefined : deployOpts.from;
|
|
516
|
+
const balance = sender ? await getFeeJuiceBalance(sender, this.aztecNode) : 0n;
|
|
517
|
+
const useClaim =
|
|
518
|
+
sender &&
|
|
519
|
+
balance < FEE_JUICE_TOP_UP_THRESHOLD &&
|
|
520
|
+
this.config.feePaymentMethod === 'fee_juice' &&
|
|
521
|
+
!!this.config.l1RpcUrls?.length;
|
|
522
|
+
const mnemonicOrPrivateKey = this.config.l1PrivateKey?.getValue() ?? this.config.l1Mnemonic?.getValue();
|
|
523
|
+
|
|
524
|
+
if (useClaim && mnemonicOrPrivateKey) {
|
|
525
|
+
const claim = await this.getOrCreateBridgeClaim(sender!);
|
|
526
|
+
const paymentMethod = new FeeJuicePaymentMethodWithClaim(sender!, claim);
|
|
527
|
+
const { estimatedGas } = await deploy.simulate({ ...deployOpts, fee: { estimateGas: true, paymentMethod } });
|
|
528
|
+
const maxFeesPerGas = (await this.getMinFees()).mul(1 + this.config.minFeePadding);
|
|
529
|
+
const gasSettings = GasSettings.from({
|
|
530
|
+
...estimatedGas!,
|
|
531
|
+
maxFeesPerGas,
|
|
532
|
+
maxPriorityFeesPerGas: GasFees.empty(),
|
|
533
|
+
});
|
|
534
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
535
|
+
const { txHash } = await deploy.send({ ...deployOpts, fee: { gasSettings, paymentMethod }, wait: NO_WAIT });
|
|
536
|
+
this.log.info(
|
|
537
|
+
`Sent contract ${name} deploy tx ${txHash.toString()} (using bridge claim, balance was ${balance})`,
|
|
538
|
+
);
|
|
539
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
540
|
+
});
|
|
541
|
+
await this.store.deleteBridgeClaim(sender!);
|
|
542
|
+
} else {
|
|
543
|
+
const { estimatedGas } = await deploy.simulate({ ...deployOpts, fee: { estimateGas: true } });
|
|
544
|
+
this.log.info(`Deploying contract ${name} at ${address.toString()}`, { estimatedGas });
|
|
545
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
546
|
+
const { txHash } = await deploy.send({ ...deployOpts, fee: { gasSettings: estimatedGas }, wait: NO_WAIT });
|
|
547
|
+
this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
|
|
548
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
549
|
+
});
|
|
550
|
+
}
|
|
492
551
|
}
|
|
493
552
|
return instance;
|
|
494
553
|
}
|
|
@@ -497,6 +556,66 @@ export class BotFactory {
|
|
|
497
556
|
* Mints private and public tokens for the sender if their balance is below the minimum.
|
|
498
557
|
* @param token - Token contract.
|
|
499
558
|
*/
|
|
559
|
+
/**
|
|
560
|
+
* Ensures the account has sufficient fee juice by bridging from L1 if balance is below threshold.
|
|
561
|
+
* Bridges repeatedly until balance reaches the target (10k FJ).
|
|
562
|
+
* Used on startup/restart to top up when the account has run out after previous runs.
|
|
563
|
+
*/
|
|
564
|
+
private async ensureFeeJuiceBalance(
|
|
565
|
+
account: AztecAddress,
|
|
566
|
+
token: TokenContract | PrivateTokenContract,
|
|
567
|
+
): Promise<void> {
|
|
568
|
+
const { feePaymentMethod, l1RpcUrls } = this.config;
|
|
569
|
+
if (feePaymentMethod !== 'fee_juice' || !l1RpcUrls?.length) {
|
|
570
|
+
return;
|
|
571
|
+
}
|
|
572
|
+
const mnemonicOrPrivateKey = this.config.l1PrivateKey?.getValue() ?? this.config.l1Mnemonic?.getValue();
|
|
573
|
+
if (!mnemonicOrPrivateKey) {
|
|
574
|
+
return;
|
|
575
|
+
}
|
|
576
|
+
|
|
577
|
+
let balance = await getFeeJuiceBalance(account, this.aztecNode);
|
|
578
|
+
if (balance >= FEE_JUICE_TOP_UP_THRESHOLD) {
|
|
579
|
+
this.log.info(`Fee juice balance ${balance} above threshold ${FEE_JUICE_TOP_UP_THRESHOLD}, skipping top-up`);
|
|
580
|
+
return;
|
|
581
|
+
}
|
|
582
|
+
|
|
583
|
+
this.log.info(
|
|
584
|
+
`Fee juice balance ${balance} below threshold ${FEE_JUICE_TOP_UP_THRESHOLD}, bridging from L1 until ${FEE_JUICE_TOP_UP_TARGET}`,
|
|
585
|
+
);
|
|
586
|
+
const maxFeesPerGas = (await this.getMinFees()).mul(1 + this.config.minFeePadding);
|
|
587
|
+
const minimalInteraction = isStandardTokenContract(token)
|
|
588
|
+
? token.methods.transfer_in_public(account, account, 0n, 0)
|
|
589
|
+
: token.methods.transfer(0n, account, account);
|
|
590
|
+
|
|
591
|
+
while (balance < FEE_JUICE_TOP_UP_TARGET) {
|
|
592
|
+
const claim = await this.bridgeL1FeeJuice(account);
|
|
593
|
+
const paymentMethod = new FeeJuicePaymentMethodWithClaim(account, claim);
|
|
594
|
+
const { estimatedGas } = await minimalInteraction.simulate({
|
|
595
|
+
from: account,
|
|
596
|
+
fee: { estimateGas: true, paymentMethod },
|
|
597
|
+
});
|
|
598
|
+
const gasSettings = GasSettings.from({
|
|
599
|
+
...estimatedGas!,
|
|
600
|
+
maxFeesPerGas,
|
|
601
|
+
maxPriorityFeesPerGas: GasFees.empty(),
|
|
602
|
+
});
|
|
603
|
+
|
|
604
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
605
|
+
const { txHash } = await minimalInteraction.send({
|
|
606
|
+
from: account,
|
|
607
|
+
fee: { gasSettings, paymentMethod },
|
|
608
|
+
wait: NO_WAIT,
|
|
609
|
+
});
|
|
610
|
+
this.log.info(`Sent fee juice top-up tx ${txHash.toString()}`);
|
|
611
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
612
|
+
});
|
|
613
|
+
balance = await getFeeJuiceBalance(account, this.aztecNode);
|
|
614
|
+
this.log.info(`Fee juice balance after top-up: ${balance}`);
|
|
615
|
+
}
|
|
616
|
+
this.log.info(`Fee juice top-up complete for ${account.toString()}`);
|
|
617
|
+
}
|
|
618
|
+
|
|
500
619
|
private async mintTokens(token: TokenContract | PrivateTokenContract, minter: AztecAddress) {
|
|
501
620
|
const isStandardToken = isStandardTokenContract(token);
|
|
502
621
|
let privateBalance = 0n;
|
|
@@ -530,15 +649,13 @@ export class BotFactory {
|
|
|
530
649
|
// PrivateToken's mint accesses contract-level private storage vars (admin, total_supply).
|
|
531
650
|
const additionalScopes = isStandardToken ? undefined : [token.address];
|
|
532
651
|
const mintBatch = new BatchCall(token.wallet, calls);
|
|
533
|
-
const { estimatedGas } = await mintBatch.simulate({ from: minter, fee: { estimateGas: true }, additionalScopes });
|
|
534
652
|
await this.withNoMinTxsPerBlock(async () => {
|
|
535
653
|
const { txHash } = await mintBatch.send({
|
|
536
654
|
from: minter,
|
|
537
655
|
additionalScopes,
|
|
538
|
-
fee: { gasSettings: estimatedGas },
|
|
539
656
|
wait: NO_WAIT,
|
|
540
657
|
});
|
|
541
|
-
this.log.info(`Sent token mint tx with hash ${txHash.toString()}
|
|
658
|
+
this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
|
|
542
659
|
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
543
660
|
});
|
|
544
661
|
}
|
|
@@ -606,6 +723,19 @@ export class BotFactory {
|
|
|
606
723
|
return claim as L2AmountClaim;
|
|
607
724
|
}
|
|
608
725
|
|
|
726
|
+
/** Returns worst-case min fees across predicted slots, with fallback to current min fees. */
|
|
727
|
+
private async getMinFees(): Promise<GasFees> {
|
|
728
|
+
try {
|
|
729
|
+
const predicted = await this.aztecNode.getPredictedMinFees(ManaUsageEstimate.Limit);
|
|
730
|
+
if (predicted.length === 0) {
|
|
731
|
+
return this.aztecNode.getCurrentMinFees();
|
|
732
|
+
}
|
|
733
|
+
return predicted.reduce((worst, fees) => (fees.feePerL2Gas > worst.feePerL2Gas ? fees : worst));
|
|
734
|
+
} catch {
|
|
735
|
+
return this.aztecNode.getCurrentMinFees();
|
|
736
|
+
}
|
|
737
|
+
}
|
|
738
|
+
|
|
609
739
|
private async withNoMinTxsPerBlock<T>(fn: () => Promise<T>): Promise<T> {
|
|
610
740
|
if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
|
|
611
741
|
this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
|
package/src/interface.ts
CHANGED
|
@@ -22,11 +22,11 @@ export interface BotRunnerApi {
|
|
|
22
22
|
}
|
|
23
23
|
|
|
24
24
|
export const BotRunnerApiSchema: ApiSchemaFor<BotRunnerApi> = {
|
|
25
|
-
start: z.function(
|
|
26
|
-
stop: z.function(
|
|
27
|
-
run: z.function(
|
|
28
|
-
setup: z.function(
|
|
29
|
-
getInfo: z.function(
|
|
30
|
-
getConfig: z.function(
|
|
31
|
-
update: z.function(
|
|
25
|
+
start: z.function({ input: z.tuple([]), output: z.void() }),
|
|
26
|
+
stop: z.function({ input: z.tuple([]), output: z.void() }),
|
|
27
|
+
run: z.function({ input: z.tuple([]), output: z.void() }),
|
|
28
|
+
setup: z.function({ input: z.tuple([]), output: z.void() }),
|
|
29
|
+
getInfo: z.function({ input: z.tuple([]), output: BotInfoSchema }),
|
|
30
|
+
getConfig: z.function({ input: z.tuple([]), output: BotConfigSchema }),
|
|
31
|
+
update: z.function({ input: z.tuple([BotConfigSchema]), output: z.void() }),
|
|
32
32
|
};
|