@aztec/bot 3.0.3 → 4.0.0-devnet.1-patch.0

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/src/config.ts CHANGED
@@ -14,7 +14,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
14
14
  import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
15
15
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
16
16
  import { protocolContractsHash } from '@aztec/protocol-contracts';
17
- import { type ZodFor, schemas } from '@aztec/stdlib/schemas';
17
+ import { schemas, zodFor } from '@aztec/stdlib/schemas';
18
18
  import type { ComponentsVersions } from '@aztec/stdlib/versioning';
19
19
 
20
20
  import { z } from 'zod';
@@ -54,8 +54,8 @@ export type BotConfig = {
54
54
  publicTransfersPerTx: number;
55
55
  /** How to handle fee payments. */
56
56
  feePaymentMethod: 'fee_juice';
57
- /** 'How much is the bot willing to overpay vs. the current base fee' */
58
- baseFeePadding: number;
57
+ /** 'How much is the bot willing to overpay vs. the current min fee' */
58
+ minFeePadding: number;
59
59
  /** True to not automatically setup or start the bot on initialization. */
60
60
  noStart: boolean;
61
61
  /** How long to wait for a tx to be mined before reporting an error. */
@@ -80,50 +80,52 @@ export type BotConfig = {
80
80
  ammTxs: boolean;
81
81
  } & Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb'>;
82
82
 
83
- export const BotConfigSchema = z
84
- .object({
85
- nodeUrl: z.string().optional(),
86
- nodeAdminUrl: z.string().optional(),
87
- l1RpcUrls: z.array(z.string()).optional(),
88
- l1Mnemonic: schemas.SecretValue(z.string()).optional(),
89
- l1PrivateKey: schemas.SecretValue(z.string()).optional(),
90
- l1ToL2MessageTimeoutSeconds: z.number(),
91
- senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
92
- senderSalt: schemas.Fr.optional(),
93
- tokenSalt: schemas.Fr,
94
- txIntervalSeconds: z.number(),
95
- privateTransfersPerTx: z.number().int().nonnegative(),
96
- publicTransfersPerTx: z.number().int().nonnegative(),
97
- feePaymentMethod: z.literal('fee_juice'),
98
- baseFeePadding: z.number().int().nonnegative(),
99
- noStart: z.boolean(),
100
- txMinedWaitSeconds: z.number(),
101
- followChain: z.enum(BotFollowChain),
102
- maxPendingTxs: z.number().int().nonnegative(),
103
- flushSetupTransactions: z.boolean(),
104
- l2GasLimit: z.number().int().nonnegative().optional(),
105
- daGasLimit: z.number().int().nonnegative().optional(),
106
- contract: z.nativeEnum(SupportedTokenContracts),
107
- maxConsecutiveErrors: z.number().int().nonnegative(),
108
- stopWhenUnhealthy: z.boolean(),
109
- ammTxs: z.boolean().default(false),
110
- dataDirectory: z.string().optional(),
111
- dataStoreMapSizeKb: z.number().optional(),
112
- })
113
- .transform(config => ({
114
- nodeUrl: undefined,
115
- nodeAdminUrl: undefined,
116
- l1RpcUrls: undefined,
117
- senderSalt: undefined,
118
- l2GasLimit: undefined,
119
- daGasLimit: undefined,
120
- l1Mnemonic: undefined,
121
- l1PrivateKey: undefined,
122
- senderPrivateKey: undefined,
123
- dataDirectory: undefined,
124
- dataStoreMapSizeKb: 1_024 * 1_024,
125
- ...config,
126
- })) satisfies ZodFor<BotConfig>;
83
+ export const BotConfigSchema = zodFor<BotConfig>()(
84
+ z
85
+ .object({
86
+ nodeUrl: z.string().optional(),
87
+ nodeAdminUrl: z.string().optional(),
88
+ l1RpcUrls: z.array(z.string()).optional(),
89
+ l1Mnemonic: schemas.SecretValue(z.string()).optional(),
90
+ l1PrivateKey: schemas.SecretValue(z.string()).optional(),
91
+ l1ToL2MessageTimeoutSeconds: z.number(),
92
+ senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
93
+ senderSalt: schemas.Fr.optional(),
94
+ tokenSalt: schemas.Fr,
95
+ txIntervalSeconds: z.number(),
96
+ privateTransfersPerTx: z.number().int().nonnegative(),
97
+ publicTransfersPerTx: z.number().int().nonnegative(),
98
+ feePaymentMethod: z.literal('fee_juice'),
99
+ minFeePadding: z.number().int().nonnegative(),
100
+ noStart: z.boolean(),
101
+ txMinedWaitSeconds: z.number(),
102
+ followChain: z.enum(BotFollowChain),
103
+ maxPendingTxs: z.number().int().nonnegative(),
104
+ flushSetupTransactions: z.boolean(),
105
+ l2GasLimit: z.number().int().nonnegative().optional(),
106
+ daGasLimit: z.number().int().nonnegative().optional(),
107
+ contract: z.nativeEnum(SupportedTokenContracts),
108
+ maxConsecutiveErrors: z.number().int().nonnegative(),
109
+ stopWhenUnhealthy: z.boolean(),
110
+ ammTxs: z.boolean().default(false),
111
+ dataDirectory: z.string().optional(),
112
+ dataStoreMapSizeKb: z.number().optional(),
113
+ })
114
+ .transform(config => ({
115
+ nodeUrl: undefined,
116
+ nodeAdminUrl: undefined,
117
+ l1RpcUrls: undefined,
118
+ senderSalt: undefined,
119
+ l2GasLimit: undefined,
120
+ daGasLimit: undefined,
121
+ l1Mnemonic: undefined,
122
+ l1PrivateKey: undefined,
123
+ senderPrivateKey: undefined,
124
+ dataDirectory: undefined,
125
+ dataStoreMapSizeKb: 1_024 * 1_024,
126
+ ...config,
127
+ })),
128
+ );
127
129
 
128
130
  export const botConfigMappings: ConfigMappingsType<BotConfig> = {
129
131
  nodeUrl: {
@@ -191,8 +193,8 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
191
193
  parseEnv: val => (val as 'fee_juice') || undefined,
192
194
  defaultValue: 'fee_juice',
193
195
  },
194
- baseFeePadding: {
195
- env: 'BOT_BASE_FEE_PADDING',
196
+ minFeePadding: {
197
+ env: 'BOT_MIN_FEE_PADDING',
196
198
  description: 'How much is the bot willing to overpay vs. the current base fee',
197
199
  ...numberConfigHelper(3),
198
200
  },
package/src/factory.ts CHANGED
@@ -7,12 +7,15 @@ import {
7
7
  ContractFunctionInteraction,
8
8
  type DeployMethod,
9
9
  type DeployOptions,
10
+ NO_WAIT,
10
11
  } from '@aztec/aztec.js/contracts';
11
12
  import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
12
13
  import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
13
14
  import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
15
+ import { deriveKeys } from '@aztec/aztec.js/keys';
14
16
  import { createLogger } from '@aztec/aztec.js/log';
15
17
  import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
18
+ import { waitForTx } from '@aztec/aztec.js/node';
16
19
  import { createEthereumChain } from '@aztec/ethereum/chain';
17
20
  import { createExtendedL1Client } from '@aztec/ethereum/client';
18
21
  import { Fr } from '@aztec/foundation/curves/bn254';
@@ -20,6 +23,7 @@ import { Timer } from '@aztec/foundation/timer';
20
23
  import { AMMContract } from '@aztec/noir-contracts.js/AMM';
21
24
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
22
25
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
26
+ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
23
27
  import { GasSettings } from '@aztec/stdlib/gas';
24
28
  import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
25
29
  import { deriveSigningKey } from '@aztec/stdlib/keys';
@@ -116,8 +120,8 @@ export class BotFactory {
116
120
  contract: new SchnorrAccountContract(signingKey!),
117
121
  };
118
122
  const accountManager = await this.wallet.createAccount(accountData);
119
- const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
120
- if (isInit) {
123
+ const metadata = await this.wallet.getContractMetadata(accountManager.address);
124
+ if (metadata.isContractInitialized) {
121
125
  this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
122
126
  const timer = new Timer();
123
127
  const address = accountManager.address;
@@ -132,12 +136,18 @@ export class BotFactory {
132
136
 
133
137
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
134
138
  const deployMethod = await accountManager.getDeployMethod();
135
- const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
139
+ const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
136
140
  const gasSettings = GasSettings.default({ maxFeesPerGas });
137
- const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
138
- const txHash = await sentTx.getTxHash();
139
- this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
140
- await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
141
+
142
+ await this.withNoMinTxsPerBlock(async () => {
143
+ const txHash = await deployMethod.send({
144
+ from: AztecAddress.ZERO,
145
+ fee: { gasSettings, paymentMethod },
146
+ wait: NO_WAIT,
147
+ });
148
+ this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
149
+ return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
150
+ });
141
151
  this.log.info(`Account deployed at ${address}`);
142
152
 
143
153
  // Clean up the consumed bridge claim
@@ -165,33 +175,49 @@ export class BotFactory {
165
175
  */
166
176
  private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
167
177
  let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
178
+ let tokenInstance: ContractInstanceWithAddress | undefined;
168
179
  const deployOpts: DeployOptions = {
169
180
  from: sender,
170
181
  contractAddressSalt: this.config.tokenSalt,
171
182
  universalDeploy: true,
172
183
  };
184
+ let token: TokenContract | PrivateTokenContract;
173
185
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
174
186
  deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
187
+ tokenInstance = await deploy.getInstance(deployOpts);
188
+ token = TokenContract.at(tokenInstance.address, this.wallet);
175
189
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
176
- deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender);
190
+ // Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
191
+ const tokenSecretKey = Fr.random();
192
+ const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
193
+ deploy = PrivateTokenContract.deployWithPublicKeys(tokenPublicKeys, this.wallet, MINT_BALANCE, sender);
177
194
  deployOpts.skipInstancePublication = true;
178
195
  deployOpts.skipClassPublication = true;
179
196
  deployOpts.skipInitialization = false;
197
+
198
+ // Register the contract with the secret key before deployment
199
+ tokenInstance = await deploy.getInstance(deployOpts);
200
+ token = PrivateTokenContract.at(tokenInstance.address, this.wallet);
201
+ await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
180
202
  } else {
181
203
  throw new Error(`Unsupported token contract type: ${this.config.contract}`);
182
204
  }
183
205
 
184
- const address = (await deploy.getInstance(deployOpts)).address;
185
- if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
206
+ const address = tokenInstance?.address ?? (await deploy.getInstance(deployOpts)).address;
207
+ const metadata = await this.wallet.getContractMetadata(address);
208
+ if (metadata.isContractPublished) {
186
209
  this.log.info(`Token at ${address.toString()} already deployed`);
187
- return deploy.register();
210
+ await deploy.register();
188
211
  } else {
189
212
  this.log.info(`Deploying token contract at ${address.toString()}`);
190
- const sentTx = deploy.send(deployOpts);
191
- const txHash = await sentTx.getTxHash();
213
+ const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
192
214
  this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`);
193
- return this.withNoMinTxsPerBlock(() => sentTx.deployed({ timeout: this.config.txMinedWaitSeconds }));
215
+ await this.withNoMinTxsPerBlock(async () => {
216
+ await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
217
+ return token;
218
+ });
194
219
  }
220
+ return token;
195
221
  }
196
222
 
197
223
  /**
@@ -199,7 +225,7 @@ export class BotFactory {
199
225
  * @param wallet - Wallet to deploy the token contract from.
200
226
  * @returns The TokenContract instance.
201
227
  */
202
- private setupTokenContract(
228
+ private async setupTokenContract(
203
229
  deployer: AztecAddress,
204
230
  contractAddressSalt: Fr,
205
231
  name: string,
@@ -208,7 +234,8 @@ export class BotFactory {
208
234
  ): Promise<TokenContract> {
209
235
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
210
236
  const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
211
- return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
237
+ const instance = await this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
238
+ return TokenContract.at(instance.address, this.wallet);
212
239
  }
213
240
 
214
241
  private async setupAmmContract(
@@ -220,12 +247,14 @@ export class BotFactory {
220
247
  ): Promise<AMMContract> {
221
248
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
222
249
  const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
223
- const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
250
+ const instance = await this.registerOrDeployContract('AMM', deploy, deployOpts);
251
+ const amm = AMMContract.at(instance.address, this.wallet);
224
252
 
225
253
  this.log.info(`AMM deployed at ${amm.address}`);
226
- const minterTx = lpToken.methods.set_minter(amm.address, true).send({ from: deployer });
227
- this.log.info(`Set LP token minter to AMM txHash=${(await minterTx.getTxHash()).toString()}`);
228
- await minterTx.wait({ timeout: this.config.txMinedWaitSeconds });
254
+ const minterReceipt = await lpToken.methods
255
+ .set_minter(amm.address, true)
256
+ .send({ from: deployer, wait: { timeout: this.config.txMinedWaitSeconds } });
257
+ this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`);
229
258
  this.log.info(`Liquidity token initialized`);
230
259
 
231
260
  return amm;
@@ -284,23 +313,22 @@ export class BotFactory {
284
313
  .getFunctionCall(),
285
314
  });
286
315
 
287
- const mintTx = new BatchCall(this.wallet, [
316
+ const mintReceipt = await new BatchCall(this.wallet, [
288
317
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
289
318
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
290
- ]).send({ from: liquidityProvider });
319
+ ]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } });
291
320
 
292
- this.log.info(`Sent mint tx: ${(await mintTx.getTxHash()).toString()}`);
293
- await mintTx.wait({ timeout: this.config.txMinedWaitSeconds });
321
+ this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
294
322
 
295
- const addLiquidityTx = amm.methods
323
+ const addLiquidityReceipt = await amm.methods
296
324
  .add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce)
297
325
  .send({
298
326
  from: liquidityProvider,
299
327
  authWitnesses: [token0Authwit, token1Authwit],
328
+ wait: { timeout: this.config.txMinedWaitSeconds },
300
329
  });
301
330
 
302
- this.log.info(`Sent tx to add liquidity to the AMM: ${(await addLiquidityTx.getTxHash()).toString()}`);
303
- await addLiquidityTx.wait({ timeout: this.config.txMinedWaitSeconds });
331
+ this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
304
332
  this.log.info(`Liquidity added`);
305
333
 
306
334
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
@@ -313,18 +341,22 @@ export class BotFactory {
313
341
  name: string,
314
342
  deploy: DeployMethod<T>,
315
343
  deployOpts: DeployOptions,
316
- ): Promise<T> {
317
- const address = (await deploy.getInstance(deployOpts)).address;
318
- if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
344
+ ): Promise<ContractInstanceWithAddress> {
345
+ const instance = await deploy.getInstance(deployOpts);
346
+ const address = instance.address;
347
+ const metadata = await this.wallet.getContractMetadata(address);
348
+ if (metadata.isContractPublished) {
319
349
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
320
- return deploy.register();
350
+ await deploy.register();
321
351
  } else {
322
352
  this.log.info(`Deploying contract ${name} at ${address.toString()}`);
323
- const sentTx = deploy.send(deployOpts);
324
- const txHash = await sentTx.getTxHash();
325
- this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
326
- return this.withNoMinTxsPerBlock(() => sentTx.deployed({ timeout: this.config.txMinedWaitSeconds }));
353
+ await this.withNoMinTxsPerBlock(async () => {
354
+ const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
355
+ this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
356
+ return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
357
+ });
327
358
  }
359
+ return instance;
328
360
  }
329
361
 
330
362
  /**
@@ -360,10 +392,12 @@ export class BotFactory {
360
392
  this.log.info(`Skipping minting as ${minter.toString()} has enough tokens`);
361
393
  return;
362
394
  }
363
- const sentTx = new BatchCall(token.wallet, calls).send({ from: minter });
364
- const txHash = await sentTx.getTxHash();
365
- this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
366
- await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
395
+
396
+ await this.withNoMinTxsPerBlock(async () => {
397
+ const txHash = await new BatchCall(token.wallet, calls).send({ from: minter, wait: NO_WAIT });
398
+ this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
399
+ return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
400
+ });
367
401
  }
368
402
 
369
403
  /**