@aztec/bot 0.0.1-commit.d3ec352c → 0.0.1-commit.d6f2b3f94

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
@@ -10,11 +10,11 @@ import {
10
10
  secretFrConfigHelper,
11
11
  secretStringConfigHelper,
12
12
  } from '@aztec/foundation/config';
13
- import { Fr } from '@aztec/foundation/fields';
13
+ 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,18 +7,23 @@ 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';
16
- import { createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
17
- import { Fr } from '@aztec/foundation/fields';
18
+ import { waitForTx } from '@aztec/aztec.js/node';
19
+ import { createEthereumChain } from '@aztec/ethereum/chain';
20
+ import { createExtendedL1Client } from '@aztec/ethereum/client';
21
+ import { Fr } from '@aztec/foundation/curves/bn254';
18
22
  import { Timer } from '@aztec/foundation/timer';
19
23
  import { AMMContract } from '@aztec/noir-contracts.js/AMM';
20
24
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
21
25
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
26
+ import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
22
27
  import { GasSettings } from '@aztec/stdlib/gas';
23
28
  import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
24
29
  import { deriveSigningKey } from '@aztec/stdlib/keys';
@@ -53,8 +58,8 @@ export class BotFactory {
53
58
  node: AztecNode;
54
59
  recipient: AztecAddress;
55
60
  }> {
56
- const recipient = (await this.wallet.createAccount()).address;
57
61
  const defaultAccountAddress = await this.setupAccount();
62
+ const recipient = (await this.wallet.createAccount()).address;
58
63
  const token = await this.setupToken(defaultAccountAddress);
59
64
  await this.mintTokens(token, defaultAccountAddress);
60
65
  return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
@@ -115,8 +120,8 @@ export class BotFactory {
115
120
  contract: new SchnorrAccountContract(signingKey!),
116
121
  };
117
122
  const accountManager = await this.wallet.createAccount(accountData);
118
- const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
119
- if (isInit) {
123
+ const metadata = await this.wallet.getContractMetadata(accountManager.address);
124
+ if (metadata.isContractInitialized) {
120
125
  this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
121
126
  const timer = new Timer();
122
127
  const address = accountManager.address;
@@ -131,12 +136,18 @@ export class BotFactory {
131
136
 
132
137
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
133
138
  const deployMethod = await accountManager.getDeployMethod();
134
- const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
139
+ const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
135
140
  const gasSettings = GasSettings.default({ maxFeesPerGas });
136
- const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
137
- const txHash = await sentTx.getTxHash();
138
- this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
139
- 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
+ });
140
151
  this.log.info(`Account deployed at ${address}`);
141
152
 
142
153
  // Clean up the consumed bridge claim
@@ -164,33 +175,49 @@ export class BotFactory {
164
175
  */
165
176
  private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
166
177
  let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
178
+ let tokenInstance: ContractInstanceWithAddress | undefined;
167
179
  const deployOpts: DeployOptions = {
168
180
  from: sender,
169
181
  contractAddressSalt: this.config.tokenSalt,
170
182
  universalDeploy: true,
171
183
  };
184
+ let token: TokenContract | PrivateTokenContract;
172
185
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
173
186
  deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
187
+ tokenInstance = await deploy.getInstance(deployOpts);
188
+ token = TokenContract.at(tokenInstance.address, this.wallet);
174
189
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
175
- 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);
176
194
  deployOpts.skipInstancePublication = true;
177
195
  deployOpts.skipClassPublication = true;
178
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);
179
202
  } else {
180
203
  throw new Error(`Unsupported token contract type: ${this.config.contract}`);
181
204
  }
182
205
 
183
- const address = (await deploy.getInstance(deployOpts)).address;
184
- 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) {
185
209
  this.log.info(`Token at ${address.toString()} already deployed`);
186
- return deploy.register();
210
+ await deploy.register();
187
211
  } else {
188
212
  this.log.info(`Deploying token contract at ${address.toString()}`);
189
- const sentTx = deploy.send(deployOpts);
190
- const txHash = await sentTx.getTxHash();
213
+ const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
191
214
  this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`);
192
- 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
+ });
193
219
  }
220
+ return token;
194
221
  }
195
222
 
196
223
  /**
@@ -198,7 +225,7 @@ export class BotFactory {
198
225
  * @param wallet - Wallet to deploy the token contract from.
199
226
  * @returns The TokenContract instance.
200
227
  */
201
- private setupTokenContract(
228
+ private async setupTokenContract(
202
229
  deployer: AztecAddress,
203
230
  contractAddressSalt: Fr,
204
231
  name: string,
@@ -207,7 +234,8 @@ export class BotFactory {
207
234
  ): Promise<TokenContract> {
208
235
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
209
236
  const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
210
- 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);
211
239
  }
212
240
 
213
241
  private async setupAmmContract(
@@ -219,12 +247,14 @@ export class BotFactory {
219
247
  ): Promise<AMMContract> {
220
248
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
221
249
  const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
222
- 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);
223
252
 
224
253
  this.log.info(`AMM deployed at ${amm.address}`);
225
- const minterTx = lpToken.methods.set_minter(amm.address, true).send({ from: deployer });
226
- this.log.info(`Set LP token minter to AMM txHash=${(await minterTx.getTxHash()).toString()}`);
227
- 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()}`);
228
258
  this.log.info(`Liquidity token initialized`);
229
259
 
230
260
  return amm;
@@ -283,23 +313,22 @@ export class BotFactory {
283
313
  .getFunctionCall(),
284
314
  });
285
315
 
286
- const mintTx = new BatchCall(this.wallet, [
316
+ const mintReceipt = await new BatchCall(this.wallet, [
287
317
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
288
318
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
289
- ]).send({ from: liquidityProvider });
319
+ ]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } });
290
320
 
291
- this.log.info(`Sent mint tx: ${(await mintTx.getTxHash()).toString()}`);
292
- await mintTx.wait({ timeout: this.config.txMinedWaitSeconds });
321
+ this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
293
322
 
294
- const addLiquidityTx = amm.methods
323
+ const addLiquidityReceipt = await amm.methods
295
324
  .add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce)
296
325
  .send({
297
326
  from: liquidityProvider,
298
327
  authWitnesses: [token0Authwit, token1Authwit],
328
+ wait: { timeout: this.config.txMinedWaitSeconds },
299
329
  });
300
330
 
301
- this.log.info(`Sent tx to add liquidity to the AMM: ${(await addLiquidityTx.getTxHash()).toString()}`);
302
- await addLiquidityTx.wait({ timeout: this.config.txMinedWaitSeconds });
331
+ this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
303
332
  this.log.info(`Liquidity added`);
304
333
 
305
334
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
@@ -312,18 +341,22 @@ export class BotFactory {
312
341
  name: string,
313
342
  deploy: DeployMethod<T>,
314
343
  deployOpts: DeployOptions,
315
- ): Promise<T> {
316
- const address = (await deploy.getInstance(deployOpts)).address;
317
- 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) {
318
349
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
319
- return deploy.register();
350
+ await deploy.register();
320
351
  } else {
321
352
  this.log.info(`Deploying contract ${name} at ${address.toString()}`);
322
- const sentTx = deploy.send(deployOpts);
323
- const txHash = await sentTx.getTxHash();
324
- this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
325
- 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
+ });
326
358
  }
359
+ return instance;
327
360
  }
328
361
 
329
362
  /**
@@ -359,10 +392,12 @@ export class BotFactory {
359
392
  this.log.info(`Skipping minting as ${minter.toString()} has enough tokens`);
360
393
  return;
361
394
  }
362
- const sentTx = new BatchCall(token.wallet, calls).send({ from: minter });
363
- const txHash = await sentTx.getTxHash();
364
- this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
365
- 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
+ });
366
401
  }
367
402
 
368
403
  /**
@@ -1,6 +1,6 @@
1
1
  import { AztecAddress } from '@aztec/aztec.js/addresses';
2
2
  import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
3
- import { Fr } from '@aztec/foundation/fields';
3
+ import { Fr } from '@aztec/foundation/curves/bn254';
4
4
  import { type Logger, createLogger } from '@aztec/foundation/log';
5
5
  import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
6
6