@aztec/bot 4.0.0-nightly.20250907 → 4.0.0-nightly.20260108

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 (46) hide show
  1. package/dest/amm_bot.d.ts +9 -9
  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 +11 -8
  5. package/dest/base_bot.d.ts.map +1 -1
  6. package/dest/base_bot.js +18 -18
  7. package/dest/bot.d.ts +8 -9
  8. package/dest/bot.d.ts.map +1 -1
  9. package/dest/bot.js +11 -12
  10. package/dest/config.d.ts +53 -50
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +27 -25
  13. package/dest/factory.d.ts +14 -32
  14. package/dest/factory.d.ts.map +1 -1
  15. package/dest/factory.js +137 -115
  16. package/dest/index.d.ts +2 -1
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +1 -0
  19. package/dest/interface.d.ts +2 -2
  20. package/dest/interface.d.ts.map +1 -1
  21. package/dest/interface.js +1 -1
  22. package/dest/rpc.d.ts +1 -1
  23. package/dest/runner.d.ts +12 -13
  24. package/dest/runner.d.ts.map +1 -1
  25. package/dest/runner.js +429 -61
  26. package/dest/store/bot_store.d.ts +44 -0
  27. package/dest/store/bot_store.d.ts.map +1 -0
  28. package/dest/store/bot_store.js +107 -0
  29. package/dest/store/index.d.ts +2 -0
  30. package/dest/store/index.d.ts.map +1 -0
  31. package/dest/store/index.js +1 -0
  32. package/dest/utils.d.ts +4 -4
  33. package/dest/utils.d.ts.map +1 -1
  34. package/dest/utils.js +5 -5
  35. package/package.json +16 -13
  36. package/src/amm_bot.ts +39 -31
  37. package/src/base_bot.ts +24 -22
  38. package/src/bot.ts +25 -14
  39. package/src/config.ts +64 -64
  40. package/src/factory.ts +172 -157
  41. package/src/index.ts +1 -0
  42. package/src/interface.ts +1 -1
  43. package/src/runner.ts +19 -23
  44. package/src/store/bot_store.ts +141 -0
  45. package/src/store/index.ts +1 -0
  46. package/src/utils.ts +10 -5
package/dest/config.js CHANGED
@@ -1,8 +1,9 @@
1
- import { booleanConfigHelper, getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, secretFrConfigHelper, secretStringConfigHelper } from '@aztec/foundation/config';
2
- import { Fr } from '@aztec/foundation/fields';
1
+ import { booleanConfigHelper, getConfigFromMappings, getDefaultConfig, numberConfigHelper, optionalNumberConfigHelper, pickConfigMappings, secretFrConfigHelper, secretStringConfigHelper } from '@aztec/foundation/config';
2
+ import { Fr } from '@aztec/foundation/curves/bn254';
3
+ import { dataConfigMappings } from '@aztec/kv-store/config';
3
4
  import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
4
- import { protocolContractTreeRoot } from '@aztec/protocol-contracts';
5
- import { schemas } from '@aztec/stdlib/schemas';
5
+ import { protocolContractsHash } from '@aztec/protocol-contracts';
6
+ import { schemas, zodFor } from '@aztec/stdlib/schemas';
6
7
  import { z } from 'zod';
7
8
  const BotFollowChain = [
8
9
  'NONE',
@@ -14,22 +15,21 @@ export var SupportedTokenContracts = /*#__PURE__*/ function(SupportedTokenContra
14
15
  SupportedTokenContracts["PrivateTokenContract"] = "PrivateTokenContract";
15
16
  return SupportedTokenContracts;
16
17
  }({});
17
- export const BotConfigSchema = z.object({
18
+ export const BotConfigSchema = zodFor()(z.object({
18
19
  nodeUrl: z.string().optional(),
19
20
  nodeAdminUrl: z.string().optional(),
20
- pxeUrl: z.string().optional(),
21
21
  l1RpcUrls: z.array(z.string()).optional(),
22
22
  l1Mnemonic: schemas.SecretValue(z.string()).optional(),
23
23
  l1PrivateKey: schemas.SecretValue(z.string()).optional(),
24
24
  l1ToL2MessageTimeoutSeconds: z.number(),
25
25
  senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
26
26
  senderSalt: schemas.Fr.optional(),
27
- recipientEncryptionSecret: schemas.SecretValue(schemas.Fr),
28
27
  tokenSalt: schemas.Fr,
29
28
  txIntervalSeconds: z.number(),
30
29
  privateTransfersPerTx: z.number().int().nonnegative(),
31
30
  publicTransfersPerTx: z.number().int().nonnegative(),
32
31
  feePaymentMethod: z.literal('fee_juice'),
32
+ minFeePadding: z.number().int().nonnegative(),
33
33
  noStart: z.boolean(),
34
34
  txMinedWaitSeconds: z.number(),
35
35
  followChain: z.enum(BotFollowChain),
@@ -40,11 +40,12 @@ export const BotConfigSchema = z.object({
40
40
  contract: z.nativeEnum(SupportedTokenContracts),
41
41
  maxConsecutiveErrors: z.number().int().nonnegative(),
42
42
  stopWhenUnhealthy: z.boolean(),
43
- ammTxs: z.boolean().default(false)
43
+ ammTxs: z.boolean().default(false),
44
+ dataDirectory: z.string().optional(),
45
+ dataStoreMapSizeKb: z.number().optional()
44
46
  }).transform((config)=>({
45
47
  nodeUrl: undefined,
46
48
  nodeAdminUrl: undefined,
47
- pxeUrl: undefined,
48
49
  l1RpcUrls: undefined,
49
50
  senderSalt: undefined,
50
51
  l2GasLimit: undefined,
@@ -52,8 +53,10 @@ export const BotConfigSchema = z.object({
52
53
  l1Mnemonic: undefined,
53
54
  l1PrivateKey: undefined,
54
55
  senderPrivateKey: undefined,
56
+ dataDirectory: undefined,
57
+ dataStoreMapSizeKb: 1_024 * 1_024,
55
58
  ...config
56
- }));
59
+ })));
57
60
  export const botConfigMappings = {
58
61
  nodeUrl: {
59
62
  env: 'AZTEC_NODE_URL',
@@ -63,10 +66,6 @@ export const botConfigMappings = {
63
66
  env: 'AZTEC_NODE_ADMIN_URL',
64
67
  description: 'The URL to the Aztec node admin API to force-flush txs if configured.'
65
68
  },
66
- pxeUrl: {
67
- env: 'BOT_PXE_URL',
68
- description: 'URL to the PXE for sending txs, or undefined if an in-proc PXE is used.'
69
- },
70
69
  l1RpcUrls: {
71
70
  env: 'ETHEREUM_HOSTS',
72
71
  description: 'URL of the ethereum host.',
@@ -85,7 +84,7 @@ export const botConfigMappings = {
85
84
  l1ToL2MessageTimeoutSeconds: {
86
85
  env: 'BOT_L1_TO_L2_TIMEOUT_SECONDS',
87
86
  description: 'How long to wait for L1 to L2 messages to become available on L2',
88
- ...numberConfigHelper(60)
87
+ ...numberConfigHelper(3600)
89
88
  },
90
89
  senderPrivateKey: {
91
90
  env: 'BOT_PRIVATE_KEY',
@@ -94,18 +93,12 @@ export const botConfigMappings = {
94
93
  },
95
94
  senderSalt: {
96
95
  env: 'BOT_ACCOUNT_SALT',
97
- description: 'The salt to use to deploys the sender account.',
96
+ description: 'The salt to use to deploy the sender account.',
98
97
  parseEnv: (val)=>val ? Fr.fromHexString(val) : undefined
99
98
  },
100
- recipientEncryptionSecret: {
101
- env: 'BOT_RECIPIENT_ENCRYPTION_SECRET',
102
- description: 'Encryption secret for a recipient account.',
103
- printDefault: (sv)=>sv?.getValue(),
104
- ...secretFrConfigHelper(Fr.fromHexString('0xcafecafe'))
105
- },
106
99
  tokenSalt: {
107
100
  env: 'BOT_TOKEN_SALT',
108
- description: 'Salt for the token contract deployment.',
101
+ description: 'The salt to use to deploy the token contract.',
109
102
  parseEnv: (val)=>Fr.fromHexString(val),
110
103
  defaultValue: Fr.fromHexString('1')
111
104
  },
@@ -130,6 +123,11 @@ export const botConfigMappings = {
130
123
  parseEnv: (val)=>val || undefined,
131
124
  defaultValue: 'fee_juice'
132
125
  },
126
+ minFeePadding: {
127
+ env: 'BOT_MIN_FEE_PADDING',
128
+ description: 'How much is the bot willing to overpay vs. the current base fee',
129
+ ...numberConfigHelper(3)
130
+ },
133
131
  noStart: {
134
132
  env: 'BOT_NO_START',
135
133
  description: 'True to not automatically setup or start the bot on initialization.',
@@ -196,7 +194,11 @@ export const botConfigMappings = {
196
194
  env: 'BOT_AMM_TXS',
197
195
  description: 'Deploy an AMM and send swaps to it',
198
196
  ...booleanConfigHelper(false)
199
- }
197
+ },
198
+ ...pickConfigMappings(dataConfigMappings, [
199
+ 'dataStoreMapSizeKb',
200
+ 'dataDirectory'
201
+ ])
200
202
  };
201
203
  export function getBotConfigFromEnv() {
202
204
  return getConfigFromMappings(botConfigMappings);
@@ -206,7 +208,7 @@ export function getBotDefaultConfig() {
206
208
  }
207
209
  export function getVersions() {
208
210
  return {
209
- l2ProtocolContractsTreeRoot: protocolContractTreeRoot.toString(),
211
+ l2ProtocolContractsHash: protocolContractsHash.toString(),
210
212
  l2CircuitsVkTreeRoot: getVKTreeRoot().toString()
211
213
  };
212
214
  }
package/dest/factory.d.ts CHANGED
@@ -1,55 +1,41 @@
1
- import { AztecAddress, type PXE } from '@aztec/aztec.js';
1
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
2
2
  import { AMMContract } from '@aztec/noir-contracts.js/AMM';
3
3
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
4
4
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
5
5
  import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
6
+ import { TestWallet } from '@aztec/test-wallet/server';
6
7
  import { type BotConfig } from './config.js';
8
+ import type { BotStore } from './store/index.js';
7
9
  export declare class BotFactory {
8
10
  private readonly config;
9
- private pxe;
10
- private node?;
11
- private nodeAdmin?;
11
+ private readonly wallet;
12
+ private readonly store;
13
+ private readonly aztecNode;
14
+ private readonly aztecNodeAdmin?;
12
15
  private log;
13
- constructor(config: BotConfig, dependencies: {
14
- pxe?: PXE;
15
- nodeAdmin?: AztecNodeAdmin;
16
- node?: AztecNode;
17
- });
16
+ constructor(config: BotConfig, wallet: TestWallet, store: BotStore, aztecNode: AztecNode, aztecNodeAdmin?: AztecNodeAdmin | undefined);
18
17
  /**
19
18
  * Initializes a new bot by setting up the sender account, registering the recipient,
20
19
  * deploying the token contract, and minting tokens if necessary.
21
20
  */
22
21
  setup(): Promise<{
23
- wallet: import("@aztec/aztec.js").AccountWalletWithSecretKey;
22
+ wallet: TestWallet;
24
23
  defaultAccountAddress: AztecAddress;
25
24
  token: TokenContract | PrivateTokenContract;
26
- pxe: PXE;
25
+ node: AztecNode;
27
26
  recipient: AztecAddress;
28
27
  }>;
29
28
  setupAmm(): Promise<{
30
- wallet: import("@aztec/aztec.js").AccountWalletWithSecretKey;
29
+ wallet: TestWallet;
31
30
  defaultAccountAddress: AztecAddress;
32
31
  amm: AMMContract;
33
32
  token0: TokenContract;
34
33
  token1: TokenContract;
35
- pxe: PXE;
34
+ node: AztecNode;
36
35
  }>;
37
- /**
38
- * Checks if the sender account contract is initialized, and initializes it if necessary.
39
- * @returns The sender wallet.
40
- */
41
36
  private setupAccount;
42
37
  private setupAccountWithPrivateKey;
43
38
  private setupTestAccount;
44
- /**
45
- * Registers the recipient for txs in the pxe.
46
- */
47
- private registerRecipient;
48
- /**
49
- * Checks if the token contract is deployed and deploys it if necessary.
50
- * @param wallet - Wallet to deploy the token contract from.
51
- * @returns The TokenContract instance.
52
- */
53
39
  private setupToken;
54
40
  /**
55
41
  * Checks if the token contract is deployed and deploys it if necessary.
@@ -60,13 +46,9 @@ export declare class BotFactory {
60
46
  private setupAmmContract;
61
47
  private fundAmm;
62
48
  private registerOrDeployContract;
63
- /**
64
- * Mints private and public tokens for the sender if their balance is below the minimum.
65
- * @param token - Token contract.
66
- */
67
49
  private mintTokens;
50
+ private getOrCreateBridgeClaim;
68
51
  private bridgeL1FeeJuice;
69
52
  private withNoMinTxsPerBlock;
70
- private advanceL2Block;
71
53
  }
72
- //# sourceMappingURL=factory.d.ts.map
54
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBRUEsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBa0J6RCxPQUFPLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDM0QsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sdUNBQXVDLENBQUM7QUFDN0UsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGdDQUFnQyxDQUFDO0FBRy9ELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFFdkQsT0FBTyxFQUFFLEtBQUssU0FBUyxFQUEyQixNQUFNLGFBQWEsQ0FBQztBQUN0RSxPQUFPLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQU1qRCxxQkFBYSxVQUFVO0lBSW5CLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtJQUN2QixPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFDdkIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztJQUMxQixPQUFPLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQztJQVBsQyxPQUFPLENBQUMsR0FBRyxDQUF1QjtJQUVsQyxZQUNtQixNQUFNLEVBQUUsU0FBUyxFQUNqQixNQUFNLEVBQUUsVUFBVSxFQUNsQixLQUFLLEVBQUUsUUFBUSxFQUNmLFNBQVMsRUFBRSxTQUFTLEVBQ3BCLGNBQWMsQ0FBQyw0QkFBZ0IsRUFDOUM7SUFFSjs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDO1FBQzVCLE1BQU0sRUFBRSxVQUFVLENBQUM7UUFDbkIscUJBQXFCLEVBQUUsWUFBWSxDQUFDO1FBQ3BDLEtBQUssRUFBRSxhQUFhLEdBQUcsb0JBQW9CLENBQUM7UUFDNUMsSUFBSSxFQUFFLFNBQVMsQ0FBQztRQUNoQixTQUFTLEVBQUUsWUFBWSxDQUFDO0tBQ3pCLENBQUMsQ0FNRDtJQUVZLFFBQVEsSUFBSSxPQUFPLENBQUM7UUFDL0IsTUFBTSxFQUFFLFVBQVUsQ0FBQztRQUNuQixxQkFBcUIsRUFBRSxZQUFZLENBQUM7UUFDcEMsR0FBRyxFQUFFLFdBQVcsQ0FBQztRQUNqQixNQUFNLEVBQUUsYUFBYSxDQUFDO1FBQ3RCLE1BQU0sRUFBRSxhQUFhLENBQUM7UUFDdEIsSUFBSSxFQUFFLFNBQVMsQ0FBQztLQUNqQixDQUFDLENBc0JEO1lBTWEsWUFBWTtZQVdaLDBCQUEwQjtZQXdDMUIsZ0JBQWdCO1lBZ0JoQixVQUFVO0lBdUN4Qjs7OztPQUlHO0lBQ0gsT0FBTyxDQUFDLGtCQUFrQjtZQVlaLGdCQUFnQjtZQW9CaEIsT0FBTztZQThFUCx3QkFBd0I7WUFzQnhCLFVBQVU7WUF3Q1Ysc0JBQXNCO1lBNEJ0QixnQkFBZ0I7WUFnQ2hCLG9CQUFvQjtDQWVuQyJ9
@@ -1 +1 @@
1
- {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAEL,YAAY,EAQZ,KAAK,GAAG,EAIT,MAAM,iBAAiB,CAAC;AAIzB,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAIjF,OAAO,EAAE,KAAK,SAAS,EAAwC,MAAM,aAAa,CAAC;AAMnF,qBAAa,UAAU;IAOnB,OAAO,CAAC,QAAQ,CAAC,MAAM;IANzB,OAAO,CAAC,GAAG,CAAM;IACjB,OAAO,CAAC,IAAI,CAAC,CAAY;IACzB,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,GAAG,CAAuB;gBAGf,MAAM,EAAE,SAAS,EAClC,YAAY,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAC;QAAC,SAAS,CAAC,EAAE,cAAc,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAA;KAAE;IA4B3E;;;OAGG;IACU,KAAK;;;;;;;IASL,QAAQ;;;;;;;;IAuCrB;;;OAGG;YACW,YAAY;YASZ,0BAA0B;YA8B1B,gBAAgB;IAc9B;;OAEG;YACW,iBAAiB;IAK/B;;;;OAIG;YACW,UAAU;IA+BxB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;YAaZ,gBAAgB;YAqBhB,OAAO;YA0EP,wBAAwB;IAkBtC;;;OAGG;YACW,UAAU;YAmCV,gBAAgB;YAgChB,oBAAoB;YAapB,cAAc;CAM7B"}
1
+ {"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAkBzD,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAG/D,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjF,OAAO,EAAE,UAAU,EAAE,MAAM,2BAA2B,CAAC;AAEvD,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,aAAa,CAAC;AACtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMjD,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAPlC,OAAO,CAAC,GAAG,CAAuB;IAElC,YACmB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,UAAU,EAClB,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,SAAS,EACpB,cAAc,CAAC,4BAAgB,EAC9C;IAEJ;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC;QAC5B,MAAM,EAAE,UAAU,CAAC;QACnB,qBAAqB,EAAE,YAAY,CAAC;QACpC,KAAK,EAAE,aAAa,GAAG,oBAAoB,CAAC;QAC5C,IAAI,EAAE,SAAS,CAAC;QAChB,SAAS,EAAE,YAAY,CAAC;KACzB,CAAC,CAMD;IAEY,QAAQ,IAAI,OAAO,CAAC;QAC/B,MAAM,EAAE,UAAU,CAAC;QACnB,qBAAqB,EAAE,YAAY,CAAC;QACpC,GAAG,EAAE,WAAW,CAAC;QACjB,MAAM,EAAE,aAAa,CAAC;QACtB,MAAM,EAAE,aAAa,CAAC;QACtB,IAAI,EAAE,SAAS,CAAC;KACjB,CAAC,CAsBD;YAMa,YAAY;YAWZ,0BAA0B;YAwC1B,gBAAgB;YAgBhB,UAAU;IAuCxB;;;;OAIG;IACH,OAAO,CAAC,kBAAkB;YAYZ,gBAAgB;YAoBhB,OAAO;YA8EP,wBAAwB;YAsBxB,UAAU;YAwCV,sBAAsB;YA4BtB,gBAAgB;YAgChB,oBAAoB;CAenC"}
package/dest/factory.js CHANGED
@@ -1,83 +1,71 @@
1
- import { getSchnorrAccount } from '@aztec/accounts/schnorr';
2
- import { getDeployedTestAccountsWallets, getInitialTestAccounts } from '@aztec/accounts/testing';
3
- import { BatchCall, FeeJuicePaymentMethodWithClaim, L1FeeJuicePortalManager, createLogger, createPXEClient, retryUntil } from '@aztec/aztec.js';
4
- import { createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
5
- import { Fr } from '@aztec/foundation/fields';
1
+ import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
2
+ import { getInitialTestAccountsData } from '@aztec/accounts/testing';
3
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
4
+ import { BatchCall } from '@aztec/aztec.js/contracts';
5
+ import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
6
+ import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
7
+ import { deriveKeys } from '@aztec/aztec.js/keys';
8
+ import { createLogger } from '@aztec/aztec.js/log';
9
+ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
10
+ import { createEthereumChain } from '@aztec/ethereum/chain';
11
+ import { createExtendedL1Client } from '@aztec/ethereum/client';
12
+ import { Fr } from '@aztec/foundation/curves/bn254';
6
13
  import { Timer } from '@aztec/foundation/timer';
7
14
  import { AMMContract } from '@aztec/noir-contracts.js/AMM';
8
15
  import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
9
16
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
17
+ import { GasSettings } from '@aztec/stdlib/gas';
10
18
  import { deriveSigningKey } from '@aztec/stdlib/keys';
11
- import { makeTracedFetch } from '@aztec/telemetry-client';
12
- import { SupportedTokenContracts, getVersions } from './config.js';
19
+ import { SupportedTokenContracts } from './config.js';
13
20
  import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
14
21
  const MINT_BALANCE = 1e12;
15
22
  const MIN_BALANCE = 1e3;
16
23
  export class BotFactory {
17
24
  config;
18
- pxe;
19
- node;
20
- nodeAdmin;
25
+ wallet;
26
+ store;
27
+ aztecNode;
28
+ aztecNodeAdmin;
21
29
  log;
22
- constructor(config, dependencies){
30
+ constructor(config, wallet, store, aztecNode, aztecNodeAdmin){
23
31
  this.config = config;
32
+ this.wallet = wallet;
33
+ this.store = store;
34
+ this.aztecNode = aztecNode;
35
+ this.aztecNodeAdmin = aztecNodeAdmin;
24
36
  this.log = createLogger('bot');
25
- if (config.flushSetupTransactions && !dependencies.nodeAdmin) {
26
- throw new Error(`Either a node admin client or node admin url must be provided if transaction flushing is requested`);
27
- }
28
- if (config.senderPrivateKey && config.senderPrivateKey.getValue() && !dependencies.node) {
29
- throw new Error(`Either a node client or node url must be provided for bridging L1 fee juice to deploy an account with private key`);
30
- }
31
- if (!dependencies.pxe && !config.pxeUrl) {
32
- throw new Error(`Either a PXE client or a PXE URL must be provided`);
33
- }
34
- this.node = dependencies.node;
35
- this.nodeAdmin = dependencies.nodeAdmin;
36
- if (dependencies.pxe) {
37
- this.log.info(`Using local PXE`);
38
- this.pxe = dependencies.pxe;
39
- return;
40
- }
41
- this.log.info(`Using remote PXE at ${config.pxeUrl}`);
42
- this.pxe = createPXEClient(config.pxeUrl, getVersions(), makeTracedFetch([
43
- 1,
44
- 2,
45
- 3
46
- ], false));
47
37
  }
48
38
  /**
49
39
  * Initializes a new bot by setting up the sender account, registering the recipient,
50
40
  * deploying the token contract, and minting tokens if necessary.
51
41
  */ async setup() {
52
- const recipient = await this.registerRecipient();
53
- const wallet = await this.setupAccount();
54
- const defaultAccountAddress = wallet.getAddress();
55
- const token = await this.setupToken(wallet, defaultAccountAddress);
42
+ const recipient = (await this.wallet.createAccount()).address;
43
+ const defaultAccountAddress = await this.setupAccount();
44
+ const token = await this.setupToken(defaultAccountAddress);
56
45
  await this.mintTokens(token, defaultAccountAddress);
57
46
  return {
58
- wallet,
47
+ wallet: this.wallet,
59
48
  defaultAccountAddress,
60
49
  token,
61
- pxe: this.pxe,
50
+ node: this.aztecNode,
62
51
  recipient
63
52
  };
64
53
  }
65
54
  async setupAmm() {
66
- const wallet = await this.setupAccount();
67
- const defaultAccountAddress = wallet.getAddress();
68
- const token0 = await this.setupTokenContract(wallet, wallet.getAddress(), this.config.tokenSalt, 'BotToken0', 'BOT0');
69
- const token1 = await this.setupTokenContract(wallet, wallet.getAddress(), this.config.tokenSalt, 'BotToken1', 'BOT1');
70
- const liquidityToken = await this.setupTokenContract(wallet, wallet.getAddress(), this.config.tokenSalt, 'BotLPToken', 'BOTLP');
71
- const amm = await this.setupAmmContract(wallet, wallet.getAddress(), this.config.tokenSalt, token0, token1, liquidityToken);
72
- await this.fundAmm(wallet, wallet.getAddress(), amm, token0, token1, liquidityToken);
55
+ const defaultAccountAddress = await this.setupAccount();
56
+ const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
57
+ const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
58
+ const liquidityToken = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotLPToken', 'BOTLP');
59
+ const amm = await this.setupAmmContract(defaultAccountAddress, this.config.tokenSalt, token0, token1, liquidityToken);
60
+ await this.fundAmm(defaultAccountAddress, defaultAccountAddress, amm, token0, token1, liquidityToken);
73
61
  this.log.info(`AMM initialized and funded`);
74
62
  return {
75
- wallet,
63
+ wallet: this.wallet,
76
64
  defaultAccountAddress,
77
65
  amm,
78
66
  token0,
79
67
  token1,
80
- pxe: this.pxe
68
+ node: this.aztecNode
81
69
  };
82
70
  }
83
71
  /**
@@ -86,86 +74,98 @@ export class BotFactory {
86
74
  */ async setupAccount() {
87
75
  const privateKey = this.config.senderPrivateKey?.getValue();
88
76
  if (privateKey) {
77
+ this.log.info(`Setting up account with provided private key`);
89
78
  return await this.setupAccountWithPrivateKey(privateKey);
90
79
  } else {
80
+ this.log.info(`Setting up test account`);
91
81
  return await this.setupTestAccount();
92
82
  }
93
83
  }
94
- async setupAccountWithPrivateKey(privateKey) {
84
+ async setupAccountWithPrivateKey(secret) {
95
85
  const salt = this.config.senderSalt ?? Fr.ONE;
96
- const signingKey = deriveSigningKey(privateKey);
97
- const account = await getSchnorrAccount(this.pxe, privateKey, signingKey, salt);
98
- const isInit = (await this.pxe.getContractMetadata(account.getAddress())).isContractInitialized;
86
+ const signingKey = deriveSigningKey(secret);
87
+ const accountData = {
88
+ secret,
89
+ salt,
90
+ contract: new SchnorrAccountContract(signingKey)
91
+ };
92
+ const accountManager = await this.wallet.createAccount(accountData);
93
+ const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
99
94
  if (isInit) {
100
- this.log.info(`Account at ${account.getAddress().toString()} already initialized`);
95
+ this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
101
96
  const timer = new Timer();
102
- const wallet = await account.register();
103
- this.log.info(`Account at ${account.getAddress()} registered. duration=${timer.ms()}`);
104
- return wallet;
97
+ const address = accountManager.address;
98
+ this.log.info(`Account at ${address} registered. duration=${timer.ms()}`);
99
+ await this.store.deleteBridgeClaim(address);
100
+ return address;
105
101
  } else {
106
- const address = account.getAddress();
102
+ const address = accountManager.address;
107
103
  this.log.info(`Deploying account at ${address}`);
108
- const claim = await this.bridgeL1FeeJuice(address);
109
- // docs:start:claim_and_deploy
110
- const wallet = await account.getWallet();
111
- const paymentMethod = new FeeJuicePaymentMethodWithClaim(wallet, claim);
112
- const sentTx = account.deploy({
104
+ const claim = await this.getOrCreateBridgeClaim(address);
105
+ const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
106
+ const deployMethod = await accountManager.getDeployMethod();
107
+ const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
108
+ const gasSettings = GasSettings.default({
109
+ maxFeesPerGas
110
+ });
111
+ const sentTx = deployMethod.send({
112
+ from: AztecAddress.ZERO,
113
113
  fee: {
114
+ gasSettings,
114
115
  paymentMethod
115
116
  }
116
117
  });
117
118
  const txHash = await sentTx.getTxHash();
118
- // docs:end:claim_and_deploy
119
119
  this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
120
120
  await this.withNoMinTxsPerBlock(()=>sentTx.wait({
121
121
  timeout: this.config.txMinedWaitSeconds
122
122
  }));
123
123
  this.log.info(`Account deployed at ${address}`);
124
- return wallet;
124
+ // Clean up the consumed bridge claim
125
+ await this.store.deleteBridgeClaim(address);
126
+ return accountManager.address;
125
127
  }
126
128
  }
127
129
  async setupTestAccount() {
128
- let [wallet] = await getDeployedTestAccountsWallets(this.pxe);
129
- if (wallet) {
130
- this.log.info(`Using funded test account: ${wallet.getAddress()}`);
131
- } else {
132
- this.log.info('Registering funded test account');
133
- const [account] = await getInitialTestAccounts();
134
- const manager = await getSchnorrAccount(this.pxe, account.secret, account.signingKey, account.salt);
135
- wallet = await manager.register();
136
- this.log.info(`Funded test account registered: ${wallet.getAddress()}`);
137
- }
138
- return wallet;
139
- }
140
- /**
141
- * Registers the recipient for txs in the pxe.
142
- */ async registerRecipient() {
143
- const recipient = await this.pxe.registerAccount(this.config.recipientEncryptionSecret.getValue(), Fr.ONE);
144
- return recipient.address;
130
+ const [initialAccountData] = await getInitialTestAccountsData();
131
+ const accountData = {
132
+ secret: initialAccountData.secret,
133
+ salt: initialAccountData.salt,
134
+ contract: new SchnorrAccountContract(initialAccountData.signingKey)
135
+ };
136
+ const accountManager = await this.wallet.createAccount(accountData);
137
+ return accountManager.address;
145
138
  }
146
139
  /**
147
140
  * Checks if the token contract is deployed and deploys it if necessary.
148
141
  * @param wallet - Wallet to deploy the token contract from.
149
142
  * @returns The TokenContract instance.
150
- */ async setupToken(wallet, sender) {
143
+ */ async setupToken(sender) {
151
144
  let deploy;
145
+ let tokenInstance;
152
146
  const deployOpts = {
153
147
  from: sender,
154
148
  contractAddressSalt: this.config.tokenSalt,
155
149
  universalDeploy: true
156
150
  };
157
151
  if (this.config.contract === SupportedTokenContracts.TokenContract) {
158
- deploy = TokenContract.deploy(wallet, sender, 'BotToken', 'BOT', 18);
152
+ deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
159
153
  } else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
160
- deploy = PrivateTokenContract.deploy(wallet, MINT_BALANCE, sender);
154
+ // Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
155
+ const tokenSecretKey = Fr.random();
156
+ const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
157
+ deploy = PrivateTokenContract.deployWithPublicKeys(tokenPublicKeys, this.wallet, MINT_BALANCE, sender);
161
158
  deployOpts.skipInstancePublication = true;
162
159
  deployOpts.skipClassPublication = true;
163
160
  deployOpts.skipInitialization = false;
161
+ // Register the contract with the secret key before deployment
162
+ tokenInstance = await deploy.getInstance(deployOpts);
163
+ await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
164
164
  } else {
165
165
  throw new Error(`Unsupported token contract type: ${this.config.contract}`);
166
166
  }
167
- const address = (await deploy.getInstance(deployOpts)).address;
168
- if ((await this.pxe.getContractMetadata(address)).isContractPublished) {
167
+ const address = tokenInstance?.address ?? (await deploy.getInstance(deployOpts)).address;
168
+ if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
169
169
  this.log.info(`Token at ${address.toString()} already deployed`);
170
170
  return deploy.register();
171
171
  } else {
@@ -182,22 +182,22 @@ export class BotFactory {
182
182
  * Checks if the token contract is deployed and deploys it if necessary.
183
183
  * @param wallet - Wallet to deploy the token contract from.
184
184
  * @returns The TokenContract instance.
185
- */ setupTokenContract(wallet, deployer, contractAddressSalt, name, ticker, decimals = 18) {
185
+ */ setupTokenContract(deployer, contractAddressSalt, name, ticker, decimals = 18) {
186
186
  const deployOpts = {
187
187
  from: deployer,
188
188
  contractAddressSalt,
189
189
  universalDeploy: true
190
190
  };
191
- const deploy = TokenContract.deploy(wallet, deployer, name, ticker, decimals);
191
+ const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
192
192
  return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
193
193
  }
194
- async setupAmmContract(wallet, deployer, contractAddressSalt, token0, token1, lpToken) {
194
+ async setupAmmContract(deployer, contractAddressSalt, token0, token1, lpToken) {
195
195
  const deployOpts = {
196
196
  from: deployer,
197
197
  contractAddressSalt,
198
198
  universalDeploy: true
199
199
  };
200
- const deploy = AMMContract.deploy(wallet, token0.address, token1.address, lpToken.address);
200
+ const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
201
201
  const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
202
202
  this.log.info(`AMM deployed at ${amm.address}`);
203
203
  const minterTx = lpToken.methods.set_minter(amm.address, true).send({
@@ -210,7 +210,7 @@ export class BotFactory {
210
210
  this.log.info(`Liquidity token initialized`);
211
211
  return amm;
212
212
  }
213
- async fundAmm(wallet, liquidityProvider, amm, token0, token1, lpToken) {
213
+ async fundAmm(defaultAccountAddress, liquidityProvider, amm, token0, token1, lpToken) {
214
214
  const getPrivateBalances = ()=>Promise.all([
215
215
  token0.methods.balance_of_private(liquidityProvider).simulate({
216
216
  from: liquidityProvider
@@ -231,15 +231,15 @@ export class BotFactory {
231
231
  const [t0Bal, t1Bal, lpBal] = await getPrivateBalances();
232
232
  this.log.info(`Minting ${MINT_BALANCE} tokens of each BotToken0 and BotToken1. Current private balances of ${liquidityProvider}: token0=${t0Bal}, token1=${t1Bal}, lp=${lpBal}`);
233
233
  // Add authwitnesses for the transfers in AMM::add_liquidity function
234
- const token0Authwit = await wallet.createAuthWit({
234
+ const token0Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
235
235
  caller: amm.address,
236
- action: token0.methods.transfer_to_public_and_prepare_private_balance_increase(liquidityProvider, amm.address, amount0Max, authwitNonce)
236
+ call: await token0.methods.transfer_to_public_and_prepare_private_balance_increase(liquidityProvider, amm.address, amount0Max, authwitNonce).getFunctionCall()
237
237
  });
238
- const token1Authwit = await wallet.createAuthWit({
238
+ const token1Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
239
239
  caller: amm.address,
240
- action: token1.methods.transfer_to_public_and_prepare_private_balance_increase(liquidityProvider, amm.address, amount1Max, authwitNonce)
240
+ call: await token1.methods.transfer_to_public_and_prepare_private_balance_increase(liquidityProvider, amm.address, amount1Max, authwitNonce).getFunctionCall()
241
241
  });
242
- const mintTx = new BatchCall(wallet, [
242
+ const mintTx = new BatchCall(this.wallet, [
243
243
  token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
244
244
  token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE)
245
245
  ]).send({
@@ -262,11 +262,11 @@ export class BotFactory {
262
262
  });
263
263
  this.log.info(`Liquidity added`);
264
264
  const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
265
- this.log.info(`Updated private balances of ${wallet.getAddress()} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`);
265
+ this.log.info(`Updated private balances of ${defaultAccountAddress} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`);
266
266
  }
267
267
  async registerOrDeployContract(name, deploy, deployOpts) {
268
268
  const address = (await deploy.getInstance(deployOpts)).address;
269
- if ((await this.pxe.getContractMetadata(address)).isContractPublished) {
269
+ if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
270
270
  this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
271
271
  return deploy.register();
272
272
  } else {
@@ -313,6 +313,32 @@ export class BotFactory {
313
313
  timeout: this.config.txMinedWaitSeconds
314
314
  }));
315
315
  }
316
+ /**
317
+ * Gets or creates a bridge claim for the recipient.
318
+ * Checks if a claim already exists in the store and reuses it if valid.
319
+ * Only creates a new bridge if fee juice balance is below threshold.
320
+ */ async getOrCreateBridgeClaim(recipient) {
321
+ // Check if we have an existing claim in the store
322
+ const existingClaim = await this.store.getBridgeClaim(recipient);
323
+ if (existingClaim) {
324
+ this.log.info(`Found existing bridge claim for ${recipient.toString()}, checking validity...`);
325
+ // Check if the message is ready on L2
326
+ try {
327
+ const messageHash = Fr.fromHexString(existingClaim.claim.messageHash);
328
+ await this.withNoMinTxsPerBlock(()=>waitForL1ToL2MessageReady(this.aztecNode, messageHash, {
329
+ timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
330
+ forPublicConsumption: false
331
+ }));
332
+ return existingClaim.claim;
333
+ } catch (err) {
334
+ this.log.warn(`Failed to verify existing claim, creating new one: ${err}`);
335
+ await this.store.deleteBridgeClaim(recipient);
336
+ }
337
+ }
338
+ const claim = await this.bridgeL1FeeJuice(recipient);
339
+ await this.store.saveBridgeClaim(recipient, claim);
340
+ return claim;
341
+ }
316
342
  async bridgeL1FeeJuice(recipient) {
317
343
  const l1RpcUrls = this.config.l1RpcUrls;
318
344
  if (!l1RpcUrls?.length) {
@@ -322,40 +348,36 @@ export class BotFactory {
322
348
  if (!mnemonicOrPrivateKey) {
323
349
  throw new Error('Either a mnemonic or private key of an L1 account is required to bridge the fee juice to fund the deployment of the account.');
324
350
  }
325
- const { l1ChainId } = await this.pxe.getNodeInfo();
351
+ const { l1ChainId } = await this.aztecNode.getNodeInfo();
326
352
  const chain = createEthereumChain(l1RpcUrls, l1ChainId);
327
353
  const extendedClient = createExtendedL1Client(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
328
- const portal = await L1FeeJuicePortalManager.new(this.pxe, extendedClient, this.log);
354
+ const portal = await L1FeeJuicePortalManager.new(this.aztecNode, extendedClient, this.log);
329
355
  const mintAmount = await portal.getTokenManager().getMintAmount();
330
356
  const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true);
331
- const isSynced = async ()=>await this.pxe.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
332
- await retryUntil(isSynced, `message ${claim.messageHash} sync`, this.config.l1ToL2MessageTimeoutSeconds, 1);
357
+ await this.withNoMinTxsPerBlock(()=>waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(claim.messageHash), {
358
+ timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
359
+ forPublicConsumption: false
360
+ }));
333
361
  this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
334
- // Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
335
- await this.advanceL2Block();
336
- await this.advanceL2Block();
337
362
  return claim;
338
363
  }
339
364
  async withNoMinTxsPerBlock(fn) {
340
- if (!this.nodeAdmin || !this.config.flushSetupTransactions) {
365
+ if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
366
+ this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
341
367
  return fn();
342
368
  }
343
- const { minTxsPerBlock } = await this.nodeAdmin.getConfig();
344
- await this.nodeAdmin.setConfig({
369
+ const { minTxsPerBlock } = await this.aztecNodeAdmin.getConfig();
370
+ this.log.warn(`Setting sequencer minTxsPerBlock to 0 from ${minTxsPerBlock} to flush setup transactions`);
371
+ await this.aztecNodeAdmin.setConfig({
345
372
  minTxsPerBlock: 0
346
373
  });
347
374
  try {
348
375
  return await fn();
349
376
  } finally{
350
- await this.nodeAdmin.setConfig({
377
+ this.log.warn(`Restoring sequencer minTxsPerBlock to ${minTxsPerBlock}`);
378
+ await this.aztecNodeAdmin.setConfig({
351
379
  minTxsPerBlock
352
380
  });
353
381
  }
354
382
  }
355
- async advanceL2Block() {
356
- await this.withNoMinTxsPerBlock(async ()=>{
357
- const initialBlockNumber = await this.node.getBlockNumber();
358
- await retryUntil(async ()=>await this.node.getBlockNumber() >= initialBlockNumber + 1);
359
- });
360
- }
361
383
  }