@aztec/bot 2.1.0-rc.9 → 3.0.0-devnet.2

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.
Files changed (45) hide show
  1. package/dest/amm_bot.d.ts +8 -8
  2. package/dest/amm_bot.d.ts.map +1 -1
  3. package/dest/amm_bot.js +23 -20
  4. package/dest/base_bot.d.ts +10 -7
  5. package/dest/base_bot.d.ts.map +1 -1
  6. package/dest/base_bot.js +18 -18
  7. package/dest/bot.d.ts +7 -8
  8. package/dest/bot.d.ts.map +1 -1
  9. package/dest/bot.js +11 -12
  10. package/dest/config.d.ts +19 -15
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +22 -20
  13. package/dest/factory.d.ts +18 -17
  14. package/dest/factory.d.ts.map +1 -1
  15. package/dest/factory.js +122 -102
  16. package/dest/index.d.ts +1 -0
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +1 -0
  19. package/dest/interface.d.ts +1 -1
  20. package/dest/interface.d.ts.map +1 -1
  21. package/dest/interface.js +1 -1
  22. package/dest/runner.d.ts +11 -12
  23. package/dest/runner.d.ts.map +1 -1
  24. package/dest/runner.js +18 -32
  25. package/dest/store/bot_store.d.ts +44 -0
  26. package/dest/store/bot_store.d.ts.map +1 -0
  27. package/dest/store/bot_store.js +107 -0
  28. package/dest/store/index.d.ts +2 -0
  29. package/dest/store/index.d.ts.map +1 -0
  30. package/dest/store/index.js +1 -0
  31. package/dest/utils.d.ts +3 -3
  32. package/dest/utils.d.ts.map +1 -1
  33. package/dest/utils.js +5 -5
  34. package/package.json +13 -11
  35. package/src/amm_bot.ts +39 -31
  36. package/src/base_bot.ts +24 -22
  37. package/src/bot.ts +25 -14
  38. package/src/config.ts +20 -22
  39. package/src/factory.ts +138 -140
  40. package/src/index.ts +1 -0
  41. package/src/interface.ts +1 -1
  42. package/src/runner.ts +19 -23
  43. package/src/store/bot_store.ts +141 -0
  44. package/src/store/index.ts +1 -0
  45. package/src/utils.ts +10 -5
package/src/config.ts CHANGED
@@ -6,12 +6,14 @@ import {
6
6
  getDefaultConfig,
7
7
  numberConfigHelper,
8
8
  optionalNumberConfigHelper,
9
+ pickConfigMappings,
9
10
  secretFrConfigHelper,
10
11
  secretStringConfigHelper,
11
12
  } from '@aztec/foundation/config';
12
13
  import { Fr } from '@aztec/foundation/fields';
14
+ import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
13
15
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
14
- import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
16
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
15
17
  import { type ZodFor, schemas } from '@aztec/stdlib/schemas';
16
18
  import type { ComponentsVersions } from '@aztec/stdlib/versioning';
17
19
 
@@ -30,8 +32,6 @@ export type BotConfig = {
30
32
  nodeUrl: string | undefined;
31
33
  /** The URL to the Aztec node admin API to force-flush txs if configured. */
32
34
  nodeAdminUrl: string | undefined;
33
- /** URL to the PXE for sending txs, or undefined if an in-proc PXE is used. */
34
- pxeUrl: string | undefined;
35
35
  /** Url of the ethereum host. */
36
36
  l1RpcUrls: string[] | undefined;
37
37
  /** The mnemonic for the account to bridge fee juice from L1. */
@@ -44,8 +44,6 @@ export type BotConfig = {
44
44
  senderPrivateKey: SecretValue<Fr> | undefined;
45
45
  /** Optional salt to use to instantiate the sender account */
46
46
  senderSalt: Fr | undefined;
47
- /** Encryption secret for a recipient account. */
48
- recipientEncryptionSecret: SecretValue<Fr>;
49
47
  /** Salt for the token contract instantiation. */
50
48
  tokenSalt: Fr;
51
49
  /** Every how many seconds should a new tx be sent. */
@@ -56,6 +54,8 @@ export type BotConfig = {
56
54
  publicTransfersPerTx: number;
57
55
  /** How to handle fee payments. */
58
56
  feePaymentMethod: 'fee_juice';
57
+ /** 'How much is the bot willing to overpay vs. the current base fee' */
58
+ baseFeePadding: 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. */
@@ -78,25 +78,24 @@ export type BotConfig = {
78
78
  stopWhenUnhealthy: boolean;
79
79
  /** Deploy an AMM contract and do swaps instead of transfers */
80
80
  ammTxs: boolean;
81
- };
81
+ } & Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb'>;
82
82
 
83
83
  export const BotConfigSchema = z
84
84
  .object({
85
85
  nodeUrl: z.string().optional(),
86
86
  nodeAdminUrl: z.string().optional(),
87
- pxeUrl: z.string().optional(),
88
87
  l1RpcUrls: z.array(z.string()).optional(),
89
88
  l1Mnemonic: schemas.SecretValue(z.string()).optional(),
90
89
  l1PrivateKey: schemas.SecretValue(z.string()).optional(),
91
90
  l1ToL2MessageTimeoutSeconds: z.number(),
92
91
  senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
93
92
  senderSalt: schemas.Fr.optional(),
94
- recipientEncryptionSecret: schemas.SecretValue(schemas.Fr),
95
93
  tokenSalt: schemas.Fr,
96
94
  txIntervalSeconds: z.number(),
97
95
  privateTransfersPerTx: z.number().int().nonnegative(),
98
96
  publicTransfersPerTx: z.number().int().nonnegative(),
99
97
  feePaymentMethod: z.literal('fee_juice'),
98
+ baseFeePadding: z.number().int().nonnegative(),
100
99
  noStart: z.boolean(),
101
100
  txMinedWaitSeconds: z.number(),
102
101
  followChain: z.enum(BotFollowChain),
@@ -108,11 +107,12 @@ export const BotConfigSchema = z
108
107
  maxConsecutiveErrors: z.number().int().nonnegative(),
109
108
  stopWhenUnhealthy: z.boolean(),
110
109
  ammTxs: z.boolean().default(false),
110
+ dataDirectory: z.string().optional(),
111
+ dataStoreMapSizeKb: z.number().optional(),
111
112
  })
112
113
  .transform(config => ({
113
114
  nodeUrl: undefined,
114
115
  nodeAdminUrl: undefined,
115
- pxeUrl: undefined,
116
116
  l1RpcUrls: undefined,
117
117
  senderSalt: undefined,
118
118
  l2GasLimit: undefined,
@@ -120,6 +120,8 @@ export const BotConfigSchema = z
120
120
  l1Mnemonic: undefined,
121
121
  l1PrivateKey: undefined,
122
122
  senderPrivateKey: undefined,
123
+ dataDirectory: undefined,
124
+ dataStoreMapSizeKb: 1_024 * 1_024,
123
125
  ...config,
124
126
  })) satisfies ZodFor<BotConfig>;
125
127
 
@@ -132,10 +134,6 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
132
134
  env: 'AZTEC_NODE_ADMIN_URL',
133
135
  description: 'The URL to the Aztec node admin API to force-flush txs if configured.',
134
136
  },
135
- pxeUrl: {
136
- env: 'BOT_PXE_URL',
137
- description: 'URL to the PXE for sending txs, or undefined if an in-proc PXE is used.',
138
- },
139
137
  l1RpcUrls: {
140
138
  env: 'ETHEREUM_HOSTS',
141
139
  description: 'URL of the ethereum host.',
@@ -163,18 +161,12 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
163
161
  },
164
162
  senderSalt: {
165
163
  env: 'BOT_ACCOUNT_SALT',
166
- description: 'The salt to use to deploys the sender account.',
164
+ description: 'The salt to use to deploy the sender account.',
167
165
  parseEnv: (val: string) => (val ? Fr.fromHexString(val) : undefined),
168
166
  },
169
- recipientEncryptionSecret: {
170
- env: 'BOT_RECIPIENT_ENCRYPTION_SECRET',
171
- description: 'Encryption secret for a recipient account.',
172
- printDefault: sv => sv?.getValue(),
173
- ...secretFrConfigHelper(Fr.fromHexString('0xcafecafe')),
174
- },
175
167
  tokenSalt: {
176
168
  env: 'BOT_TOKEN_SALT',
177
- description: 'Salt for the token contract deployment.',
169
+ description: 'The salt to use to deploy the token contract.',
178
170
  parseEnv: (val: string) => Fr.fromHexString(val),
179
171
  defaultValue: Fr.fromHexString('1'),
180
172
  },
@@ -199,6 +191,11 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
199
191
  parseEnv: val => (val as 'fee_juice') || undefined,
200
192
  defaultValue: 'fee_juice',
201
193
  },
194
+ baseFeePadding: {
195
+ env: 'BOT_BASE_FEE_PADDING',
196
+ description: 'How much is the bot willing to overpay vs. the current base fee',
197
+ ...numberConfigHelper(3),
198
+ },
202
199
  noStart: {
203
200
  env: 'BOT_NO_START',
204
201
  description: 'True to not automatically setup or start the bot on initialization.',
@@ -270,6 +267,7 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
270
267
  description: 'Deploy an AMM and send swaps to it',
271
268
  ...booleanConfigHelper(false),
272
269
  },
270
+ ...pickConfigMappings(dataConfigMappings, ['dataStoreMapSizeKb', 'dataDirectory']),
273
271
  };
274
272
 
275
273
  export function getBotConfigFromEnv(): BotConfig {
@@ -282,7 +280,7 @@ export function getBotDefaultConfig(): BotConfig {
282
280
 
283
281
  export function getVersions(): Partial<ComponentsVersions> {
284
282
  return {
285
- l2ProtocolContractsTreeRoot: protocolContractTreeRoot.toString(),
283
+ l2ProtocolContractsHash: protocolContractsHash.toString(),
286
284
  l2CircuitsVkTreeRoot: getVKTreeRoot().toString(),
287
285
  };
288
286
  }
package/src/factory.ts CHANGED
@@ -1,122 +1,81 @@
1
- import { getSchnorrAccount } from '@aztec/accounts/schnorr';
2
- import { getDeployedTestAccountsWallets, getInitialTestAccounts } from '@aztec/accounts/testing';
1
+ import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
2
+ import { getInitialTestAccountsData } from '@aztec/accounts/testing';
3
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
3
4
  import {
4
- type AccountWallet,
5
- AztecAddress,
6
5
  BatchCall,
7
6
  ContractBase,
8
7
  ContractFunctionInteraction,
9
8
  type DeployMethod,
10
9
  type DeployOptions,
11
- FeeJuicePaymentMethodWithClaim,
12
- L1FeeJuicePortalManager,
13
- type PXE,
14
- createLogger,
15
- createPXEClient,
16
- waitForL1ToL2MessageReady,
17
- } from '@aztec/aztec.js';
10
+ } from '@aztec/aztec.js/contracts';
11
+ import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
12
+ import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
13
+ import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
14
+ import { createLogger } from '@aztec/aztec.js/log';
15
+ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
18
16
  import { createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
19
17
  import { Fr } from '@aztec/foundation/fields';
20
18
  import { Timer } from '@aztec/foundation/timer';
21
19
  import { AMMContract } from '@aztec/noir-contracts.js/AMM';
22
20
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
23
21
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
22
+ import { GasSettings } from '@aztec/stdlib/gas';
24
23
  import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
25
24
  import { deriveSigningKey } from '@aztec/stdlib/keys';
26
- import { makeTracedFetch } from '@aztec/telemetry-client';
25
+ import { TestWallet } from '@aztec/test-wallet/server';
27
26
 
28
- import { type BotConfig, SupportedTokenContracts, getVersions } from './config.js';
27
+ import { type BotConfig, SupportedTokenContracts } from './config.js';
28
+ import type { BotStore } from './store/index.js';
29
29
  import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
30
30
 
31
31
  const MINT_BALANCE = 1e12;
32
32
  const MIN_BALANCE = 1e3;
33
33
 
34
34
  export class BotFactory {
35
- private pxe: PXE;
36
- private node?: AztecNode;
37
- private nodeAdmin?: AztecNodeAdmin;
38
35
  private log = createLogger('bot');
39
36
 
40
37
  constructor(
41
38
  private readonly config: BotConfig,
42
- dependencies: { pxe?: PXE; nodeAdmin?: AztecNodeAdmin; node?: AztecNode },
43
- ) {
44
- if (config.flushSetupTransactions && !dependencies.nodeAdmin) {
45
- throw new Error(
46
- `Either a node admin client or node admin url must be provided if transaction flushing is requested`,
47
- );
48
- }
49
- if (config.senderPrivateKey && config.senderPrivateKey.getValue() && !dependencies.node) {
50
- throw new Error(
51
- `Either a node client or node url must be provided for bridging L1 fee juice to deploy an account with private key`,
52
- );
53
- }
54
- if (!dependencies.pxe && !config.pxeUrl) {
55
- throw new Error(`Either a PXE client or a PXE URL must be provided`);
56
- }
57
-
58
- this.node = dependencies.node;
59
- this.nodeAdmin = dependencies.nodeAdmin;
60
-
61
- if (dependencies.pxe) {
62
- this.log.info(`Using local PXE`);
63
- this.pxe = dependencies.pxe;
64
- return;
65
- }
66
- this.log.info(`Using remote PXE at ${config.pxeUrl!}`);
67
- this.pxe = createPXEClient(config.pxeUrl!, getVersions(), makeTracedFetch([1, 2, 3], false));
68
- }
39
+ private readonly wallet: TestWallet,
40
+ private readonly store: BotStore,
41
+ private readonly aztecNode: AztecNode,
42
+ private readonly aztecNodeAdmin?: AztecNodeAdmin,
43
+ ) {}
69
44
 
70
45
  /**
71
46
  * Initializes a new bot by setting up the sender account, registering the recipient,
72
47
  * deploying the token contract, and minting tokens if necessary.
73
48
  */
74
49
  public async setup() {
75
- const recipient = await this.registerRecipient();
76
- const wallet = await this.setupAccount();
77
- const defaultAccountAddress = wallet.getAddress();
78
- const token = await this.setupToken(wallet, defaultAccountAddress);
50
+ const recipient = (await this.wallet.createAccount()).address;
51
+ const defaultAccountAddress = await this.setupAccount();
52
+ const token = await this.setupToken(defaultAccountAddress);
79
53
  await this.mintTokens(token, defaultAccountAddress);
80
- return { wallet, defaultAccountAddress, token, pxe: this.pxe, recipient };
54
+ return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
81
55
  }
82
56
 
83
57
  public async setupAmm() {
84
- const wallet = await this.setupAccount();
85
- const defaultAccountAddress = wallet.getAddress();
86
- const token0 = await this.setupTokenContract(
87
- wallet,
88
- wallet.getAddress(),
89
- this.config.tokenSalt,
90
- 'BotToken0',
91
- 'BOT0',
92
- );
93
- const token1 = await this.setupTokenContract(
94
- wallet,
95
- wallet.getAddress(),
96
- this.config.tokenSalt,
97
- 'BotToken1',
98
- 'BOT1',
99
- );
58
+ const defaultAccountAddress = await this.setupAccount();
59
+ const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
60
+ const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
100
61
  const liquidityToken = await this.setupTokenContract(
101
- wallet,
102
- wallet.getAddress(),
62
+ defaultAccountAddress,
103
63
  this.config.tokenSalt,
104
64
  'BotLPToken',
105
65
  'BOTLP',
106
66
  );
107
67
  const amm = await this.setupAmmContract(
108
- wallet,
109
- wallet.getAddress(),
68
+ defaultAccountAddress,
110
69
  this.config.tokenSalt,
111
70
  token0,
112
71
  token1,
113
72
  liquidityToken,
114
73
  );
115
74
 
116
- await this.fundAmm(wallet, wallet.getAddress(), amm, token0, token1, liquidityToken);
75
+ await this.fundAmm(defaultAccountAddress, defaultAccountAddress, amm, token0, token1, liquidityToken);
117
76
  this.log.info(`AMM initialized and funded`);
118
77
 
119
- return { wallet, defaultAccountAddress, amm, token0, token1, pxe: this.pxe };
78
+ return { wallet: this.wallet, defaultAccountAddress, amm, token0, token1, node: this.aztecNode };
120
79
  }
121
80
 
122
81
  /**
@@ -126,62 +85,63 @@ export class BotFactory {
126
85
  private async setupAccount() {
127
86
  const privateKey = this.config.senderPrivateKey?.getValue();
128
87
  if (privateKey) {
88
+ this.log.info(`Setting up account with provided private key`);
129
89
  return await this.setupAccountWithPrivateKey(privateKey);
130
90
  } else {
91
+ this.log.info(`Setting up test account`);
131
92
  return await this.setupTestAccount();
132
93
  }
133
94
  }
134
95
 
135
- private async setupAccountWithPrivateKey(privateKey: Fr) {
96
+ private async setupAccountWithPrivateKey(secret: Fr) {
136
97
  const salt = this.config.senderSalt ?? Fr.ONE;
137
- const signingKey = deriveSigningKey(privateKey);
138
- const account = await getSchnorrAccount(this.pxe, privateKey, signingKey, salt);
139
- const isInit = (await this.pxe.getContractMetadata(account.getAddress())).isContractInitialized;
98
+ const signingKey = deriveSigningKey(secret);
99
+ const accountData = {
100
+ secret,
101
+ salt,
102
+ contract: new SchnorrAccountContract(signingKey!),
103
+ };
104
+ const accountManager = await this.wallet.createAccount(accountData);
105
+ const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
140
106
  if (isInit) {
141
- this.log.info(`Account at ${account.getAddress().toString()} already initialized`);
107
+ this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
142
108
  const timer = new Timer();
143
- const wallet = await account.register();
144
- this.log.info(`Account at ${account.getAddress()} registered. duration=${timer.ms()}`);
145
- return wallet;
109
+ const address = accountManager.address;
110
+ this.log.info(`Account at ${address} registered. duration=${timer.ms()}`);
111
+ await this.store.deleteBridgeClaim(address);
112
+ return address;
146
113
  } else {
147
- const address = account.getAddress();
114
+ const address = accountManager.address;
148
115
  this.log.info(`Deploying account at ${address}`);
149
116
 
150
- const claim = await this.bridgeL1FeeJuice(address);
117
+ const claim = await this.getOrCreateBridgeClaim(address);
151
118
 
152
- // docs:start:claim_and_deploy
153
- const wallet = await account.getWallet();
154
- const paymentMethod = new FeeJuicePaymentMethodWithClaim(wallet, claim);
155
- const sentTx = account.deploy({ fee: { paymentMethod } });
119
+ const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
120
+ const deployMethod = await accountManager.getDeployMethod();
121
+ const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
122
+ const gasSettings = GasSettings.default({ maxFeesPerGas });
123
+ const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
156
124
  const txHash = await sentTx.getTxHash();
157
- // docs:end:claim_and_deploy
158
125
  this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
159
126
  await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
160
127
  this.log.info(`Account deployed at ${address}`);
161
- return wallet;
162
- }
163
- }
164
128
 
165
- private async setupTestAccount() {
166
- let [wallet] = await getDeployedTestAccountsWallets(this.pxe);
167
- if (wallet) {
168
- this.log.info(`Using funded test account: ${wallet.getAddress()}`);
169
- } else {
170
- this.log.info('Registering funded test account');
171
- const [account] = await getInitialTestAccounts();
172
- const manager = await getSchnorrAccount(this.pxe, account.secret, account.signingKey, account.salt);
173
- wallet = await manager.register();
174
- this.log.info(`Funded test account registered: ${wallet.getAddress()}`);
129
+ // Clean up the consumed bridge claim
130
+ await this.store.deleteBridgeClaim(address);
131
+
132
+ return accountManager.address;
175
133
  }
176
- return wallet;
177
134
  }
178
135
 
179
- /**
180
- * Registers the recipient for txs in the pxe.
181
- */
182
- private async registerRecipient() {
183
- const recipient = await this.pxe.registerAccount(this.config.recipientEncryptionSecret.getValue(), Fr.ONE);
184
- return recipient.address;
136
+ private async setupTestAccount() {
137
+ const [initialAccountData] = await getInitialTestAccountsData();
138
+ const accountData = {
139
+ secret: initialAccountData.secret,
140
+ salt: initialAccountData.salt,
141
+ contract: new SchnorrAccountContract(initialAccountData.signingKey),
142
+ };
143
+ const accountManager = await this.wallet.createAccount(accountData);
144
+ return accountManager.address;
185
145
  }
186
146
 
187
147
  /**
@@ -189,7 +149,7 @@ export class BotFactory {
189
149
  * @param wallet - Wallet to deploy the token contract from.
190
150
  * @returns The TokenContract instance.
191
151
  */
192
- private async setupToken(wallet: AccountWallet, sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
152
+ private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
193
153
  let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
194
154
  const deployOpts: DeployOptions = {
195
155
  from: sender,
@@ -197,9 +157,9 @@ export class BotFactory {
197
157
  universalDeploy: true,
198
158
  };
199
159
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
200
- deploy = TokenContract.deploy(wallet, sender, 'BotToken', 'BOT', 18);
160
+ deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
201
161
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
202
- deploy = PrivateTokenContract.deploy(wallet, MINT_BALANCE, sender);
162
+ deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender);
203
163
  deployOpts.skipInstancePublication = true;
204
164
  deployOpts.skipClassPublication = true;
205
165
  deployOpts.skipInitialization = false;
@@ -208,7 +168,7 @@ export class BotFactory {
208
168
  }
209
169
 
210
170
  const address = (await deploy.getInstance(deployOpts)).address;
211
- if ((await this.pxe.getContractMetadata(address)).isContractPublished) {
171
+ if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
212
172
  this.log.info(`Token at ${address.toString()} already deployed`);
213
173
  return deploy.register();
214
174
  } else {
@@ -226,7 +186,6 @@ export class BotFactory {
226
186
  * @returns The TokenContract instance.
227
187
  */
228
188
  private setupTokenContract(
229
- wallet: AccountWallet,
230
189
  deployer: AztecAddress,
231
190
  contractAddressSalt: Fr,
232
191
  name: string,
@@ -234,12 +193,11 @@ export class BotFactory {
234
193
  decimals = 18,
235
194
  ): Promise<TokenContract> {
236
195
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
237
- const deploy = TokenContract.deploy(wallet, deployer, name, ticker, decimals);
196
+ const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
238
197
  return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
239
198
  }
240
199
 
241
200
  private async setupAmmContract(
242
- wallet: AccountWallet,
243
201
  deployer: AztecAddress,
244
202
  contractAddressSalt: Fr,
245
203
  token0: TokenContract,
@@ -247,7 +205,7 @@ export class BotFactory {
247
205
  lpToken: TokenContract,
248
206
  ): Promise<AMMContract> {
249
207
  const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
250
- const deploy = AMMContract.deploy(wallet, token0.address, token1.address, lpToken.address);
208
+ const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
251
209
  const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
252
210
 
253
211
  this.log.info(`AMM deployed at ${amm.address}`);
@@ -260,7 +218,7 @@ export class BotFactory {
260
218
  }
261
219
 
262
220
  private async fundAmm(
263
- wallet: AccountWallet,
221
+ defaultAccountAddress: AztecAddress,
264
222
  liquidityProvider: AztecAddress,
265
223
  amm: AMMContract,
266
224
  token0: TokenContract,
@@ -289,26 +247,30 @@ export class BotFactory {
289
247
  );
290
248
 
291
249
  // Add authwitnesses for the transfers in AMM::add_liquidity function
292
- const token0Authwit = await wallet.createAuthWit({
250
+ const token0Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
293
251
  caller: amm.address,
294
- action: token0.methods.transfer_to_public_and_prepare_private_balance_increase(
295
- liquidityProvider,
296
- amm.address,
297
- amount0Max,
298
- authwitNonce,
299
- ),
252
+ call: await token0.methods
253
+ .transfer_to_public_and_prepare_private_balance_increase(
254
+ liquidityProvider,
255
+ amm.address,
256
+ amount0Max,
257
+ authwitNonce,
258
+ )
259
+ .getFunctionCall(),
300
260
  });
301
- const token1Authwit = await wallet.createAuthWit({
261
+ const token1Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
302
262
  caller: amm.address,
303
- action: token1.methods.transfer_to_public_and_prepare_private_balance_increase(
304
- liquidityProvider,
305
- amm.address,
306
- amount1Max,
307
- authwitNonce,
308
- ),
263
+ call: await token1.methods
264
+ .transfer_to_public_and_prepare_private_balance_increase(
265
+ liquidityProvider,
266
+ amm.address,
267
+ amount1Max,
268
+ authwitNonce,
269
+ )
270
+ .getFunctionCall(),
309
271
  });
310
272
 
311
- const mintTx = new BatchCall(wallet, [
273
+ const mintTx = new BatchCall(this.wallet, [
312
274
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
313
275
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
314
276
  ]).send({ from: liquidityProvider });
@@ -329,7 +291,7 @@ export class BotFactory {
329
291
 
330
292
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
331
293
  this.log.info(
332
- `Updated private balances of ${wallet.getAddress()} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`,
294
+ `Updated private balances of ${defaultAccountAddress} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`,
333
295
  );
334
296
  }
335
297
 
@@ -339,7 +301,7 @@ export class BotFactory {
339
301
  deployOpts: DeployOptions,
340
302
  ): Promise<T> {
341
303
  const address = (await deploy.getInstance(deployOpts)).address;
342
- if ((await this.pxe.getContractMetadata(address)).isContractPublished) {
304
+ if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
343
305
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
344
306
  return deploy.register();
345
307
  } else {
@@ -390,7 +352,40 @@ export class BotFactory {
390
352
  await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
391
353
  }
392
354
 
393
- private async bridgeL1FeeJuice(recipient: AztecAddress) {
355
+ /**
356
+ * Gets or creates a bridge claim for the recipient.
357
+ * Checks if a claim already exists in the store and reuses it if valid.
358
+ * Only creates a new bridge if fee juice balance is below threshold.
359
+ */
360
+ private async getOrCreateBridgeClaim(recipient: AztecAddress): Promise<L2AmountClaim> {
361
+ // Check if we have an existing claim in the store
362
+ const existingClaim = await this.store.getBridgeClaim(recipient);
363
+ if (existingClaim) {
364
+ this.log.info(`Found existing bridge claim for ${recipient.toString()}, checking validity...`);
365
+
366
+ // Check if the message is ready on L2
367
+ try {
368
+ const messageHash = Fr.fromHexString(existingClaim.claim.messageHash);
369
+ await this.withNoMinTxsPerBlock(() =>
370
+ waitForL1ToL2MessageReady(this.aztecNode, messageHash, {
371
+ timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
372
+ forPublicConsumption: false,
373
+ }),
374
+ );
375
+ return existingClaim.claim;
376
+ } catch (err) {
377
+ this.log.warn(`Failed to verify existing claim, creating new one: ${err}`);
378
+ await this.store.deleteBridgeClaim(recipient);
379
+ }
380
+ }
381
+
382
+ const claim = await this.bridgeL1FeeJuice(recipient);
383
+ await this.store.saveBridgeClaim(recipient, claim);
384
+
385
+ return claim;
386
+ }
387
+
388
+ private async bridgeL1FeeJuice(recipient: AztecAddress): Promise<L2AmountClaim> {
394
389
  const l1RpcUrls = this.config.l1RpcUrls;
395
390
  if (!l1RpcUrls?.length) {
396
391
  throw new Error('L1 Rpc url is required to bridge the fee juice to fund the deployment of the account.');
@@ -402,16 +397,16 @@ export class BotFactory {
402
397
  );
403
398
  }
404
399
 
405
- const { l1ChainId } = await this.pxe.getNodeInfo();
400
+ const { l1ChainId } = await this.aztecNode.getNodeInfo();
406
401
  const chain = createEthereumChain(l1RpcUrls, l1ChainId);
407
402
  const extendedClient = createExtendedL1Client(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
408
403
 
409
- const portal = await L1FeeJuicePortalManager.new(this.pxe, extendedClient, this.log);
404
+ const portal = await L1FeeJuicePortalManager.new(this.aztecNode, extendedClient, this.log);
410
405
  const mintAmount = await portal.getTokenManager().getMintAmount();
411
406
  const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true /* mint */);
412
407
 
413
408
  await this.withNoMinTxsPerBlock(() =>
414
- waitForL1ToL2MessageReady(this.pxe, Fr.fromHexString(claim.messageHash), {
409
+ waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(claim.messageHash), {
415
410
  timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
416
411
  forPublicConsumption: false,
417
412
  }),
@@ -419,19 +414,22 @@ export class BotFactory {
419
414
 
420
415
  this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
421
416
 
422
- return claim;
417
+ return claim as L2AmountClaim;
423
418
  }
424
419
 
425
420
  private async withNoMinTxsPerBlock<T>(fn: () => Promise<T>): Promise<T> {
426
- if (!this.nodeAdmin || !this.config.flushSetupTransactions) {
421
+ if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
422
+ this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
427
423
  return fn();
428
424
  }
429
- const { minTxsPerBlock } = await this.nodeAdmin.getConfig();
430
- await this.nodeAdmin.setConfig({ minTxsPerBlock: 0 });
425
+ const { minTxsPerBlock } = await this.aztecNodeAdmin.getConfig();
426
+ this.log.warn(`Setting sequencer minTxsPerBlock to 0 from ${minTxsPerBlock} to flush setup transactions`);
427
+ await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 });
431
428
  try {
432
429
  return await fn();
433
430
  } finally {
434
- await this.nodeAdmin.setConfig({ minTxsPerBlock });
431
+ this.log.warn(`Restoring sequencer minTxsPerBlock to ${minTxsPerBlock}`);
432
+ await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
435
433
  }
436
434
  }
437
435
  }
package/src/index.ts CHANGED
@@ -1,6 +1,7 @@
1
1
  export { Bot } from './bot.js';
2
2
  export { AmmBot } from './amm_bot.js';
3
3
  export { BotRunner } from './runner.js';
4
+ export { BotStore } from './store/bot_store.js';
4
5
  export {
5
6
  type BotConfig,
6
7
  getBotConfigFromEnv,
package/src/interface.ts CHANGED
@@ -1,4 +1,4 @@
1
- import { AztecAddress } from '@aztec/aztec.js';
1
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
2
2
  import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
3
3
 
4
4
  import { z } from 'zod';