@aztec/bot 0.0.1-commit.b655e406 → 0.0.1-commit.d1f2d6c

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';
@@ -46,15 +51,28 @@ export class BotFactory {
46
51
  * Initializes a new bot by setting up the sender account, registering the recipient,
47
52
  * deploying the token contract, and minting tokens if necessary.
48
53
  */
49
- public async setup() {
50
- const recipient = (await this.wallet.createAccount()).address;
54
+ public async setup(): Promise<{
55
+ wallet: TestWallet;
56
+ defaultAccountAddress: AztecAddress;
57
+ token: TokenContract | PrivateTokenContract;
58
+ node: AztecNode;
59
+ recipient: AztecAddress;
60
+ }> {
51
61
  const defaultAccountAddress = await this.setupAccount();
62
+ const recipient = (await this.wallet.createAccount()).address;
52
63
  const token = await this.setupToken(defaultAccountAddress);
53
64
  await this.mintTokens(token, defaultAccountAddress);
54
65
  return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
55
66
  }
56
67
 
57
- public async setupAmm() {
68
+ public async setupAmm(): Promise<{
69
+ wallet: TestWallet;
70
+ defaultAccountAddress: AztecAddress;
71
+ amm: AMMContract;
72
+ token0: TokenContract;
73
+ token1: TokenContract;
74
+ node: AztecNode;
75
+ }> {
58
76
  const defaultAccountAddress = await this.setupAccount();
59
77
  const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
60
78
  const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
@@ -102,8 +120,8 @@ export class BotFactory {
102
120
  contract: new SchnorrAccountContract(signingKey!),
103
121
  };
104
122
  const accountManager = await this.wallet.createAccount(accountData);
105
- const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
106
- if (isInit) {
123
+ const metadata = await this.wallet.getContractMetadata(accountManager.address);
124
+ if (metadata.isContractInitialized) {
107
125
  this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
108
126
  const timer = new Timer();
109
127
  const address = accountManager.address;
@@ -118,12 +136,18 @@ export class BotFactory {
118
136
 
119
137
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
120
138
  const deployMethod = await accountManager.getDeployMethod();
121
- const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
139
+ const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
122
140
  const gasSettings = GasSettings.default({ maxFeesPerGas });
123
- const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
124
- const txHash = await sentTx.getTxHash();
125
- this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
126
- 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
+ });
127
151
  this.log.info(`Account deployed at ${address}`);
128
152
 
129
153
  // Clean up the consumed bridge claim
@@ -151,33 +175,49 @@ export class BotFactory {
151
175
  */
152
176
  private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
153
177
  let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
178
+ let tokenInstance: ContractInstanceWithAddress | undefined;
154
179
  const deployOpts: DeployOptions = {
155
180
  from: sender,
156
181
  contractAddressSalt: this.config.tokenSalt,
157
182
  universalDeploy: true,
158
183
  };
184
+ let token: TokenContract | PrivateTokenContract;
159
185
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
160
186
  deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
187
+ tokenInstance = await deploy.getInstance(deployOpts);
188
+ token = TokenContract.at(tokenInstance.address, this.wallet);
161
189
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
162
- 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);
163
194
  deployOpts.skipInstancePublication = true;
164
195
  deployOpts.skipClassPublication = true;
165
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);
166
202
  } else {
167
203
  throw new Error(`Unsupported token contract type: ${this.config.contract}`);
168
204
  }
169
205
 
170
- const address = (await deploy.getInstance(deployOpts)).address;
171
- 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) {
172
209
  this.log.info(`Token at ${address.toString()} already deployed`);
173
- return deploy.register();
210
+ await deploy.register();
174
211
  } else {
175
212
  this.log.info(`Deploying token contract at ${address.toString()}`);
176
- const sentTx = deploy.send(deployOpts);
177
- const txHash = await sentTx.getTxHash();
213
+ const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
178
214
  this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`);
179
- 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
+ });
180
219
  }
220
+ return token;
181
221
  }
182
222
 
183
223
  /**
@@ -185,7 +225,7 @@ export class BotFactory {
185
225
  * @param wallet - Wallet to deploy the token contract from.
186
226
  * @returns The TokenContract instance.
187
227
  */
188
- private setupTokenContract(
228
+ private async setupTokenContract(
189
229
  deployer: AztecAddress,
190
230
  contractAddressSalt: Fr,
191
231
  name: string,
@@ -194,7 +234,8 @@ export class BotFactory {
194
234
  ): Promise<TokenContract> {
195
235
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
196
236
  const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
197
- 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);
198
239
  }
199
240
 
200
241
  private async setupAmmContract(
@@ -206,12 +247,14 @@ export class BotFactory {
206
247
  ): Promise<AMMContract> {
207
248
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
208
249
  const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
209
- 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);
210
252
 
211
253
  this.log.info(`AMM deployed at ${amm.address}`);
212
- const minterTx = lpToken.methods.set_minter(amm.address, true).send({ from: deployer });
213
- this.log.info(`Set LP token minter to AMM txHash=${(await minterTx.getTxHash()).toString()}`);
214
- 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()}`);
215
258
  this.log.info(`Liquidity token initialized`);
216
259
 
217
260
  return amm;
@@ -270,23 +313,22 @@ export class BotFactory {
270
313
  .getFunctionCall(),
271
314
  });
272
315
 
273
- const mintTx = new BatchCall(this.wallet, [
316
+ const mintReceipt = await new BatchCall(this.wallet, [
274
317
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
275
318
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
276
- ]).send({ from: liquidityProvider });
319
+ ]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } });
277
320
 
278
- this.log.info(`Sent mint tx: ${(await mintTx.getTxHash()).toString()}`);
279
- await mintTx.wait({ timeout: this.config.txMinedWaitSeconds });
321
+ this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
280
322
 
281
- const addLiquidityTx = amm.methods
323
+ const addLiquidityReceipt = await amm.methods
282
324
  .add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce)
283
325
  .send({
284
326
  from: liquidityProvider,
285
327
  authWitnesses: [token0Authwit, token1Authwit],
328
+ wait: { timeout: this.config.txMinedWaitSeconds },
286
329
  });
287
330
 
288
- this.log.info(`Sent tx to add liquidity to the AMM: ${(await addLiquidityTx.getTxHash()).toString()}`);
289
- await addLiquidityTx.wait({ timeout: this.config.txMinedWaitSeconds });
331
+ this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
290
332
  this.log.info(`Liquidity added`);
291
333
 
292
334
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
@@ -299,18 +341,22 @@ export class BotFactory {
299
341
  name: string,
300
342
  deploy: DeployMethod<T>,
301
343
  deployOpts: DeployOptions,
302
- ): Promise<T> {
303
- const address = (await deploy.getInstance(deployOpts)).address;
304
- 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) {
305
349
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
306
- return deploy.register();
350
+ await deploy.register();
307
351
  } else {
308
352
  this.log.info(`Deploying contract ${name} at ${address.toString()}`);
309
- const sentTx = deploy.send(deployOpts);
310
- const txHash = await sentTx.getTxHash();
311
- this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
312
- 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
+ });
313
358
  }
359
+ return instance;
314
360
  }
315
361
 
316
362
  /**
@@ -346,10 +392,12 @@ export class BotFactory {
346
392
  this.log.info(`Skipping minting as ${minter.toString()} has enough tokens`);
347
393
  return;
348
394
  }
349
- const sentTx = new BatchCall(token.wallet, calls).send({ from: minter });
350
- const txHash = await sentTx.getTxHash();
351
- this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
352
- 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
+ });
353
401
  }
354
402
 
355
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