@aztec/bot 0.0.0-test.0 → 0.0.1-commit.0208eb9

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 (49) hide show
  1. package/dest/amm_bot.d.ts +32 -0
  2. package/dest/amm_bot.d.ts.map +1 -0
  3. package/dest/amm_bot.js +101 -0
  4. package/dest/base_bot.d.ts +21 -0
  5. package/dest/base_bot.d.ts.map +1 -0
  6. package/dest/base_bot.js +80 -0
  7. package/dest/bot.d.ts +13 -18
  8. package/dest/bot.d.ts.map +1 -1
  9. package/dest/bot.js +27 -86
  10. package/dest/config.d.ts +84 -60
  11. package/dest/config.d.ts.map +1 -1
  12. package/dest/config.js +59 -36
  13. package/dest/factory.d.ts +30 -31
  14. package/dest/factory.d.ts.map +1 -1
  15. package/dest/factory.js +318 -139
  16. package/dest/index.d.ts +4 -2
  17. package/dest/index.d.ts.map +1 -1
  18. package/dest/index.js +3 -1
  19. package/dest/interface.d.ts +12 -1
  20. package/dest/interface.d.ts.map +1 -1
  21. package/dest/interface.js +5 -0
  22. package/dest/rpc.d.ts +1 -7
  23. package/dest/rpc.d.ts.map +1 -1
  24. package/dest/rpc.js +0 -11
  25. package/dest/runner.d.ts +15 -11
  26. package/dest/runner.d.ts.map +1 -1
  27. package/dest/runner.js +441 -51
  28. package/dest/store/bot_store.d.ts +44 -0
  29. package/dest/store/bot_store.d.ts.map +1 -0
  30. package/dest/store/bot_store.js +107 -0
  31. package/dest/store/index.d.ts +2 -0
  32. package/dest/store/index.d.ts.map +1 -0
  33. package/dest/store/index.js +1 -0
  34. package/dest/utils.d.ts +8 -5
  35. package/dest/utils.d.ts.map +1 -1
  36. package/dest/utils.js +14 -5
  37. package/package.json +27 -23
  38. package/src/amm_bot.ts +124 -0
  39. package/src/base_bot.ts +92 -0
  40. package/src/bot.ts +53 -103
  41. package/src/config.ts +98 -68
  42. package/src/factory.ts +378 -154
  43. package/src/index.ts +3 -1
  44. package/src/interface.ts +9 -0
  45. package/src/rpc.ts +0 -13
  46. package/src/runner.ts +38 -21
  47. package/src/store/bot_store.ts +141 -0
  48. package/src/store/index.ts +1 -0
  49. package/src/utils.ts +17 -6
@@ -0,0 +1,107 @@
1
+ import { Fr } from '@aztec/foundation/curves/bn254';
2
+ import { createLogger } from '@aztec/foundation/log';
3
+ /**
4
+ * Simple data store for the bot to persist L1 bridge claims.
5
+ */ export class BotStore {
6
+ store;
7
+ log;
8
+ static SCHEMA_VERSION = 1;
9
+ bridgeClaims;
10
+ constructor(store, log = createLogger('bot:store')){
11
+ this.store = store;
12
+ this.log = log;
13
+ this.bridgeClaims = store.openMap('bridge_claims');
14
+ }
15
+ /**
16
+ * Saves a bridge claim for a recipient.
17
+ */ async saveBridgeClaim(recipient, claim) {
18
+ // Convert Fr fields and BigInts to strings for JSON serialization
19
+ const serializableClaim = {
20
+ claimAmount: claim.claimAmount.toString(),
21
+ claimSecret: claim.claimSecret.toString(),
22
+ claimSecretHash: claim.claimSecretHash.toString(),
23
+ messageHash: claim.messageHash,
24
+ messageLeafIndex: claim.messageLeafIndex.toString()
25
+ };
26
+ const data = {
27
+ claim: serializableClaim,
28
+ timestamp: Date.now(),
29
+ recipient: recipient.toString()
30
+ };
31
+ await this.bridgeClaims.set(recipient.toString(), JSON.stringify(data));
32
+ this.log.info(`Saved bridge claim for ${recipient.toString()}`);
33
+ }
34
+ /**
35
+ * Gets a bridge claim for a recipient if it exists.
36
+ */ async getBridgeClaim(recipient) {
37
+ const data = await this.bridgeClaims.getAsync(recipient.toString());
38
+ if (!data) {
39
+ return undefined;
40
+ }
41
+ const parsed = JSON.parse(data);
42
+ // Reconstruct L2AmountClaim from serialized data
43
+ const claim = {
44
+ claimAmount: BigInt(parsed.claim.claimAmount),
45
+ claimSecret: Fr.fromString(parsed.claim.claimSecret),
46
+ claimSecretHash: Fr.fromString(parsed.claim.claimSecretHash),
47
+ messageHash: parsed.claim.messageHash,
48
+ messageLeafIndex: BigInt(parsed.claim.messageLeafIndex)
49
+ };
50
+ return {
51
+ claim,
52
+ timestamp: parsed.timestamp,
53
+ recipient: parsed.recipient
54
+ };
55
+ }
56
+ /**
57
+ * Deletes a bridge claim for a recipient.
58
+ */ async deleteBridgeClaim(recipient) {
59
+ await this.bridgeClaims.delete(recipient.toString());
60
+ this.log.info(`Deleted bridge claim for ${recipient.toString()}`);
61
+ }
62
+ /**
63
+ * Gets all stored bridge claims.
64
+ */ async getAllBridgeClaims() {
65
+ const claims = [];
66
+ const entries = this.bridgeClaims.entriesAsync();
67
+ for await (const [_, data] of entries){
68
+ const parsed = JSON.parse(data);
69
+ // Reconstruct L2AmountClaim from serialized data
70
+ const claim = {
71
+ claimAmount: BigInt(parsed.claim.claimAmount),
72
+ claimSecret: Fr.fromString(parsed.claim.claimSecret),
73
+ claimSecretHash: Fr.fromString(parsed.claim.claimSecretHash),
74
+ messageHash: parsed.claim.messageHash,
75
+ messageLeafIndex: BigInt(parsed.claim.messageLeafIndex)
76
+ };
77
+ claims.push({
78
+ claim,
79
+ timestamp: parsed.timestamp,
80
+ recipient: parsed.recipient
81
+ });
82
+ }
83
+ return claims;
84
+ }
85
+ /**
86
+ * Cleans up old bridge claims (older than 24 hours).
87
+ */ async cleanupOldClaims(maxAgeMs = 24 * 60 * 60 * 1000) {
88
+ const now = Date.now();
89
+ let cleanedCount = 0;
90
+ const entries = this.bridgeClaims.entriesAsync();
91
+ for await (const [key, data] of entries){
92
+ const parsed = JSON.parse(data);
93
+ if (now - parsed.timestamp > maxAgeMs) {
94
+ await this.bridgeClaims.delete(key);
95
+ cleanedCount++;
96
+ this.log.info(`Cleaned up old bridge claim for ${parsed.recipient}`);
97
+ }
98
+ }
99
+ return cleanedCount;
100
+ }
101
+ /**
102
+ * Closes the store.
103
+ */ async close() {
104
+ await this.store.close();
105
+ this.log.info('Closed bot data store');
106
+ }
107
+ }
@@ -0,0 +1,2 @@
1
+ export { BotStore, type BridgeClaimData } from './bot_store.js';
2
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy9zdG9yZS9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsUUFBUSxFQUFFLEtBQUssZUFBZSxFQUFFLE1BQU0sZ0JBQWdCLENBQUMifQ==
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/store/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,QAAQ,EAAE,KAAK,eAAe,EAAE,MAAM,gBAAgB,CAAC"}
@@ -0,0 +1 @@
1
+ export { BotStore } from './bot_store.js';
package/dest/utils.d.ts CHANGED
@@ -1,4 +1,6 @@
1
- import type { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken';
1
+ import { ContractBase } from '@aztec/aztec.js/contracts';
2
+ import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
3
+ import type { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
2
4
  import type { TokenContract } from '@aztec/noir-contracts.js/Token';
3
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
4
6
  /**
@@ -7,10 +9,11 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
7
9
  * @param who - Address to get the balance for.
8
10
  * @returns - Private and public token balances as bigints.
9
11
  */
10
- export declare function getBalances(token: TokenContract, who: AztecAddress): Promise<{
12
+ export declare function getBalances(token: TokenContract, who: AztecAddress, from?: AztecAddress): Promise<{
11
13
  privateBalance: bigint;
12
14
  publicBalance: bigint;
13
15
  }>;
14
- export declare function getPrivateBalance(token: EasyPrivateTokenContract, who: AztecAddress): Promise<bigint>;
15
- export declare function isStandardTokenContract(token: TokenContract | EasyPrivateTokenContract): token is TokenContract;
16
- //# sourceMappingURL=utils.d.ts.map
16
+ export declare function getPrivateBalance(token: PrivateTokenContract, who: AztecAddress, from?: AztecAddress): Promise<bigint>;
17
+ export declare function isStandardTokenContract(token: ContractBase): token is TokenContract;
18
+ export declare function isAMMContract(contract: ContractBase): contract is AMMContract;
19
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidXRpbHMuZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy91dGlscy50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sMkJBQTJCLENBQUM7QUFDekQsT0FBTyxLQUFLLEVBQUUsV0FBVyxFQUFFLE1BQU0sOEJBQThCLENBQUM7QUFDaEUsT0FBTyxLQUFLLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUNsRixPQUFPLEtBQUssRUFBRSxhQUFhLEVBQUUsTUFBTSxnQ0FBZ0MsQ0FBQztBQUNwRSxPQUFPLEtBQUssRUFBRSxZQUFZLEVBQUUsTUFBTSw2QkFBNkIsQ0FBQztBQUVoRTs7Ozs7R0FLRztBQUNILHdCQUFzQixXQUFXLENBQy9CLEtBQUssRUFBRSxhQUFhLEVBQ3BCLEdBQUcsRUFBRSxZQUFZLEVBQ2pCLElBQUksQ0FBQyxFQUFFLFlBQVksR0FDbEIsT0FBTyxDQUFDO0lBQUUsY0FBYyxFQUFFLE1BQU0sQ0FBQztJQUFDLGFBQWEsRUFBRSxNQUFNLENBQUE7Q0FBRSxDQUFDLENBSTVEO0FBRUQsd0JBQXNCLGlCQUFpQixDQUNyQyxLQUFLLEVBQUUsb0JBQW9CLEVBQzNCLEdBQUcsRUFBRSxZQUFZLEVBQ2pCLElBQUksQ0FBQyxFQUFFLFlBQVksR0FDbEIsT0FBTyxDQUFDLE1BQU0sQ0FBQyxDQUdqQjtBQUVELHdCQUFnQix1QkFBdUIsQ0FBQyxLQUFLLEVBQUUsWUFBWSxHQUFHLEtBQUssSUFBSSxhQUFhLENBRW5GO0FBRUQsd0JBQWdCLGFBQWEsQ0FBQyxRQUFRLEVBQUUsWUFBWSxHQUFHLFFBQVEsSUFBSSxXQUFXLENBRTdFIn0=
@@ -1 +1 @@
1
- {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,2CAA2C,CAAC;AAC1F,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,YAAY,GAChB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAI5D;AAED,wBAAsB,iBAAiB,CAAC,KAAK,EAAE,wBAAwB,EAAE,GAAG,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,CAAC,CAG3G;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,aAAa,GAAG,wBAAwB,GAAG,KAAK,IAAI,aAAa,CAE/G"}
1
+ {"version":3,"file":"utils.d.ts","sourceRoot":"","sources":["../src/utils.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AACzD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,OAAO,KAAK,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAClF,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AACpE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAEhE;;;;;GAKG;AACH,wBAAsB,WAAW,CAC/B,KAAK,EAAE,aAAa,EACpB,GAAG,EAAE,YAAY,EACjB,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC;IAAE,cAAc,EAAE,MAAM,CAAC;IAAC,aAAa,EAAE,MAAM,CAAA;CAAE,CAAC,CAI5D;AAED,wBAAsB,iBAAiB,CACrC,KAAK,EAAE,oBAAoB,EAC3B,GAAG,EAAE,YAAY,EACjB,IAAI,CAAC,EAAE,YAAY,GAClB,OAAO,CAAC,MAAM,CAAC,CAGjB;AAED,wBAAgB,uBAAuB,CAAC,KAAK,EAAE,YAAY,GAAG,KAAK,IAAI,aAAa,CAEnF;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,QAAQ,IAAI,WAAW,CAE7E"}
package/dest/utils.js CHANGED
@@ -3,18 +3,27 @@
3
3
  * @param token - Token contract.
4
4
  * @param who - Address to get the balance for.
5
5
  * @returns - Private and public token balances as bigints.
6
- */ export async function getBalances(token, who) {
7
- const privateBalance = await token.methods.balance_of_private(who).simulate();
8
- const publicBalance = await token.methods.balance_of_public(who).simulate();
6
+ */ export async function getBalances(token, who, from) {
7
+ const privateBalance = await token.methods.balance_of_private(who).simulate({
8
+ from: from ?? who
9
+ });
10
+ const publicBalance = await token.methods.balance_of_public(who).simulate({
11
+ from: from ?? who
12
+ });
9
13
  return {
10
14
  privateBalance,
11
15
  publicBalance
12
16
  };
13
17
  }
14
- export async function getPrivateBalance(token, who) {
15
- const privateBalance = await token.methods.get_balance(who).simulate();
18
+ export async function getPrivateBalance(token, who, from) {
19
+ const privateBalance = await token.methods.get_balance(who).simulate({
20
+ from: from ?? who
21
+ });
16
22
  return privateBalance;
17
23
  }
18
24
  export function isStandardTokenContract(token) {
19
25
  return 'mint_to_public' in token.methods;
20
26
  }
27
+ export function isAMMContract(contract) {
28
+ return 'add_liquidity' in contract.methods;
29
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/bot",
3
- "version": "0.0.0-test.0",
3
+ "version": "0.0.1-commit.0208eb9",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -10,11 +10,9 @@
10
10
  "../package.common.json"
11
11
  ],
12
12
  "scripts": {
13
- "build": "yarn clean && tsc -b",
14
- "build:dev": "tsc -b --watch",
13
+ "build": "yarn clean && ../scripts/tsc.sh",
14
+ "build:dev": "../scripts/tsc.sh --watch",
15
15
  "clean": "rm -rf ./dest .tsbuildinfo",
16
- "formatting": "run -T prettier --check ./src && run -T eslint ./src",
17
- "formatting:fix": "run -T eslint --fix ./src && run -T prettier -w ./src",
18
16
  "bb": "node --no-warnings ./dest/bb/index.js",
19
17
  "test": "NODE_NO_WARNINGS=1 node --experimental-vm-modules ../node_modules/.bin/jest --passWithNoTests --maxWorkers=${JEST_MAX_WORKERS:-8}"
20
18
  },
@@ -49,33 +47,39 @@
49
47
  "testTimeout": 120000,
50
48
  "setupFiles": [
51
49
  "../../foundation/src/jest/setup.mjs"
50
+ ],
51
+ "testEnvironment": "../../foundation/src/jest/env.mjs",
52
+ "setupFilesAfterEnv": [
53
+ "../../foundation/src/jest/setupAfterEnv.mjs"
52
54
  ]
53
55
  },
54
56
  "dependencies": {
55
- "@aztec/accounts": "0.0.0-test.0",
56
- "@aztec/aztec.js": "0.0.0-test.0",
57
- "@aztec/entrypoints": "0.0.0-test.0",
58
- "@aztec/ethereum": "0.0.0-test.0",
59
- "@aztec/foundation": "0.0.0-test.0",
60
- "@aztec/noir-contracts.js": "0.0.0-test.0",
61
- "@aztec/noir-protocol-circuits-types": "0.0.0-test.0",
62
- "@aztec/protocol-contracts": "0.0.0-test.0",
63
- "@aztec/stdlib": "0.0.0-test.0",
64
- "@aztec/telemetry-client": "0.0.0-test.0",
57
+ "@aztec/accounts": "0.0.1-commit.0208eb9",
58
+ "@aztec/aztec.js": "0.0.1-commit.0208eb9",
59
+ "@aztec/entrypoints": "0.0.1-commit.0208eb9",
60
+ "@aztec/ethereum": "0.0.1-commit.0208eb9",
61
+ "@aztec/foundation": "0.0.1-commit.0208eb9",
62
+ "@aztec/kv-store": "0.0.1-commit.0208eb9",
63
+ "@aztec/noir-contracts.js": "0.0.1-commit.0208eb9",
64
+ "@aztec/noir-protocol-circuits-types": "0.0.1-commit.0208eb9",
65
+ "@aztec/protocol-contracts": "0.0.1-commit.0208eb9",
66
+ "@aztec/stdlib": "0.0.1-commit.0208eb9",
67
+ "@aztec/telemetry-client": "0.0.1-commit.0208eb9",
68
+ "@aztec/test-wallet": "0.0.1-commit.0208eb9",
65
69
  "source-map-support": "^0.5.21",
66
70
  "tslib": "^2.4.0",
67
71
  "zod": "^3.23.8"
68
72
  },
69
73
  "devDependencies": {
70
- "@jest/globals": "^29.5.0",
71
- "@types/jest": "^29.5.0",
72
- "@types/memdown": "^3.0.0",
73
- "@types/node": "^18.7.23",
74
+ "@jest/globals": "^30.0.0",
75
+ "@types/jest": "^30.0.0",
76
+ "@types/node": "^22.15.17",
74
77
  "@types/source-map-support": "^0.5.10",
75
- "jest": "^29.5.0",
76
- "jest-mock-extended": "^3.0.3",
78
+ "@typescript/native-preview": "7.0.0-dev.20260113.1",
79
+ "jest": "^30.0.0",
80
+ "jest-mock-extended": "^4.0.0",
77
81
  "ts-node": "^10.9.1",
78
- "typescript": "^5.0.4"
82
+ "typescript": "^5.3.3"
79
83
  },
80
84
  "files": [
81
85
  "dest",
@@ -84,6 +88,6 @@
84
88
  ],
85
89
  "types": "./dest/index.d.ts",
86
90
  "engines": {
87
- "node": ">=18"
91
+ "node": ">=20.10"
88
92
  }
89
93
  }
package/src/amm_bot.ts ADDED
@@ -0,0 +1,124 @@
1
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import { NO_WAIT } from '@aztec/aztec.js/contracts';
3
+ import { Fr } from '@aztec/aztec.js/fields';
4
+ import { TxHash, TxReceipt } from '@aztec/aztec.js/tx';
5
+ import { jsonStringify } from '@aztec/foundation/json-rpc';
6
+ import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
7
+ import type { TokenContract } from '@aztec/noir-contracts.js/Token';
8
+ import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
9
+ import type { TestWallet } from '@aztec/test-wallet/server';
10
+
11
+ import { BaseBot } from './base_bot.js';
12
+ import type { BotConfig } from './config.js';
13
+ import { BotFactory } from './factory.js';
14
+ import type { BotStore } from './store/index.js';
15
+
16
+ const TRANSFER_BASE_AMOUNT = 1_000;
17
+ const TRANSFER_VARIANCE = 200;
18
+
19
+ type Balances = { token0: bigint; token1: bigint };
20
+
21
+ export class AmmBot extends BaseBot {
22
+ protected constructor(
23
+ node: AztecNode,
24
+ wallet: TestWallet,
25
+ defaultAccountAddress: AztecAddress,
26
+ public readonly amm: AMMContract,
27
+ public readonly token0: TokenContract,
28
+ public readonly token1: TokenContract,
29
+ config: BotConfig,
30
+ ) {
31
+ super(node, wallet, defaultAccountAddress, config);
32
+ }
33
+
34
+ static async create(
35
+ config: BotConfig,
36
+ wallet: TestWallet,
37
+ aztecNode: AztecNode,
38
+ aztecNodeAdmin: AztecNodeAdmin | undefined,
39
+ store: BotStore,
40
+ ): Promise<AmmBot> {
41
+ const { defaultAccountAddress, token0, token1, amm } = await new BotFactory(
42
+ config,
43
+ wallet,
44
+ store,
45
+ aztecNode,
46
+ aztecNodeAdmin,
47
+ ).setupAmm();
48
+ return new AmmBot(aztecNode, wallet, defaultAccountAddress, amm, token0, token1, config);
49
+ }
50
+
51
+ protected async createAndSendTx(logCtx: object): Promise<TxHash> {
52
+ const { feePaymentMethod } = this.config;
53
+ const { wallet, amm, token0, token1 } = this;
54
+
55
+ const balances = this.getBalances();
56
+ this.log.info(`Preparing tx with ${feePaymentMethod} fee to swap tokens. Balances: ${jsonStringify(balances)}`, {
57
+ ...logCtx,
58
+ balances,
59
+ });
60
+
61
+ // 1000 ± 200
62
+ const amountIn = Math.floor(TRANSFER_BASE_AMOUNT + (Math.random() - 0.5) * TRANSFER_VARIANCE);
63
+ const authwitNonce = Fr.random();
64
+
65
+ const [tokenIn, tokenOut] = Math.random() < 0.5 ? [token0, token1] : [token1, token0];
66
+
67
+ const swapAuthwit = await wallet.createAuthWit(this.defaultAccountAddress, {
68
+ caller: amm.address,
69
+ call: await tokenIn.methods
70
+ .transfer_to_public(this.defaultAccountAddress, amm.address, amountIn, authwitNonce)
71
+ .getFunctionCall(),
72
+ });
73
+
74
+ const amountOutMin = await amm.methods
75
+ .get_amount_out_for_exact_in(
76
+ await tokenIn.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }),
77
+ await tokenOut.methods.balance_of_public(amm.address).simulate({ from: this.defaultAccountAddress }),
78
+ amountIn,
79
+ )
80
+ .simulate({ from: this.defaultAccountAddress });
81
+
82
+ const swapExactTokensInteraction = amm.methods
83
+ .swap_exact_tokens_for_tokens(tokenIn.address, tokenOut.address, amountIn, amountOutMin, authwitNonce)
84
+ .with({
85
+ authWitnesses: [swapAuthwit],
86
+ });
87
+
88
+ const opts = await this.getSendMethodOpts(swapExactTokensInteraction);
89
+
90
+ this.log.verbose(`Sending transaction`, logCtx);
91
+ this.log.info(`Tx. Balances: ${jsonStringify(balances)}`, { ...logCtx, balances });
92
+ return swapExactTokensInteraction.send({ ...opts, wait: NO_WAIT });
93
+ }
94
+
95
+ protected override async onTxMined(receipt: TxReceipt, logCtx: object): Promise<void> {
96
+ const balances = await this.getBalances();
97
+ this.log.info(`Balances after swap in tx ${receipt.txHash}: ${jsonStringify(balances)}`, { ...logCtx, balances });
98
+ }
99
+
100
+ public getAmmBalances(): Promise<Balances> {
101
+ return this.getPublicBalanceFor(this.amm.address);
102
+ }
103
+
104
+ public async getBalances(): Promise<{ senderPublic: Balances; senderPrivate: Balances; amm: Balances }> {
105
+ return {
106
+ senderPublic: await this.getPublicBalanceFor(this.defaultAccountAddress),
107
+ senderPrivate: await this.getPrivateBalanceFor(this.defaultAccountAddress),
108
+ amm: await this.getPublicBalanceFor(this.amm.address, this.defaultAccountAddress),
109
+ };
110
+ }
111
+
112
+ private async getPublicBalanceFor(address: AztecAddress, from?: AztecAddress): Promise<Balances> {
113
+ return {
114
+ token0: await this.token0.methods.balance_of_public(address).simulate({ from: from ?? address }),
115
+ token1: await this.token1.methods.balance_of_public(address).simulate({ from: from ?? address }),
116
+ };
117
+ }
118
+ private async getPrivateBalanceFor(address: AztecAddress, from?: AztecAddress): Promise<Balances> {
119
+ return {
120
+ token0: await this.token0.methods.balance_of_private(address).simulate({ from: from ?? address }),
121
+ token1: await this.token1.methods.balance_of_private(address).simulate({ from: from ?? address }),
122
+ };
123
+ }
124
+ }
@@ -0,0 +1,92 @@
1
+ import { AztecAddress } from '@aztec/aztec.js/addresses';
2
+ import {
3
+ BatchCall,
4
+ ContractFunctionInteraction,
5
+ type SendInteractionOptions,
6
+ waitForProven,
7
+ } from '@aztec/aztec.js/contracts';
8
+ import { createLogger } from '@aztec/aztec.js/log';
9
+ import { waitForTx } from '@aztec/aztec.js/node';
10
+ import { TxHash, TxReceipt } from '@aztec/aztec.js/tx';
11
+ import { Gas } from '@aztec/stdlib/gas';
12
+ import type { AztecNode } from '@aztec/stdlib/interfaces/client';
13
+ import type { TestWallet } from '@aztec/test-wallet/server';
14
+
15
+ import type { BotConfig } from './config.js';
16
+
17
+ export abstract class BaseBot {
18
+ protected log = createLogger('bot');
19
+
20
+ protected attempts: number = 0;
21
+ protected successes: number = 0;
22
+
23
+ protected constructor(
24
+ public readonly node: AztecNode,
25
+ public readonly wallet: TestWallet,
26
+ public readonly defaultAccountAddress: AztecAddress,
27
+ public config: BotConfig,
28
+ ) {}
29
+
30
+ public async run(): Promise<TxReceipt | TxHash> {
31
+ this.attempts++;
32
+ const logCtx = { runId: Date.now() * 1000 + Math.floor(Math.random() * 1000) };
33
+ const { followChain, txMinedWaitSeconds } = this.config;
34
+
35
+ this.log.verbose(`Creating tx`, logCtx);
36
+ const txHash = await this.createAndSendTx(logCtx);
37
+
38
+ if (followChain === 'NONE') {
39
+ this.log.info(`Transaction ${txHash.toString()} sent, not waiting for it to be mined`);
40
+ return txHash;
41
+ }
42
+
43
+ this.log.verbose(
44
+ `Awaiting tx ${txHash.toString()} to be on the ${followChain} chain (timeout ${txMinedWaitSeconds}s)`,
45
+ logCtx,
46
+ );
47
+ const receipt = await waitForTx(this.node, txHash, { timeout: txMinedWaitSeconds });
48
+ if (followChain === 'PROVEN') {
49
+ await waitForProven(this.node, receipt, { provenTimeout: txMinedWaitSeconds });
50
+ }
51
+ this.successes++;
52
+ this.log.info(
53
+ `Tx #${this.attempts} ${receipt.txHash} successfully mined in block ${receipt.blockNumber} (stats: ${this.successes}/${this.attempts} success)`,
54
+ logCtx,
55
+ );
56
+
57
+ await this.onTxMined(receipt, logCtx);
58
+
59
+ return receipt;
60
+ }
61
+
62
+ protected abstract createAndSendTx(logCtx: object): Promise<TxHash>;
63
+
64
+ protected onTxMined(_receipt: TxReceipt, _logCtx: object): Promise<void> {
65
+ // no-op
66
+ return Promise.resolve();
67
+ }
68
+
69
+ protected async getSendMethodOpts(
70
+ interaction: ContractFunctionInteraction | BatchCall,
71
+ ): Promise<SendInteractionOptions> {
72
+ const { l2GasLimit, daGasLimit, minFeePadding } = this.config;
73
+
74
+ this.wallet.setMinFeePadding(minFeePadding);
75
+
76
+ let gasSettings;
77
+ if (l2GasLimit !== undefined && l2GasLimit > 0 && daGasLimit !== undefined && daGasLimit > 0) {
78
+ gasSettings = { gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) };
79
+ this.log.verbose(`Using gas limits ${l2GasLimit} L2 gas ${daGasLimit} DA gas`);
80
+ } else {
81
+ this.log.verbose(`Estimating gas for transaction`);
82
+ ({ estimatedGas: gasSettings } = await interaction.simulate({
83
+ fee: { estimateGas: true },
84
+ from: this.defaultAccountAddress,
85
+ }));
86
+ }
87
+ return {
88
+ from: this.defaultAccountAddress,
89
+ fee: { gasSettings },
90
+ };
91
+ }
92
+ }