@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/factory.js CHANGED
@@ -1,5 +1,5 @@
1
1
  import { getInitialTestAccountsData } from '@aztec/accounts/testing';
2
- import { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import { NO_FROM } from '@aztec/aztec.js/account';
3
3
  import { BatchCall, NO_WAIT } from '@aztec/aztec.js/contracts';
4
4
  import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
5
5
  import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
@@ -7,6 +7,8 @@ import { deriveKeys } from '@aztec/aztec.js/keys';
7
7
  import { createLogger } from '@aztec/aztec.js/log';
8
8
  import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
9
9
  import { waitForTx } from '@aztec/aztec.js/node';
10
+ import { getFeeJuiceBalance } from '@aztec/aztec.js/utils';
11
+ import { ContractInitializationStatus } from '@aztec/aztec.js/wallet';
10
12
  import { createEthereumChain } from '@aztec/ethereum/chain';
11
13
  import { createExtendedL1Client } from '@aztec/ethereum/client';
12
14
  import { RollupContract } from '@aztec/ethereum/contracts';
@@ -17,13 +19,15 @@ import { AMMContract } from '@aztec/noir-contracts.js/AMM';
17
19
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
18
20
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
19
21
  import { TestContract } from '@aztec/noir-test-contracts.js/Test';
20
- import { GasFees, GasSettings } from '@aztec/stdlib/gas';
22
+ import { GasFees, GasSettings, ManaUsageEstimate } from '@aztec/stdlib/gas';
21
23
  import { deriveSigningKey } from '@aztec/stdlib/keys';
22
24
  import { SupportedTokenContracts } from './config.js';
23
25
  import { seedL1ToL2Message } from './l1_to_l2_seeding.js';
24
26
  import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
25
27
  const MINT_BALANCE = 1e12;
26
28
  const MIN_BALANCE = 1e3;
29
+ const FEE_JUICE_TOP_UP_THRESHOLD = 100n * 10n ** 18n;
30
+ const FEE_JUICE_TOP_UP_TARGET = 10_000n * 10n ** 18n;
27
31
  export class BotFactory {
28
32
  config;
29
33
  wallet;
@@ -48,7 +52,8 @@ export class BotFactory {
48
52
  */ async setup() {
49
53
  const defaultAccountAddress = await this.setupAccount();
50
54
  const recipient = (await this.wallet.createSchnorrAccount(Fr.random(), Fr.random())).address;
51
- const token = await this.setupToken(defaultAccountAddress);
55
+ const token = await this.setupTokenWithOptionalEarlyRefuel(defaultAccountAddress);
56
+ await this.ensureFeeJuiceBalance(defaultAccountAddress, token);
52
57
  await this.mintTokens(token, defaultAccountAddress);
53
58
  return {
54
59
  wallet: this.wallet,
@@ -60,7 +65,8 @@ export class BotFactory {
60
65
  }
61
66
  async setupAmm() {
62
67
  const defaultAccountAddress = await this.setupAccount();
63
- const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
68
+ const token0 = await this.setupTokenContractWithOptionalEarlyRefuel(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
69
+ await this.ensureFeeJuiceBalance(defaultAccountAddress, token0);
64
70
  const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
65
71
  const liquidityToken = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotLPToken', 'BOTLP');
66
72
  const amm = await this.setupAmmContract(defaultAccountAddress, this.config.tokenSalt, token0, token1, liquidityToken);
@@ -126,11 +132,12 @@ export class BotFactory {
126
132
  }
127
133
  async setupTestContract(deployer) {
128
134
  const deployOpts = {
129
- from: deployer,
130
- contractAddressSalt: this.config.tokenSalt,
131
- universalDeploy: true
135
+ from: deployer
132
136
  };
133
- const deploy = TestContract.deploy(this.wallet);
137
+ const deploy = TestContract.deploy(this.wallet, {
138
+ salt: this.config.tokenSalt,
139
+ universalDeploy: true
140
+ });
134
141
  const instance = await this.registerOrDeployContract('TestContract', deploy, deployOpts);
135
142
  return TestContract.at(instance.address, this.wallet);
136
143
  }
@@ -152,7 +159,7 @@ export class BotFactory {
152
159
  const signingKey = deriveSigningKey(secret);
153
160
  const accountManager = await this.wallet.createSchnorrAccount(secret, salt, signingKey);
154
161
  const metadata = await this.wallet.getContractMetadata(accountManager.address);
155
- if (metadata.isContractInitialized) {
162
+ if (metadata.initializationStatus === ContractInitializationStatus.INITIALIZED) {
156
163
  this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
157
164
  const timer = new Timer();
158
165
  const address = accountManager.address;
@@ -165,31 +172,15 @@ export class BotFactory {
165
172
  const claim = await this.getOrCreateBridgeClaim(address);
166
173
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
167
174
  const deployMethod = await accountManager.getDeployMethod();
168
- const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
169
- const { estimatedGas } = await deployMethod.simulate({
170
- from: AztecAddress.ZERO,
171
- fee: {
172
- estimateGas: true,
173
- paymentMethod
174
- }
175
- });
176
- const gasSettings = GasSettings.from({
177
- ...estimatedGas,
178
- maxFeesPerGas,
179
- maxPriorityFeesPerGas: GasFees.empty()
180
- });
181
175
  await this.withNoMinTxsPerBlock(async ()=>{
182
176
  const { txHash } = await deployMethod.send({
183
- from: AztecAddress.ZERO,
177
+ from: NO_FROM,
184
178
  fee: {
185
- gasSettings,
186
179
  paymentMethod
187
180
  },
188
181
  wait: NO_WAIT
189
182
  });
190
- this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`, {
191
- gasSettings
192
- });
183
+ this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
193
184
  return waitForTx(this.aztecNode, txHash, {
194
185
  timeout: this.config.txMinedWaitSeconds
195
186
  });
@@ -206,32 +197,92 @@ export class BotFactory {
206
197
  return accountManager.address;
207
198
  }
208
199
  /**
200
+ * Setup token and refuel first: if the token already exists (restart scenario),
201
+ * run ensureFeeJuiceBalance before any step that might need fee juice. When deploying,
202
+ * use a bridge claim if balance is below threshold.
203
+ */ async setupTokenWithOptionalEarlyRefuel(sender) {
204
+ const token = await this.getTokenInstance(sender);
205
+ const address = token.address;
206
+ const metadata = await this.wallet.getContractMetadata(address);
207
+ if (metadata.isContractPublished) {
208
+ this.log.info(`Token at ${address.toString()} already deployed, refueling before setup`);
209
+ await this.ensureFeeJuiceBalance(sender, token);
210
+ }
211
+ return this.setupToken(sender);
212
+ }
213
+ /**
214
+ * Setup token0 for AMM with refuel-first behaviour when token already exists.
215
+ */ async setupTokenContractWithOptionalEarlyRefuel(deployer, salt, name, ticker, decimals = 18) {
216
+ const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals, {
217
+ salt,
218
+ universalDeploy: true
219
+ });
220
+ const instance = await deploy.getInstance();
221
+ const metadata = await this.wallet.getContractMetadata(instance.address);
222
+ if (metadata.isContractPublished) {
223
+ this.log.info(`Token ${name} at ${instance.address.toString()} already deployed, refueling before setup`);
224
+ const token = TokenContract.at(instance.address, this.wallet);
225
+ await this.ensureFeeJuiceBalance(deployer, token);
226
+ }
227
+ return this.setupTokenContract(deployer, salt, name, ticker, decimals);
228
+ }
229
+ async getTokenInstance(sender) {
230
+ const salt = this.config.tokenSalt;
231
+ if (this.config.contract === SupportedTokenContracts.TokenContract) {
232
+ const deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18, {
233
+ salt,
234
+ universalDeploy: true
235
+ });
236
+ const instance = await deploy.getInstance();
237
+ return TokenContract.at(instance.address, this.wallet);
238
+ }
239
+ if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
240
+ const tokenSecretKey = Fr.random();
241
+ const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
242
+ const deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender, {
243
+ salt,
244
+ universalDeploy: true,
245
+ publicKeys: tokenPublicKeys
246
+ });
247
+ const instance = await deploy.getInstance();
248
+ return PrivateTokenContract.at(instance.address, this.wallet);
249
+ }
250
+ throw new Error(`Unsupported token contract type: ${this.config.contract}`);
251
+ }
252
+ /**
209
253
  * Checks if the token contract is deployed and deploys it if necessary.
210
- * @param wallet - Wallet to deploy the token contract from.
211
- * @returns The TokenContract instance.
254
+ * Uses a bridge claim for deploy when balance is below threshold to avoid failing before refuel.
255
+ * @param sender - Aztec address to deploy the token contract from.
256
+ * @param existingToken - Optional token instance when called from setupTokenWithOptionalEarlyRefuel.
257
+ * @returns The TokenContract or PrivateTokenContract instance.
212
258
  */ async setupToken(sender) {
213
259
  let deploy;
214
- let tokenInstance;
260
+ const salt = this.config.tokenSalt;
215
261
  const deployOpts = {
216
- from: sender,
217
- contractAddressSalt: this.config.tokenSalt,
218
- universalDeploy: true
262
+ from: sender
219
263
  };
220
264
  let token;
221
265
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
222
- deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
223
- tokenInstance = await deploy.getInstance(deployOpts);
224
- token = TokenContract.at(tokenInstance.address, this.wallet);
266
+ deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18, {
267
+ salt,
268
+ universalDeploy: true
269
+ });
270
+ const instance = await deploy.getInstance();
271
+ token = TokenContract.at(instance.address, this.wallet);
225
272
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
226
273
  // Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
227
274
  const tokenSecretKey = Fr.random();
228
275
  const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
229
- deploy = PrivateTokenContract.deployWithPublicKeys(tokenPublicKeys, this.wallet, MINT_BALANCE, sender);
276
+ deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender, {
277
+ salt,
278
+ universalDeploy: true,
279
+ publicKeys: tokenPublicKeys
280
+ });
230
281
  deployOpts.skipInstancePublication = true;
231
282
  deployOpts.skipClassPublication = true;
232
283
  deployOpts.skipInitialization = false;
233
284
  // Register the contract with the secret key before deployment
234
- tokenInstance = await deploy.getInstance(deployOpts);
285
+ const tokenInstance = await deploy.getInstance();
235
286
  token = PrivateTokenContract.at(tokenInstance.address, this.wallet);
236
287
  await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
237
288
  // The contract constructor initializes private storage vars that need the contract's own nullifier key.
@@ -241,81 +292,43 @@ export class BotFactory {
241
292
  } else {
242
293
  throw new Error(`Unsupported token contract type: ${this.config.contract}`);
243
294
  }
244
- const address = tokenInstance?.address ?? (await deploy.getInstance(deployOpts)).address;
245
- const metadata = await this.wallet.getContractMetadata(address);
246
- if (metadata.isContractPublished) {
247
- this.log.info(`Token at ${address.toString()} already deployed`);
248
- await deploy.register();
249
- } else {
250
- this.log.info(`Deploying token contract at ${address.toString()}`);
251
- const { estimatedGas } = await deploy.simulate({
252
- ...deployOpts,
253
- fee: {
254
- estimateGas: true
255
- }
256
- });
257
- const { txHash } = await deploy.send({
258
- ...deployOpts,
259
- fee: {
260
- gasSettings: estimatedGas
261
- },
262
- wait: NO_WAIT
263
- });
264
- this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`, {
265
- estimatedGas
266
- });
267
- await this.withNoMinTxsPerBlock(async ()=>{
268
- await waitForTx(this.aztecNode, txHash, {
269
- timeout: this.config.txMinedWaitSeconds
270
- });
271
- return token;
272
- });
273
- }
295
+ await this.registerOrDeployContract('token', deploy, deployOpts);
274
296
  return token;
275
297
  }
276
298
  /**
277
299
  * Checks if the token contract is deployed and deploys it if necessary.
278
300
  * @param wallet - Wallet to deploy the token contract from.
279
301
  * @returns The TokenContract instance.
280
- */ async setupTokenContract(deployer, contractAddressSalt, name, ticker, decimals = 18) {
302
+ */ async setupTokenContract(deployer, salt, name, ticker, decimals = 18) {
281
303
  const deployOpts = {
282
- from: deployer,
283
- contractAddressSalt,
284
- universalDeploy: true
304
+ from: deployer
285
305
  };
286
- const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
306
+ const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals, {
307
+ salt,
308
+ universalDeploy: true
309
+ });
287
310
  const instance = await this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
288
311
  return TokenContract.at(instance.address, this.wallet);
289
312
  }
290
- async setupAmmContract(deployer, contractAddressSalt, token0, token1, lpToken) {
313
+ async setupAmmContract(deployer, salt, token0, token1, lpToken) {
291
314
  const deployOpts = {
292
- from: deployer,
293
- contractAddressSalt,
294
- universalDeploy: true
315
+ from: deployer
295
316
  };
296
- const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
317
+ const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address, {
318
+ salt,
319
+ universalDeploy: true
320
+ });
297
321
  const instance = await this.registerOrDeployContract('AMM', deploy, deployOpts);
298
322
  const amm = AMMContract.at(instance.address, this.wallet);
299
323
  this.log.info(`AMM deployed at ${amm.address}`);
300
324
  const setMinterInteraction = lpToken.methods.set_minter(amm.address, true);
301
- const { estimatedGas: setMinterGas } = await setMinterInteraction.simulate({
302
- from: deployer,
303
- fee: {
304
- estimateGas: true
305
- }
306
- });
307
325
  const { receipt: minterReceipt } = await setMinterInteraction.send({
308
326
  from: deployer,
309
- fee: {
310
- gasSettings: setMinterGas
311
- },
312
327
  wait: {
313
328
  timeout: this.config.txMinedWaitSeconds
314
329
  }
315
330
  });
316
- this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`, {
317
- estimatedGas: setMinterGas
318
- });
331
+ this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`);
319
332
  this.log.info(`Liquidity token initialized`);
320
333
  return amm;
321
334
  }
@@ -352,40 +365,16 @@ export class BotFactory {
352
365
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
353
366
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE)
354
367
  ]);
355
- const { estimatedGas: mintGas } = await mintBatch.simulate({
356
- from: liquidityProvider,
357
- fee: {
358
- estimateGas: true
359
- }
360
- });
361
368
  const { receipt: mintReceipt } = await mintBatch.send({
362
369
  from: liquidityProvider,
363
- fee: {
364
- gasSettings: mintGas
365
- },
366
370
  wait: {
367
371
  timeout: this.config.txMinedWaitSeconds
368
372
  }
369
373
  });
370
- this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`, {
371
- estimatedGas: mintGas
372
- });
374
+ this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
373
375
  const addLiquidityInteraction = amm.methods.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce);
374
- const { estimatedGas: addLiquidityGas } = await addLiquidityInteraction.simulate({
375
- from: liquidityProvider,
376
- fee: {
377
- estimateGas: true
378
- },
379
- authWitnesses: [
380
- token0Authwit,
381
- token1Authwit
382
- ]
383
- });
384
376
  const { receipt: addLiquidityReceipt } = await addLiquidityInteraction.send({
385
377
  from: liquidityProvider,
386
- fee: {
387
- gasSettings: addLiquidityGas
388
- },
389
378
  authWitnesses: [
390
379
  token0Authwit,
391
380
  token1Authwit
@@ -394,50 +383,140 @@ export class BotFactory {
394
383
  timeout: this.config.txMinedWaitSeconds
395
384
  }
396
385
  });
397
- this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`, {
398
- estimatedGas: addLiquidityGas
399
- });
386
+ this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
400
387
  this.log.info(`Liquidity added`);
401
388
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
402
389
  this.log.info(`Updated private balances of ${defaultAccountAddress} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`);
403
390
  }
404
391
  async registerOrDeployContract(name, deploy, deployOpts) {
405
- const instance = await deploy.getInstance(deployOpts);
392
+ const instance = await deploy.getInstance();
406
393
  const address = instance.address;
407
394
  const metadata = await this.wallet.getContractMetadata(address);
408
395
  if (metadata.isContractPublished) {
409
396
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
410
397
  await deploy.register();
411
398
  } else {
412
- const { estimatedGas } = await deploy.simulate({
413
- ...deployOpts,
399
+ const sender = deployOpts.from === NO_FROM ? undefined : deployOpts.from;
400
+ const balance = sender ? await getFeeJuiceBalance(sender, this.aztecNode) : 0n;
401
+ const useClaim = sender && balance < FEE_JUICE_TOP_UP_THRESHOLD && this.config.feePaymentMethod === 'fee_juice' && !!this.config.l1RpcUrls?.length;
402
+ const mnemonicOrPrivateKey = this.config.l1PrivateKey?.getValue() ?? this.config.l1Mnemonic?.getValue();
403
+ if (useClaim && mnemonicOrPrivateKey) {
404
+ const claim = await this.getOrCreateBridgeClaim(sender);
405
+ const paymentMethod = new FeeJuicePaymentMethodWithClaim(sender, claim);
406
+ const { estimatedGas } = await deploy.simulate({
407
+ ...deployOpts,
408
+ fee: {
409
+ estimateGas: true,
410
+ paymentMethod
411
+ }
412
+ });
413
+ const maxFeesPerGas = (await this.getMinFees()).mul(1 + this.config.minFeePadding);
414
+ const gasSettings = GasSettings.from({
415
+ ...estimatedGas,
416
+ maxFeesPerGas,
417
+ maxPriorityFeesPerGas: GasFees.empty()
418
+ });
419
+ await this.withNoMinTxsPerBlock(async ()=>{
420
+ const { txHash } = await deploy.send({
421
+ ...deployOpts,
422
+ fee: {
423
+ gasSettings,
424
+ paymentMethod
425
+ },
426
+ wait: NO_WAIT
427
+ });
428
+ this.log.info(`Sent contract ${name} deploy tx ${txHash.toString()} (using bridge claim, balance was ${balance})`);
429
+ return waitForTx(this.aztecNode, txHash, {
430
+ timeout: this.config.txMinedWaitSeconds
431
+ });
432
+ });
433
+ await this.store.deleteBridgeClaim(sender);
434
+ } else {
435
+ const { estimatedGas } = await deploy.simulate({
436
+ ...deployOpts,
437
+ fee: {
438
+ estimateGas: true
439
+ }
440
+ });
441
+ this.log.info(`Deploying contract ${name} at ${address.toString()}`, {
442
+ estimatedGas
443
+ });
444
+ await this.withNoMinTxsPerBlock(async ()=>{
445
+ const { txHash } = await deploy.send({
446
+ ...deployOpts,
447
+ fee: {
448
+ gasSettings: estimatedGas
449
+ },
450
+ wait: NO_WAIT
451
+ });
452
+ this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
453
+ return waitForTx(this.aztecNode, txHash, {
454
+ timeout: this.config.txMinedWaitSeconds
455
+ });
456
+ });
457
+ }
458
+ }
459
+ return instance;
460
+ }
461
+ /**
462
+ * Mints private and public tokens for the sender if their balance is below the minimum.
463
+ * @param token - Token contract.
464
+ */ /**
465
+ * Ensures the account has sufficient fee juice by bridging from L1 if balance is below threshold.
466
+ * Bridges repeatedly until balance reaches the target (10k FJ).
467
+ * Used on startup/restart to top up when the account has run out after previous runs.
468
+ */ async ensureFeeJuiceBalance(account, token) {
469
+ const { feePaymentMethod, l1RpcUrls } = this.config;
470
+ if (feePaymentMethod !== 'fee_juice' || !l1RpcUrls?.length) {
471
+ return;
472
+ }
473
+ const mnemonicOrPrivateKey = this.config.l1PrivateKey?.getValue() ?? this.config.l1Mnemonic?.getValue();
474
+ if (!mnemonicOrPrivateKey) {
475
+ return;
476
+ }
477
+ let balance = await getFeeJuiceBalance(account, this.aztecNode);
478
+ if (balance >= FEE_JUICE_TOP_UP_THRESHOLD) {
479
+ this.log.info(`Fee juice balance ${balance} above threshold ${FEE_JUICE_TOP_UP_THRESHOLD}, skipping top-up`);
480
+ return;
481
+ }
482
+ this.log.info(`Fee juice balance ${balance} below threshold ${FEE_JUICE_TOP_UP_THRESHOLD}, bridging from L1 until ${FEE_JUICE_TOP_UP_TARGET}`);
483
+ const maxFeesPerGas = (await this.getMinFees()).mul(1 + this.config.minFeePadding);
484
+ const minimalInteraction = isStandardTokenContract(token) ? token.methods.transfer_in_public(account, account, 0n, 0) : token.methods.transfer(0n, account, account);
485
+ while(balance < FEE_JUICE_TOP_UP_TARGET){
486
+ const claim = await this.bridgeL1FeeJuice(account);
487
+ const paymentMethod = new FeeJuicePaymentMethodWithClaim(account, claim);
488
+ const { estimatedGas } = await minimalInteraction.simulate({
489
+ from: account,
414
490
  fee: {
415
- estimateGas: true
491
+ estimateGas: true,
492
+ paymentMethod
416
493
  }
417
494
  });
418
- this.log.info(`Deploying contract ${name} at ${address.toString()}`, {
419
- estimatedGas
495
+ const gasSettings = GasSettings.from({
496
+ ...estimatedGas,
497
+ maxFeesPerGas,
498
+ maxPriorityFeesPerGas: GasFees.empty()
420
499
  });
421
500
  await this.withNoMinTxsPerBlock(async ()=>{
422
- const { txHash } = await deploy.send({
423
- ...deployOpts,
501
+ const { txHash } = await minimalInteraction.send({
502
+ from: account,
424
503
  fee: {
425
- gasSettings: estimatedGas
504
+ gasSettings,
505
+ paymentMethod
426
506
  },
427
507
  wait: NO_WAIT
428
508
  });
429
- this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
509
+ this.log.info(`Sent fee juice top-up tx ${txHash.toString()}`);
430
510
  return waitForTx(this.aztecNode, txHash, {
431
511
  timeout: this.config.txMinedWaitSeconds
432
512
  });
433
513
  });
514
+ balance = await getFeeJuiceBalance(account, this.aztecNode);
515
+ this.log.info(`Fee juice balance after top-up: ${balance}`);
434
516
  }
435
- return instance;
517
+ this.log.info(`Fee juice top-up complete for ${account.toString()}`);
436
518
  }
437
- /**
438
- * Mints private and public tokens for the sender if their balance is below the minimum.
439
- * @param token - Token contract.
440
- */ async mintTokens(token, minter) {
519
+ async mintTokens(token, minter) {
441
520
  const isStandardToken = isStandardTokenContract(token);
442
521
  let privateBalance = 0n;
443
522
  let publicBalance = 0n;
@@ -464,25 +543,13 @@ export class BotFactory {
464
543
  token.address
465
544
  ];
466
545
  const mintBatch = new BatchCall(token.wallet, calls);
467
- const { estimatedGas } = await mintBatch.simulate({
468
- from: minter,
469
- fee: {
470
- estimateGas: true
471
- },
472
- additionalScopes
473
- });
474
546
  await this.withNoMinTxsPerBlock(async ()=>{
475
547
  const { txHash } = await mintBatch.send({
476
548
  from: minter,
477
549
  additionalScopes,
478
- fee: {
479
- gasSettings: estimatedGas
480
- },
481
550
  wait: NO_WAIT
482
551
  });
483
- this.log.info(`Sent token mint tx with hash ${txHash.toString()}`, {
484
- estimatedGas
485
- });
552
+ this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
486
553
  return waitForTx(this.aztecNode, txHash, {
487
554
  timeout: this.config.txMinedWaitSeconds
488
555
  });
@@ -534,6 +601,17 @@ export class BotFactory {
534
601
  this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
535
602
  return claim;
536
603
  }
604
+ /** Returns worst-case min fees across predicted slots, with fallback to current min fees. */ async getMinFees() {
605
+ try {
606
+ const predicted = await this.aztecNode.getPredictedMinFees(ManaUsageEstimate.Limit);
607
+ if (predicted.length === 0) {
608
+ return this.aztecNode.getCurrentMinFees();
609
+ }
610
+ return predicted.reduce((worst, fees)=>fees.feePerL2Gas > worst.feePerL2Gas ? fees : worst);
611
+ } catch {
612
+ return this.aztecNode.getCurrentMinFees();
613
+ }
614
+ }
537
615
  async withNoMinTxsPerBlock(fn) {
538
616
  if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
539
617
  this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
@@ -4,11 +4,7 @@ import { z } from 'zod';
4
4
  import { type BotConfig } from './config.js';
5
5
  export declare const BotInfoSchema: z.ZodObject<{
6
6
  botAddress: import("@aztec/stdlib/schemas").ZodFor<AztecAddress>;
7
- }, "strip", z.ZodTypeAny, {
8
- botAddress: AztecAddress;
9
- }, {
10
- botAddress?: any;
11
- }>;
7
+ }, z.core.$strip>;
12
8
  export type BotInfo = z.infer<typeof BotInfoSchema>;
13
9
  export interface BotRunnerApi {
14
10
  start(): Promise<void>;
@@ -20,4 +16,4 @@ export interface BotRunnerApi {
20
16
  update(config: BotConfig): Promise<void>;
21
17
  }
22
18
  export declare const BotRunnerApiSchema: ApiSchemaFor<BotRunnerApi>;
23
- //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW50ZXJmYWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUUxRCxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLE9BQU8sRUFBRSxLQUFLLFNBQVMsRUFBbUIsTUFBTSxhQUFhLENBQUM7QUFFOUQsZUFBTyxNQUFNLGFBQWE7Ozs7OztFQUV4QixDQUFDO0FBRUgsTUFBTSxNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sYUFBYSxDQUFDLENBQUM7QUFFcEQsTUFBTSxXQUFXLFlBQVk7SUFDM0IsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QixJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RCLEdBQUcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDckIsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QixTQUFTLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2hDLE9BQU8sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUIsTUFBTSxDQUFDLE1BQU0sRUFBRSxTQUFTLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQzFDO0FBRUQsZUFBTyxNQUFNLGtCQUFrQixFQUFFLFlBQVksQ0FBQyxZQUFZLENBUXpELENBQUMifQ==
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW50ZXJmYWNlLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW50ZXJmYWNlLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxZQUFZLEVBQUUsTUFBTSwyQkFBMkIsQ0FBQztBQUN6RCxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUUxRCxPQUFPLEVBQUUsQ0FBQyxFQUFFLE1BQU0sS0FBSyxDQUFDO0FBRXhCLE9BQU8sRUFBRSxLQUFLLFNBQVMsRUFBbUIsTUFBTSxhQUFhLENBQUM7QUFFOUQsZUFBTyxNQUFNLGFBQWE7O2lCQUV4QixDQUFDO0FBRUgsTUFBTSxNQUFNLE9BQU8sR0FBRyxDQUFDLENBQUMsS0FBSyxDQUFDLE9BQU8sYUFBYSxDQUFDLENBQUM7QUFFcEQsTUFBTSxXQUFXLFlBQVk7SUFDM0IsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QixJQUFJLElBQUksT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0lBQ3RCLEdBQUcsSUFBSSxPQUFPLENBQUMsSUFBSSxDQUFDLENBQUM7SUFDckIsS0FBSyxJQUFJLE9BQU8sQ0FBQyxJQUFJLENBQUMsQ0FBQztJQUN2QixTQUFTLElBQUksT0FBTyxDQUFDLFNBQVMsQ0FBQyxDQUFDO0lBQ2hDLE9BQU8sSUFBSSxPQUFPLENBQUMsT0FBTyxDQUFDLENBQUM7SUFDNUIsTUFBTSxDQUFDLE1BQU0sRUFBRSxTQUFTLEdBQUcsT0FBTyxDQUFDLElBQUksQ0FBQyxDQUFDO0NBQzFDO0FBRUQsZUFBTyxNQUFNLGtCQUFrQixFQUFFLFlBQVksQ0FBQyxZQUFZLENBUXpELENBQUMifQ==
@@ -1 +1 @@
1
- {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../src/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAE9D,eAAO,MAAM,aAAa;;;;;;EAExB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,eAAO,MAAM,kBAAkB,EAAE,YAAY,CAAC,YAAY,CAQzD,CAAC"}
1
+ {"version":3,"file":"interface.d.ts","sourceRoot":"","sources":["../src/interface.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,uBAAuB,CAAC;AAE1D,OAAO,EAAE,CAAC,EAAE,MAAM,KAAK,CAAC;AAExB,OAAO,EAAE,KAAK,SAAS,EAAmB,MAAM,aAAa,CAAC;AAE9D,eAAO,MAAM,aAAa;;iBAExB,CAAC;AAEH,MAAM,MAAM,OAAO,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,aAAa,CAAC,CAAC;AAEpD,MAAM,WAAW,YAAY;IAC3B,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,IAAI,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACtB,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACrB,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC,CAAC;IACvB,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,CAAC;IAChC,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAAC;IAC5B,MAAM,CAAC,MAAM,EAAE,SAAS,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;CAC1C;AAED,eAAO,MAAM,kBAAkB,EAAE,YAAY,CAAC,YAAY,CAQzD,CAAC"}
package/dest/interface.js CHANGED
@@ -5,11 +5,34 @@ export const BotInfoSchema = z.object({
5
5
  botAddress: AztecAddress.schema
6
6
  });
7
7
  export const BotRunnerApiSchema = {
8
- start: z.function().args().returns(z.void()),
9
- stop: z.function().args().returns(z.void()),
10
- run: z.function().args().returns(z.void()),
11
- setup: z.function().args().returns(z.void()),
12
- getInfo: z.function().args().returns(BotInfoSchema),
13
- getConfig: z.function().args().returns(BotConfigSchema),
14
- update: z.function().args(BotConfigSchema).returns(z.void())
8
+ start: z.function({
9
+ input: z.tuple([]),
10
+ output: z.void()
11
+ }),
12
+ stop: z.function({
13
+ input: z.tuple([]),
14
+ output: z.void()
15
+ }),
16
+ run: z.function({
17
+ input: z.tuple([]),
18
+ output: z.void()
19
+ }),
20
+ setup: z.function({
21
+ input: z.tuple([]),
22
+ output: z.void()
23
+ }),
24
+ getInfo: z.function({
25
+ input: z.tuple([]),
26
+ output: BotInfoSchema
27
+ }),
28
+ getConfig: z.function({
29
+ input: z.tuple([]),
30
+ output: BotConfigSchema
31
+ }),
32
+ update: z.function({
33
+ input: z.tuple([
34
+ BotConfigSchema
35
+ ]),
36
+ output: z.void()
37
+ })
15
38
  };