@aztec/bot 0.80.0 → 0.82.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dest/factory.js CHANGED
@@ -3,6 +3,7 @@ import { getDeployedTestAccountsWallets, getInitialTestAccounts } from '@aztec/a
3
3
  import { BatchCall, FeeJuicePaymentMethodWithClaim, L1FeeJuicePortalManager, createLogger, createPXEClient, retryUntil } from '@aztec/aztec.js';
4
4
  import { createEthereumChain, createL1Clients } from '@aztec/ethereum';
5
5
  import { Fr } from '@aztec/foundation/fields';
6
+ import { AMMContract } from '@aztec/noir-contracts.js/AMM';
6
7
  import { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken';
7
8
  import { TokenContract } from '@aztec/noir-contracts.js/Token';
8
9
  import { deriveSigningKey } from '@aztec/stdlib/keys';
@@ -15,12 +16,13 @@ export class BotFactory {
15
16
  config;
16
17
  pxe;
17
18
  node;
19
+ nodeAdmin;
18
20
  log;
19
- constructor(config, dependencies = {}){
21
+ constructor(config, dependencies){
20
22
  this.config = config;
21
23
  this.log = createLogger('bot');
22
- if (config.flushSetupTransactions && !dependencies.node) {
23
- throw new Error(`Either a node client or node url must be provided if transaction flushing is requested`);
24
+ if (config.flushSetupTransactions && !dependencies.nodeAdmin) {
25
+ throw new Error(`Either a node admin client or node admin url must be provided if transaction flushing is requested`);
24
26
  }
25
27
  if (config.senderPrivateKey && !dependencies.node) {
26
28
  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`);
@@ -29,6 +31,7 @@ export class BotFactory {
29
31
  throw new Error(`Either a PXE client or a PXE URL must be provided`);
30
32
  }
31
33
  this.node = dependencies.node;
34
+ this.nodeAdmin = dependencies.nodeAdmin;
32
35
  if (dependencies.pxe) {
33
36
  this.log.info(`Using local PXE`);
34
37
  this.pxe = dependencies.pxe;
@@ -56,6 +59,22 @@ export class BotFactory {
56
59
  recipient
57
60
  };
58
61
  }
62
+ async setupAmm() {
63
+ const wallet = await this.setupAccount();
64
+ const token0 = await this.setupTokenContract(wallet, this.config.tokenSalt, 'BotToken0', 'BOT0');
65
+ const token1 = await this.setupTokenContract(wallet, this.config.tokenSalt, 'BotToken1', 'BOT1');
66
+ const liquidityToken = await this.setupTokenContract(wallet, this.config.tokenSalt, 'BotLPToken', 'BOTLP');
67
+ const amm = await this.setupAmmContract(wallet, this.config.tokenSalt, token0, token1, liquidityToken);
68
+ await this.fundAmm(wallet, amm, token0, token1);
69
+ this.log.info(`AMM initialized and funded`);
70
+ return {
71
+ wallet,
72
+ amm,
73
+ token0,
74
+ token1,
75
+ pxe: this.pxe
76
+ };
77
+ }
59
78
  /**
60
79
  * Checks if the sender account contract is initialized, and initializes it if necessary.
61
80
  * @returns The sender wallet.
@@ -67,7 +86,7 @@ export class BotFactory {
67
86
  }
68
87
  }
69
88
  async setupAccountWithPrivateKey(privateKey) {
70
- const salt = Fr.ONE;
89
+ const salt = this.config.senderSalt ?? Fr.ONE;
71
90
  const signingKey = deriveSigningKey(privateKey);
72
91
  const account = await getSchnorrAccount(this.pxe, privateKey, signingKey, salt);
73
92
  const isInit = (await this.pxe.getContractMetadata(account.getAddress())).isContractInitialized;
@@ -78,7 +97,8 @@ export class BotFactory {
78
97
  } else {
79
98
  const address = account.getAddress();
80
99
  this.log.info(`Deploying account at ${address}`);
81
- const claim = await this.bridgeL1FeeJuice(address, 10n ** 22n);
100
+ const claim = await this.bridgeL1FeeJuice(address);
101
+ // docs:start:claim_and_deploy
82
102
  const wallet = await account.getWallet();
83
103
  const paymentMethod = new FeeJuicePaymentMethodWithClaim(wallet, claim);
84
104
  const sentTx = account.deploy({
@@ -87,6 +107,7 @@ export class BotFactory {
87
107
  }
88
108
  });
89
109
  const txHash = await sentTx.getTxHash();
110
+ // docs:end:claim_and_deploy
90
111
  this.log.info(`Sent tx with hash ${txHash.toString()}`);
91
112
  await this.tryFlushTxs();
92
113
  this.log.verbose('Waiting for account deployment to settle');
@@ -154,6 +175,87 @@ export class BotFactory {
154
175
  }
155
176
  }
156
177
  /**
178
+ * Checks if the token contract is deployed and deploys it if necessary.
179
+ * @param wallet - Wallet to deploy the token contract from.
180
+ * @returns The TokenContract instance.
181
+ */ setupTokenContract(wallet, contractAddressSalt, name, ticker, decimals = 18) {
182
+ const deployOpts = {
183
+ contractAddressSalt,
184
+ universalDeploy: true
185
+ };
186
+ const deploy = TokenContract.deploy(wallet, wallet.getAddress(), name, ticker, decimals);
187
+ return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
188
+ }
189
+ async setupAmmContract(wallet, contractAddressSalt, token0, token1, lpToken) {
190
+ const deployOpts = {
191
+ contractAddressSalt,
192
+ universalDeploy: true
193
+ };
194
+ const deploy = AMMContract.deploy(wallet, token0.address, token1.address, lpToken.address);
195
+ const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
196
+ this.log.info(`AMM deployed at ${amm.address}`);
197
+ const minterTx = lpToken.methods.set_minter(amm.address, true).send();
198
+ this.log.info(`Set LP token minter to AMM txHash=${await minterTx.getTxHash()}`);
199
+ await minterTx.wait({
200
+ timeout: this.config.txMinedWaitSeconds
201
+ });
202
+ this.log.info(`Liquidity token initialized`);
203
+ return amm;
204
+ }
205
+ async fundAmm(wallet, amm, token0, token1) {
206
+ const nonce = Fr.random();
207
+ // keep some tokens for swapping
208
+ const amount0Max = MINT_BALANCE / 2;
209
+ const amount0Min = MINT_BALANCE / 4;
210
+ const amount1Max = MINT_BALANCE / 2;
211
+ const amount1Min = MINT_BALANCE / 4;
212
+ const token0Authwit = await wallet.createAuthWit({
213
+ caller: amm.address,
214
+ action: token0.methods.transfer_to_public(wallet.getAddress(), amm.address, amount0Max, nonce)
215
+ });
216
+ const token1Authwit = await wallet.createAuthWit({
217
+ caller: amm.address,
218
+ action: token1.methods.transfer_to_public(wallet.getAddress(), amm.address, amount1Max, nonce)
219
+ });
220
+ this.log.info(`Minting tokens`);
221
+ const mintTx = new BatchCall(wallet, [
222
+ token0.methods.mint_to_private(wallet.getAddress(), wallet.getAddress(), MINT_BALANCE),
223
+ token1.methods.mint_to_private(wallet.getAddress(), wallet.getAddress(), MINT_BALANCE)
224
+ ]).send();
225
+ this.log.info(`Sent mint tx: ${await mintTx.getTxHash()}`);
226
+ await mintTx.wait({
227
+ timeout: this.config.txMinedWaitSeconds
228
+ });
229
+ this.log.info(`Funding AMM`);
230
+ const addLiquidityTx = amm.methods.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, nonce).send({
231
+ authWitnesses: [
232
+ token0Authwit,
233
+ token1Authwit
234
+ ]
235
+ });
236
+ this.log.info(`Sent tx to add liquidity to the AMM: ${await addLiquidityTx.getTxHash()}`);
237
+ await addLiquidityTx.wait({
238
+ timeout: this.config.txMinedWaitSeconds
239
+ });
240
+ }
241
+ async registerOrDeployContract(name, deploy, deployOpts) {
242
+ const address = (await deploy.getInstance(deployOpts)).address;
243
+ if ((await this.pxe.getContractMetadata(address)).isContractPubliclyDeployed) {
244
+ this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
245
+ return deploy.register();
246
+ } else {
247
+ this.log.info(`Deploying contract ${name} at ${address.toString()}`);
248
+ const sentTx = deploy.send(deployOpts);
249
+ const txHash = await sentTx.getTxHash();
250
+ this.log.info(`Sent tx with hash ${txHash.toString()}`);
251
+ await this.tryFlushTxs();
252
+ this.log.verbose(`Waiting for contract ${name} setup to settle`);
253
+ return sentTx.deployed({
254
+ timeout: this.config.txMinedWaitSeconds
255
+ });
256
+ }
257
+ }
258
+ /**
157
259
  * Mints private and public tokens for the sender if their balance is below the minimum.
158
260
  * @param token - Token contract.
159
261
  */ async mintTokens(token) {
@@ -189,7 +291,7 @@ export class BotFactory {
189
291
  timeout: this.config.txMinedWaitSeconds
190
292
  });
191
293
  }
192
- async bridgeL1FeeJuice(recipient, amount) {
294
+ async bridgeL1FeeJuice(recipient) {
193
295
  const l1RpcUrls = this.config.l1RpcUrls;
194
296
  if (!l1RpcUrls?.length) {
195
297
  throw new Error('L1 Rpc url is required to bridge the fee juice to fund the deployment of the account.');
@@ -202,10 +304,11 @@ export class BotFactory {
202
304
  const chain = createEthereumChain(l1RpcUrls, l1ChainId);
203
305
  const { publicClient, walletClient } = createL1Clients(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
204
306
  const portal = await L1FeeJuicePortalManager.new(this.pxe, publicClient, walletClient, this.log);
205
- const claim = await portal.bridgeTokensPublic(recipient, amount, true);
307
+ const mintAmount = await portal.getTokenManager().getMintAmount();
308
+ const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true);
206
309
  const isSynced = async ()=>await this.pxe.isL1ToL2MessageSynced(Fr.fromHexString(claim.messageHash));
207
310
  await retryUntil(isSynced, `message ${claim.messageHash} sync`, 24, 1);
208
- this.log.info(`Created a claim for ${amount} L1 fee juice to ${recipient}.`, claim);
311
+ this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
209
312
  // Progress by 2 L2 blocks so that the l1ToL2Message added above will be available to use on L2.
210
313
  await this.advanceL2Block();
211
314
  await this.advanceL2Block();
@@ -220,7 +323,7 @@ export class BotFactory {
220
323
  if (this.config.flushSetupTransactions) {
221
324
  this.log.verbose('Flushing transactions');
222
325
  try {
223
- await this.node.flushTxs();
326
+ await this.nodeAdmin.flushTxs();
224
327
  } catch (err) {
225
328
  this.log.error(`Failed to flush transactions: ${err}`);
226
329
  }
package/dest/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Bot } from './bot.js';
2
+ export { AmmBot } from './amm_bot.js';
2
3
  export { BotRunner } from './runner.js';
3
4
  export { type BotConfig, getBotConfigFromEnv, getBotDefaultConfig, botConfigMappings, SupportedTokenContracts, } from './config.js';
4
5
  export { createBotRunnerRpcServer, getBotRunnerApiHandler } from './rpc.js';
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,KAAK,SAAS,EACd,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAC5E,cAAc,gBAAgB,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EACL,KAAK,SAAS,EACd,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,wBAAwB,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAC5E,cAAc,gBAAgB,CAAC"}
package/dest/index.js CHANGED
@@ -1,4 +1,5 @@
1
1
  export { Bot } from './bot.js';
2
+ export { AmmBot } from './amm_bot.js';
2
3
  export { BotRunner } from './runner.js';
3
4
  export { getBotConfigFromEnv, getBotDefaultConfig, botConfigMappings, SupportedTokenContracts } from './config.js';
4
5
  export { createBotRunnerRpcServer, getBotRunnerApiHandler } from './rpc.js';
package/dest/runner.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import { type AztecNode, type PXE } from '@aztec/aztec.js';
2
+ import { type AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
2
3
  import { type TelemetryClient, type Traceable, type Tracer } from '@aztec/telemetry-client';
3
4
  import { type BotConfig } from './config.js';
4
5
  import type { BotRunnerApi } from './interface.js';
@@ -9,6 +10,7 @@ export declare class BotRunner implements BotRunnerApi, Traceable {
9
10
  private bot?;
10
11
  private pxe?;
11
12
  private node;
13
+ private nodeAdmin?;
12
14
  private runningPromise;
13
15
  private consecutiveErrors;
14
16
  private healthy;
@@ -16,6 +18,7 @@ export declare class BotRunner implements BotRunnerApi, Traceable {
16
18
  constructor(config: BotConfig, dependencies: {
17
19
  pxe?: PXE;
18
20
  node?: AztecNode;
21
+ nodeAdmin?: AztecNodeAdmin;
19
22
  telemetry: TelemetryClient;
20
23
  });
21
24
  /** Initializes the bot if needed. Blocks until the bot setup is finished. */
@@ -1 +1 @@
1
- {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAuC,MAAM,iBAAiB,CAAC;AAEhG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAA8B,MAAM,yBAAyB,CAAC;AAGxH,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,qBAAa,SAAU,YAAW,YAAY,EAAE,SAAS;;IAYrD,OAAO,CAAC,MAAM;IAXhB,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,GAAG,CAAC,CAAe;IAC3B,OAAO,CAAC,GAAG,CAAC,CAAM;IAClB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,OAAO,CAAQ;IAEvB,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAGrB,MAAM,EAAE,SAAS,EACzB,YAAY,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAC;QAAC,SAAS,EAAE,eAAe,CAAA;KAAE;IAY3E,6EAA6E;IAChE,KAAK;YAOJ,OAAO;IAMrB;;;OAGG;IACU,KAAK;IAQlB;;OAEG;IACU,IAAI;IAQV,SAAS;IAIhB,0CAA0C;IACnC,SAAS;IAIhB;;;OAGG;IACU,MAAM,CAAC,MAAM,EAAE,SAAS;IAerC;;;OAGG;IACU,GAAG;IAwBhB,qDAAqD;IAC9C,SAAS;CAuCjB"}
1
+ {"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,SAAS,EAAE,KAAK,GAAG,EAAuC,MAAM,iBAAiB,CAAC;AAEhG,OAAO,EAAE,KAAK,cAAc,EAA8B,MAAM,iCAAiC,CAAC;AAClG,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAA8B,MAAM,yBAAyB,CAAC;AAKxH,OAAO,EAAE,KAAK,SAAS,EAAe,MAAM,aAAa,CAAC;AAC1D,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAEnD,qBAAa,SAAU,YAAW,YAAY,EAAE,SAAS;;IAarD,OAAO,CAAC,MAAM;IAZhB,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,GAAG,CAAC,CAAmB;IAC/B,OAAO,CAAC,GAAG,CAAC,CAAM;IAClB,OAAO,CAAC,IAAI,CAAY;IACxB,OAAO,CAAC,SAAS,CAAC,CAAiB;IACnC,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,OAAO,CAAQ;IAEvB,SAAgB,MAAM,EAAE,MAAM,CAAC;gBAGrB,MAAM,EAAE,SAAS,EACzB,YAAY,EAAE;QAAE,GAAG,CAAC,EAAE,GAAG,CAAC;QAAC,IAAI,CAAC,EAAE,SAAS,CAAC;QAAC,SAAS,CAAC,EAAE,cAAc,CAAC;QAAC,SAAS,EAAE,eAAe,CAAA;KAAE;IAgBvG,6EAA6E;IAChE,KAAK;YAOJ,OAAO;IAMrB;;;OAGG;IACU,KAAK;IAQlB;;OAEG;IACU,IAAI;IAQV,SAAS;IAIhB,0CAA0C;IACnC,SAAS;IAIhB;;;OAGG;IACU,MAAM,CAAC,MAAM,EAAE,SAAS;IAerC;;;OAGG;IACU,GAAG;IAwBhB,qDAAqD;IAC9C,SAAS;CAyCjB"}
package/dest/runner.js CHANGED
@@ -6,7 +6,9 @@ function _ts_decorate(decorators, target, key, desc) {
6
6
  }
7
7
  import { createAztecNodeClient, createLogger } from '@aztec/aztec.js';
8
8
  import { RunningPromise } from '@aztec/foundation/running-promise';
9
+ import { createAztecNodeAdminClient } from '@aztec/stdlib/interfaces/client';
9
10
  import { makeTracedFetch, trackSpan } from '@aztec/telemetry-client';
11
+ import { AmmBot } from './amm_bot.js';
10
12
  import { Bot } from './bot.js';
11
13
  import { getVersions } from './config.js';
12
14
  export class BotRunner {
@@ -15,6 +17,7 @@ export class BotRunner {
15
17
  bot;
16
18
  pxe;
17
19
  node;
20
+ nodeAdmin;
18
21
  runningPromise;
19
22
  consecutiveErrors;
20
23
  healthy;
@@ -29,11 +32,14 @@ export class BotRunner {
29
32
  if (!dependencies.node && !config.nodeUrl) {
30
33
  throw new Error(`Missing node URL in config or dependencies`);
31
34
  }
32
- this.node = dependencies.node ?? createAztecNodeClient(config.nodeUrl, getVersions(), makeTracedFetch([
35
+ const versions = getVersions();
36
+ const fetch = makeTracedFetch([
33
37
  1,
34
38
  2,
35
39
  3
36
- ], true));
40
+ ], true);
41
+ this.node = dependencies.node ?? createAztecNodeClient(config.nodeUrl, versions, fetch);
42
+ this.nodeAdmin = dependencies.nodeAdmin ?? (config.nodeAdminUrl ? createAztecNodeAdminClient(config.nodeAdminUrl, versions, fetch) : undefined);
37
43
  this.runningPromise = new RunningPromise(()=>this.#work(), this.log, config.txIntervalSeconds * 1000);
38
44
  }
39
45
  /** Initializes the bot if needed. Blocks until the bot setup is finished. */ async setup() {
@@ -120,9 +126,14 @@ export class BotRunner {
120
126
  }
121
127
  async #createBot() {
122
128
  try {
123
- this.bot = Bot.create(this.config, {
129
+ this.bot = this.config.ammTxs ? AmmBot.create(this.config, {
124
130
  pxe: this.pxe,
125
- node: this.node
131
+ node: this.node,
132
+ nodeAdmin: this.nodeAdmin
133
+ }) : Bot.create(this.config, {
134
+ pxe: this.pxe,
135
+ node: this.node,
136
+ nodeAdmin: this.nodeAdmin
126
137
  });
127
138
  await this.bot;
128
139
  } catch (err) {
package/dest/utils.d.ts CHANGED
@@ -1,3 +1,5 @@
1
+ import type { ContractBase } from '@aztec/aztec.js';
2
+ import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
1
3
  import type { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken';
2
4
  import type { TokenContract } from '@aztec/noir-contracts.js/Token';
3
5
  import type { AztecAddress } from '@aztec/stdlib/aztec-address';
@@ -12,5 +14,6 @@ export declare function getBalances(token: TokenContract, who: AztecAddress): Pr
12
14
  publicBalance: bigint;
13
15
  }>;
14
16
  export declare function getPrivateBalance(token: EasyPrivateTokenContract, who: AztecAddress): Promise<bigint>;
15
- export declare function isStandardTokenContract(token: TokenContract | EasyPrivateTokenContract): token is TokenContract;
17
+ export declare function isStandardTokenContract(token: ContractBase): token is TokenContract;
18
+ export declare function isAMMContract(contract: ContractBase): contract is AMMContract;
16
19
  //# sourceMappingURL=utils.d.ts.map
@@ -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,KAAK,EAAE,YAAY,EAAE,MAAM,iBAAiB,CAAC;AACpD,OAAO,KAAK,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAChE,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,YAAY,GAAG,KAAK,IAAI,aAAa,CAEnF;AAED,wBAAgB,aAAa,CAAC,QAAQ,EAAE,YAAY,GAAG,QAAQ,IAAI,WAAW,CAE7E"}
package/dest/utils.js CHANGED
@@ -18,3 +18,6 @@ export async function getPrivateBalance(token, who) {
18
18
  export function isStandardTokenContract(token) {
19
19
  return 'mint_to_public' in token.methods;
20
20
  }
21
+ export function isAMMContract(contract) {
22
+ return 'add_liquidity' in contract.methods;
23
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@aztec/bot",
3
- "version": "0.80.0",
3
+ "version": "0.82.0",
4
4
  "type": "module",
5
5
  "exports": {
6
6
  ".": "./dest/index.js",
@@ -52,16 +52,16 @@
52
52
  ]
53
53
  },
54
54
  "dependencies": {
55
- "@aztec/accounts": "0.80.0",
56
- "@aztec/aztec.js": "0.80.0",
57
- "@aztec/entrypoints": "0.80.0",
58
- "@aztec/ethereum": "0.80.0",
59
- "@aztec/foundation": "0.80.0",
60
- "@aztec/noir-contracts.js": "0.80.0",
61
- "@aztec/noir-protocol-circuits-types": "0.80.0",
62
- "@aztec/protocol-contracts": "0.80.0",
63
- "@aztec/stdlib": "0.80.0",
64
- "@aztec/telemetry-client": "0.80.0",
55
+ "@aztec/accounts": "0.82.0",
56
+ "@aztec/aztec.js": "0.82.0",
57
+ "@aztec/entrypoints": "0.82.0",
58
+ "@aztec/ethereum": "0.82.0",
59
+ "@aztec/foundation": "0.82.0",
60
+ "@aztec/noir-contracts.js": "0.82.0",
61
+ "@aztec/noir-protocol-circuits-types": "0.82.0",
62
+ "@aztec/protocol-contracts": "0.82.0",
63
+ "@aztec/stdlib": "0.82.0",
64
+ "@aztec/telemetry-client": "0.82.0",
65
65
  "source-map-support": "^0.5.21",
66
66
  "tslib": "^2.4.0",
67
67
  "zod": "^3.23.8"
package/src/amm_bot.ts ADDED
@@ -0,0 +1,93 @@
1
+ import { AztecAddress, Fr, SentTx, type Wallet } from '@aztec/aztec.js';
2
+ import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
3
+ import type { TokenContract } from '@aztec/noir-contracts.js/Token';
4
+ import type { AztecNode, AztecNodeAdmin, PXE } from '@aztec/stdlib/interfaces/client';
5
+
6
+ import { BaseBot } from './base_bot.js';
7
+ import type { BotConfig } from './config.js';
8
+ import { BotFactory } from './factory.js';
9
+
10
+ const TRANSFER_AMOUNT = 1_000;
11
+
12
+ type Balances = { token0: bigint; token1: bigint };
13
+
14
+ export class AmmBot extends BaseBot {
15
+ protected constructor(
16
+ pxe: PXE,
17
+ wallet: Wallet,
18
+ public readonly amm: AMMContract,
19
+ public readonly token0: TokenContract,
20
+ public readonly token1: TokenContract,
21
+ config: BotConfig,
22
+ ) {
23
+ super(pxe, wallet, config);
24
+ }
25
+
26
+ static async create(
27
+ config: BotConfig,
28
+ dependencies: { pxe?: PXE; node?: AztecNode; nodeAdmin?: AztecNodeAdmin },
29
+ ): Promise<AmmBot> {
30
+ const { pxe, wallet, token0, token1, amm } = await new BotFactory(config, dependencies).setupAmm();
31
+ return new AmmBot(pxe, wallet, amm, token0, token1, config);
32
+ }
33
+
34
+ protected async createAndSendTx(logCtx: object): Promise<SentTx> {
35
+ const { feePaymentMethod } = this.config;
36
+ const { wallet, amm, token0, token1 } = this;
37
+
38
+ this.log.verbose(`Preparing tx with ${feePaymentMethod} fee to swap tokens`, logCtx);
39
+
40
+ const ammBalances = await this.getAmmBalances();
41
+ const amountIn = TRANSFER_AMOUNT;
42
+ const nonce = Fr.random();
43
+
44
+ const swapAuthwit = await wallet.createAuthWit({
45
+ caller: amm.address,
46
+ action: token0.methods.transfer_to_public(wallet.getAddress(), amm.address, amountIn, nonce),
47
+ });
48
+
49
+ const amountOutMin = await amm.methods
50
+ .get_amount_out_for_exact_in(ammBalances.token0, ammBalances.token1, amountIn)
51
+ .simulate();
52
+
53
+ const swapExactTokensInteraction = amm.methods.swap_exact_tokens_for_tokens(
54
+ token0.address,
55
+ token1.address,
56
+ amountIn,
57
+ amountOutMin,
58
+ nonce,
59
+ );
60
+
61
+ const opts = this.getSendMethodOpts(swapAuthwit);
62
+
63
+ this.log.verbose(`Proving transaction`, logCtx);
64
+ const tx = await swapExactTokensInteraction.prove(opts);
65
+
66
+ return tx.send();
67
+ }
68
+
69
+ public getAmmBalances(): Promise<Balances> {
70
+ return this.getPublicBalanceFor(this.amm.address);
71
+ }
72
+
73
+ public async getBalances(): Promise<{ senderPublic: Balances; senderPrivate: Balances; amm: Balances }> {
74
+ return {
75
+ senderPublic: await this.getPublicBalanceFor(this.wallet.getAddress()),
76
+ senderPrivate: await this.getPrivateBalanceFor(this.wallet.getAddress()),
77
+ amm: await this.getPublicBalanceFor(this.amm.address),
78
+ };
79
+ }
80
+
81
+ private async getPublicBalanceFor(address: AztecAddress): Promise<Balances> {
82
+ return {
83
+ token0: await this.token0.methods.balance_of_public(address).simulate(),
84
+ token1: await this.token1.methods.balance_of_public(address).simulate(),
85
+ };
86
+ }
87
+ private async getPrivateBalanceFor(address: AztecAddress): Promise<Balances> {
88
+ return {
89
+ token0: await this.token0.methods.balance_of_private(address).simulate(),
90
+ token1: await this.token1.methods.balance_of_private(address).simulate(),
91
+ };
92
+ }
93
+ }
@@ -0,0 +1,75 @@
1
+ import {
2
+ AuthWitness,
3
+ FeeJuicePaymentMethod,
4
+ type SendMethodOptions,
5
+ SentTx,
6
+ type Wallet,
7
+ createLogger,
8
+ waitForProven,
9
+ } from '@aztec/aztec.js';
10
+ import { Gas } from '@aztec/stdlib/gas';
11
+ import type { PXE } from '@aztec/stdlib/interfaces/client';
12
+
13
+ import type { BotConfig } from './config.js';
14
+
15
+ export abstract class BaseBot {
16
+ protected log = createLogger('bot');
17
+
18
+ protected attempts: number = 0;
19
+ protected successes: number = 0;
20
+
21
+ protected constructor(public readonly pxe: PXE, public readonly wallet: Wallet, public config: BotConfig) {}
22
+
23
+ public async run() {
24
+ this.attempts++;
25
+ const logCtx = { runId: Date.now() * 1000 + Math.floor(Math.random() * 1000) };
26
+ const { followChain, txMinedWaitSeconds } = this.config;
27
+
28
+ this.log.verbose(`Creating tx`, logCtx);
29
+ const tx = await this.createAndSendTx(logCtx);
30
+
31
+ const txHash = await tx.getTxHash();
32
+
33
+ if (followChain === 'NONE') {
34
+ this.log.info(`Transaction ${txHash} sent, not waiting for it to be mined`);
35
+ return;
36
+ }
37
+
38
+ this.log.verbose(
39
+ `Awaiting tx ${txHash} to be on the ${followChain} chain (timeout ${txMinedWaitSeconds}s)`,
40
+ logCtx,
41
+ );
42
+ const receipt = await tx.wait({
43
+ timeout: txMinedWaitSeconds,
44
+ });
45
+ if (followChain === 'PROVEN') {
46
+ await waitForProven(this.pxe, receipt, { provenTimeout: txMinedWaitSeconds });
47
+ }
48
+ this.successes++;
49
+ this.log.info(
50
+ `Tx #${this.attempts} ${receipt.txHash} successfully mined in block ${receipt.blockNumber} (stats: ${this.successes}/${this.attempts} success)`,
51
+ logCtx,
52
+ );
53
+ }
54
+
55
+ protected abstract createAndSendTx(logCtx: object): Promise<SentTx>;
56
+
57
+ protected getSendMethodOpts(...authWitnesses: AuthWitness[]): SendMethodOptions {
58
+ const sender = this.wallet.getAddress();
59
+ const { l2GasLimit, daGasLimit, skipPublicSimulation } = this.config;
60
+ const paymentMethod = new FeeJuicePaymentMethod(sender);
61
+
62
+ let gasSettings, estimateGas;
63
+ if (l2GasLimit !== undefined && l2GasLimit > 0 && daGasLimit !== undefined && daGasLimit > 0) {
64
+ gasSettings = { gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) };
65
+ estimateGas = false;
66
+ this.log.verbose(`Using gas limits ${l2GasLimit} L2 gas ${daGasLimit} DA gas`);
67
+ } else {
68
+ estimateGas = true;
69
+ this.log.verbose(`Estimating gas for transaction`);
70
+ }
71
+ const baseFeePadding = 2; // Send 3x the current base fee
72
+ this.log.verbose(skipPublicSimulation ? `Skipping public simulation` : `Simulating public transfers`);
73
+ return { fee: { estimateGas, paymentMethod, gasSettings, baseFeePadding }, skipPublicSimulation, authWitnesses };
74
+ }
75
+ }
package/src/bot.ts CHANGED
@@ -1,39 +1,33 @@
1
- import {
2
- type AztecAddress,
3
- BatchCall,
4
- FeeJuicePaymentMethod,
5
- type SendMethodOptions,
6
- type Wallet,
7
- createLogger,
8
- } from '@aztec/aztec.js';
1
+ import { type AztecAddress, BatchCall, SentTx, type Wallet } from '@aztec/aztec.js';
9
2
  import { times } from '@aztec/foundation/collection';
10
3
  import type { EasyPrivateTokenContract } from '@aztec/noir-contracts.js/EasyPrivateToken';
11
4
  import type { TokenContract } from '@aztec/noir-contracts.js/Token';
12
- import { Gas } from '@aztec/stdlib/gas';
13
- import type { AztecNode, PXE } from '@aztec/stdlib/interfaces/client';
5
+ import type { AztecNode, AztecNodeAdmin, PXE } from '@aztec/stdlib/interfaces/client';
14
6
 
7
+ import { BaseBot } from './base_bot.js';
15
8
  import type { BotConfig } from './config.js';
16
9
  import { BotFactory } from './factory.js';
17
10
  import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
18
11
 
19
12
  const TRANSFER_AMOUNT = 1;
20
13
 
21
- export class Bot {
22
- private log = createLogger('bot');
23
-
24
- private attempts: number = 0;
25
- private successes: number = 0;
26
-
14
+ export class Bot extends BaseBot {
27
15
  protected constructor(
28
- public readonly wallet: Wallet,
16
+ pxe: PXE,
17
+ wallet: Wallet,
29
18
  public readonly token: TokenContract | EasyPrivateTokenContract,
30
19
  public readonly recipient: AztecAddress,
31
- public config: BotConfig,
32
- ) {}
20
+ config: BotConfig,
21
+ ) {
22
+ super(pxe, wallet, config);
23
+ }
33
24
 
34
- static async create(config: BotConfig, dependencies: { pxe?: PXE; node?: AztecNode } = {}): Promise<Bot> {
35
- const { wallet, token, recipient } = await new BotFactory(config, dependencies).setup();
36
- return new Bot(wallet, token, recipient, config);
25
+ static async create(
26
+ config: BotConfig,
27
+ dependencies: { pxe?: PXE; node?: AztecNode; nodeAdmin?: AztecNodeAdmin },
28
+ ): Promise<Bot> {
29
+ const { pxe, wallet, token, recipient } = await new BotFactory(config, dependencies).setup();
30
+ return new Bot(pxe, wallet, token, recipient, config);
37
31
  }
38
32
 
39
33
  public updateConfig(config: Partial<BotConfig>) {
@@ -41,11 +35,8 @@ export class Bot {
41
35
  this.config = { ...this.config, ...config };
42
36
  }
43
37
 
44
- public async run() {
45
- this.attempts++;
46
- const logCtx = { runId: Date.now() * 1000 + Math.floor(Math.random() * 1000) };
47
- const { privateTransfersPerTx, publicTransfersPerTx, feePaymentMethod, followChain, txMinedWaitSeconds } =
48
- this.config;
38
+ protected async createAndSendTx(logCtx: object): Promise<SentTx> {
39
+ const { privateTransfersPerTx, publicTransfersPerTx, feePaymentMethod } = this.config;
49
40
  const { token, recipient, wallet } = this;
50
41
  const sender = wallet.getAddress();
51
42
 
@@ -69,31 +60,7 @@ export class Bot {
69
60
 
70
61
  this.log.verbose(`Proving transaction`, logCtx);
71
62
  const provenTx = await batch.prove(opts);
72
-
73
- this.log.verbose(`Sending tx`, logCtx);
74
- const tx = provenTx.send();
75
-
76
- const txHash = await tx.getTxHash();
77
-
78
- if (followChain === 'NONE') {
79
- this.log.info(`Transaction ${txHash} sent, not waiting for it to be mined`);
80
- return;
81
- }
82
-
83
- this.log.verbose(
84
- `Awaiting tx ${txHash} to be on the ${followChain} chain (timeout ${txMinedWaitSeconds}s)`,
85
- logCtx,
86
- );
87
- const receipt = await tx.wait({
88
- timeout: txMinedWaitSeconds,
89
- provenTimeout: txMinedWaitSeconds,
90
- proven: followChain === 'PROVEN',
91
- });
92
- this.log.info(
93
- `Tx #${this.attempts} ${receipt.txHash} successfully mined in block ${receipt.blockNumber} (stats: ${this.successes}/${this.attempts} success)`,
94
- logCtx,
95
- );
96
- this.successes++;
63
+ return provenTx.send();
97
64
  }
98
65
 
99
66
  public async getBalances() {
@@ -115,23 +82,4 @@ export class Bot {
115
82
  };
116
83
  }
117
84
  }
118
-
119
- private getSendMethodOpts(): SendMethodOptions {
120
- const sender = this.wallet.getAddress();
121
- const { l2GasLimit, daGasLimit, skipPublicSimulation } = this.config;
122
- const paymentMethod = new FeeJuicePaymentMethod(sender);
123
-
124
- let gasSettings, estimateGas;
125
- if (l2GasLimit !== undefined && l2GasLimit > 0 && daGasLimit !== undefined && daGasLimit > 0) {
126
- gasSettings = { gasLimits: Gas.from({ l2Gas: l2GasLimit, daGas: daGasLimit }) };
127
- estimateGas = false;
128
- this.log.verbose(`Using gas limits ${l2GasLimit} L2 gas ${daGasLimit} DA gas`);
129
- } else {
130
- estimateGas = true;
131
- this.log.verbose(`Estimating gas for transaction`);
132
- }
133
- const baseFeePadding = 2; // Send 3x the current base fee
134
- this.log.verbose(skipPublicSimulation ? `Skipping public simulation` : `Simulating public transfers`);
135
- return { fee: { estimateGas, paymentMethod, gasSettings, baseFeePadding }, skipPublicSimulation };
136
- }
137
85
  }