@aztec/bot 3.0.3 → 4.0.0-devnet.1-patch.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/amm_bot.d.ts +3 -4
- package/dest/amm_bot.d.ts.map +1 -1
- package/dest/amm_bot.js +5 -1
- package/dest/base_bot.d.ts +3 -3
- package/dest/base_bot.d.ts.map +1 -1
- package/dest/base_bot.js +5 -5
- package/dest/bot.d.ts +3 -3
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +5 -2
- package/dest/config.d.ts +10 -11
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +6 -6
- package/dest/factory.d.ts +1 -6
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +92 -59
- package/dest/runner.js +412 -30
- package/dest/store/bot_store.d.ts +2 -2
- package/dest/store/bot_store.d.ts.map +1 -1
- package/package.json +14 -14
- package/src/amm_bot.ts +4 -4
- package/src/base_bot.ts +6 -10
- package/src/bot.ts +4 -3
- package/src/config.ts +51 -49
- package/src/factory.ts +73 -39
package/src/config.ts
CHANGED
|
@@ -14,7 +14,7 @@ import { Fr } from '@aztec/foundation/curves/bn254';
|
|
|
14
14
|
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
15
15
|
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
16
16
|
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
17
|
-
import {
|
|
17
|
+
import { schemas, zodFor } from '@aztec/stdlib/schemas';
|
|
18
18
|
import type { ComponentsVersions } from '@aztec/stdlib/versioning';
|
|
19
19
|
|
|
20
20
|
import { z } from 'zod';
|
|
@@ -54,8 +54,8 @@ export type BotConfig = {
|
|
|
54
54
|
publicTransfersPerTx: number;
|
|
55
55
|
/** How to handle fee payments. */
|
|
56
56
|
feePaymentMethod: 'fee_juice';
|
|
57
|
-
/** 'How much is the bot willing to overpay vs. the current
|
|
58
|
-
|
|
57
|
+
/** 'How much is the bot willing to overpay vs. the current min fee' */
|
|
58
|
+
minFeePadding: number;
|
|
59
59
|
/** True to not automatically setup or start the bot on initialization. */
|
|
60
60
|
noStart: boolean;
|
|
61
61
|
/** How long to wait for a tx to be mined before reporting an error. */
|
|
@@ -80,50 +80,52 @@ export type BotConfig = {
|
|
|
80
80
|
ammTxs: boolean;
|
|
81
81
|
} & Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb'>;
|
|
82
82
|
|
|
83
|
-
export const BotConfigSchema =
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
83
|
+
export const BotConfigSchema = zodFor<BotConfig>()(
|
|
84
|
+
z
|
|
85
|
+
.object({
|
|
86
|
+
nodeUrl: z.string().optional(),
|
|
87
|
+
nodeAdminUrl: z.string().optional(),
|
|
88
|
+
l1RpcUrls: z.array(z.string()).optional(),
|
|
89
|
+
l1Mnemonic: schemas.SecretValue(z.string()).optional(),
|
|
90
|
+
l1PrivateKey: schemas.SecretValue(z.string()).optional(),
|
|
91
|
+
l1ToL2MessageTimeoutSeconds: z.number(),
|
|
92
|
+
senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
|
|
93
|
+
senderSalt: schemas.Fr.optional(),
|
|
94
|
+
tokenSalt: schemas.Fr,
|
|
95
|
+
txIntervalSeconds: z.number(),
|
|
96
|
+
privateTransfersPerTx: z.number().int().nonnegative(),
|
|
97
|
+
publicTransfersPerTx: z.number().int().nonnegative(),
|
|
98
|
+
feePaymentMethod: z.literal('fee_juice'),
|
|
99
|
+
minFeePadding: z.number().int().nonnegative(),
|
|
100
|
+
noStart: z.boolean(),
|
|
101
|
+
txMinedWaitSeconds: z.number(),
|
|
102
|
+
followChain: z.enum(BotFollowChain),
|
|
103
|
+
maxPendingTxs: z.number().int().nonnegative(),
|
|
104
|
+
flushSetupTransactions: z.boolean(),
|
|
105
|
+
l2GasLimit: z.number().int().nonnegative().optional(),
|
|
106
|
+
daGasLimit: z.number().int().nonnegative().optional(),
|
|
107
|
+
contract: z.nativeEnum(SupportedTokenContracts),
|
|
108
|
+
maxConsecutiveErrors: z.number().int().nonnegative(),
|
|
109
|
+
stopWhenUnhealthy: z.boolean(),
|
|
110
|
+
ammTxs: z.boolean().default(false),
|
|
111
|
+
dataDirectory: z.string().optional(),
|
|
112
|
+
dataStoreMapSizeKb: z.number().optional(),
|
|
113
|
+
})
|
|
114
|
+
.transform(config => ({
|
|
115
|
+
nodeUrl: undefined,
|
|
116
|
+
nodeAdminUrl: undefined,
|
|
117
|
+
l1RpcUrls: undefined,
|
|
118
|
+
senderSalt: undefined,
|
|
119
|
+
l2GasLimit: undefined,
|
|
120
|
+
daGasLimit: undefined,
|
|
121
|
+
l1Mnemonic: undefined,
|
|
122
|
+
l1PrivateKey: undefined,
|
|
123
|
+
senderPrivateKey: undefined,
|
|
124
|
+
dataDirectory: undefined,
|
|
125
|
+
dataStoreMapSizeKb: 1_024 * 1_024,
|
|
126
|
+
...config,
|
|
127
|
+
})),
|
|
128
|
+
);
|
|
127
129
|
|
|
128
130
|
export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
129
131
|
nodeUrl: {
|
|
@@ -191,8 +193,8 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
|
191
193
|
parseEnv: val => (val as 'fee_juice') || undefined,
|
|
192
194
|
defaultValue: 'fee_juice',
|
|
193
195
|
},
|
|
194
|
-
|
|
195
|
-
env: '
|
|
196
|
+
minFeePadding: {
|
|
197
|
+
env: 'BOT_MIN_FEE_PADDING',
|
|
196
198
|
description: 'How much is the bot willing to overpay vs. the current base fee',
|
|
197
199
|
...numberConfigHelper(3),
|
|
198
200
|
},
|
package/src/factory.ts
CHANGED
|
@@ -7,12 +7,15 @@ import {
|
|
|
7
7
|
ContractFunctionInteraction,
|
|
8
8
|
type DeployMethod,
|
|
9
9
|
type DeployOptions,
|
|
10
|
+
NO_WAIT,
|
|
10
11
|
} from '@aztec/aztec.js/contracts';
|
|
11
12
|
import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
|
|
12
13
|
import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
|
|
13
14
|
import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
|
|
15
|
+
import { deriveKeys } from '@aztec/aztec.js/keys';
|
|
14
16
|
import { createLogger } from '@aztec/aztec.js/log';
|
|
15
17
|
import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
18
|
+
import { waitForTx } from '@aztec/aztec.js/node';
|
|
16
19
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
17
20
|
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
18
21
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
@@ -20,6 +23,7 @@ import { Timer } from '@aztec/foundation/timer';
|
|
|
20
23
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
21
24
|
import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
22
25
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
26
|
+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
23
27
|
import { GasSettings } from '@aztec/stdlib/gas';
|
|
24
28
|
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
25
29
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
@@ -116,8 +120,8 @@ export class BotFactory {
|
|
|
116
120
|
contract: new SchnorrAccountContract(signingKey!),
|
|
117
121
|
};
|
|
118
122
|
const accountManager = await this.wallet.createAccount(accountData);
|
|
119
|
-
const
|
|
120
|
-
if (
|
|
123
|
+
const metadata = await this.wallet.getContractMetadata(accountManager.address);
|
|
124
|
+
if (metadata.isContractInitialized) {
|
|
121
125
|
this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
|
|
122
126
|
const timer = new Timer();
|
|
123
127
|
const address = accountManager.address;
|
|
@@ -132,12 +136,18 @@ export class BotFactory {
|
|
|
132
136
|
|
|
133
137
|
const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
|
|
134
138
|
const deployMethod = await accountManager.getDeployMethod();
|
|
135
|
-
const maxFeesPerGas = (await this.aztecNode.
|
|
139
|
+
const maxFeesPerGas = (await this.aztecNode.getCurrentMinFees()).mul(1 + this.config.minFeePadding);
|
|
136
140
|
const gasSettings = GasSettings.default({ maxFeesPerGas });
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
+
|
|
142
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
143
|
+
const txHash = await deployMethod.send({
|
|
144
|
+
from: AztecAddress.ZERO,
|
|
145
|
+
fee: { gasSettings, paymentMethod },
|
|
146
|
+
wait: NO_WAIT,
|
|
147
|
+
});
|
|
148
|
+
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
|
|
149
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
150
|
+
});
|
|
141
151
|
this.log.info(`Account deployed at ${address}`);
|
|
142
152
|
|
|
143
153
|
// Clean up the consumed bridge claim
|
|
@@ -165,33 +175,49 @@ export class BotFactory {
|
|
|
165
175
|
*/
|
|
166
176
|
private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
167
177
|
let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
|
|
178
|
+
let tokenInstance: ContractInstanceWithAddress | undefined;
|
|
168
179
|
const deployOpts: DeployOptions = {
|
|
169
180
|
from: sender,
|
|
170
181
|
contractAddressSalt: this.config.tokenSalt,
|
|
171
182
|
universalDeploy: true,
|
|
172
183
|
};
|
|
184
|
+
let token: TokenContract | PrivateTokenContract;
|
|
173
185
|
if (this.config.contract === SupportedTokenContracts.TokenContract) {
|
|
174
186
|
deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
|
|
187
|
+
tokenInstance = await deploy.getInstance(deployOpts);
|
|
188
|
+
token = TokenContract.at(tokenInstance.address, this.wallet);
|
|
175
189
|
} else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
|
|
176
|
-
|
|
190
|
+
// Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
|
|
191
|
+
const tokenSecretKey = Fr.random();
|
|
192
|
+
const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
|
|
193
|
+
deploy = PrivateTokenContract.deployWithPublicKeys(tokenPublicKeys, this.wallet, MINT_BALANCE, sender);
|
|
177
194
|
deployOpts.skipInstancePublication = true;
|
|
178
195
|
deployOpts.skipClassPublication = true;
|
|
179
196
|
deployOpts.skipInitialization = false;
|
|
197
|
+
|
|
198
|
+
// Register the contract with the secret key before deployment
|
|
199
|
+
tokenInstance = await deploy.getInstance(deployOpts);
|
|
200
|
+
token = PrivateTokenContract.at(tokenInstance.address, this.wallet);
|
|
201
|
+
await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
|
|
180
202
|
} else {
|
|
181
203
|
throw new Error(`Unsupported token contract type: ${this.config.contract}`);
|
|
182
204
|
}
|
|
183
205
|
|
|
184
|
-
const address = (await deploy.getInstance(deployOpts)).address;
|
|
185
|
-
|
|
206
|
+
const address = tokenInstance?.address ?? (await deploy.getInstance(deployOpts)).address;
|
|
207
|
+
const metadata = await this.wallet.getContractMetadata(address);
|
|
208
|
+
if (metadata.isContractPublished) {
|
|
186
209
|
this.log.info(`Token at ${address.toString()} already deployed`);
|
|
187
|
-
|
|
210
|
+
await deploy.register();
|
|
188
211
|
} else {
|
|
189
212
|
this.log.info(`Deploying token contract at ${address.toString()}`);
|
|
190
|
-
const
|
|
191
|
-
const txHash = await sentTx.getTxHash();
|
|
213
|
+
const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
|
|
192
214
|
this.log.info(`Sent tx for token setup with hash ${txHash.toString()}`);
|
|
193
|
-
|
|
215
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
216
|
+
await waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
217
|
+
return token;
|
|
218
|
+
});
|
|
194
219
|
}
|
|
220
|
+
return token;
|
|
195
221
|
}
|
|
196
222
|
|
|
197
223
|
/**
|
|
@@ -199,7 +225,7 @@ export class BotFactory {
|
|
|
199
225
|
* @param wallet - Wallet to deploy the token contract from.
|
|
200
226
|
* @returns The TokenContract instance.
|
|
201
227
|
*/
|
|
202
|
-
private setupTokenContract(
|
|
228
|
+
private async setupTokenContract(
|
|
203
229
|
deployer: AztecAddress,
|
|
204
230
|
contractAddressSalt: Fr,
|
|
205
231
|
name: string,
|
|
@@ -208,7 +234,8 @@ export class BotFactory {
|
|
|
208
234
|
): Promise<TokenContract> {
|
|
209
235
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
210
236
|
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
|
|
211
|
-
|
|
237
|
+
const instance = await this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
|
|
238
|
+
return TokenContract.at(instance.address, this.wallet);
|
|
212
239
|
}
|
|
213
240
|
|
|
214
241
|
private async setupAmmContract(
|
|
@@ -220,12 +247,14 @@ export class BotFactory {
|
|
|
220
247
|
): Promise<AMMContract> {
|
|
221
248
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
222
249
|
const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
|
|
223
|
-
const
|
|
250
|
+
const instance = await this.registerOrDeployContract('AMM', deploy, deployOpts);
|
|
251
|
+
const amm = AMMContract.at(instance.address, this.wallet);
|
|
224
252
|
|
|
225
253
|
this.log.info(`AMM deployed at ${amm.address}`);
|
|
226
|
-
const
|
|
227
|
-
|
|
228
|
-
|
|
254
|
+
const minterReceipt = await lpToken.methods
|
|
255
|
+
.set_minter(amm.address, true)
|
|
256
|
+
.send({ from: deployer, wait: { timeout: this.config.txMinedWaitSeconds } });
|
|
257
|
+
this.log.info(`Set LP token minter to AMM txHash=${minterReceipt.txHash.toString()}`);
|
|
229
258
|
this.log.info(`Liquidity token initialized`);
|
|
230
259
|
|
|
231
260
|
return amm;
|
|
@@ -284,23 +313,22 @@ export class BotFactory {
|
|
|
284
313
|
.getFunctionCall(),
|
|
285
314
|
});
|
|
286
315
|
|
|
287
|
-
const
|
|
316
|
+
const mintReceipt = await new BatchCall(this.wallet, [
|
|
288
317
|
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
289
318
|
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
290
|
-
]).send({ from: liquidityProvider });
|
|
319
|
+
]).send({ from: liquidityProvider, wait: { timeout: this.config.txMinedWaitSeconds } });
|
|
291
320
|
|
|
292
|
-
this.log.info(`Sent mint tx: ${
|
|
293
|
-
await mintTx.wait({ timeout: this.config.txMinedWaitSeconds });
|
|
321
|
+
this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
|
|
294
322
|
|
|
295
|
-
const
|
|
323
|
+
const addLiquidityReceipt = await amm.methods
|
|
296
324
|
.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce)
|
|
297
325
|
.send({
|
|
298
326
|
from: liquidityProvider,
|
|
299
327
|
authWitnesses: [token0Authwit, token1Authwit],
|
|
328
|
+
wait: { timeout: this.config.txMinedWaitSeconds },
|
|
300
329
|
});
|
|
301
330
|
|
|
302
|
-
this.log.info(`Sent tx to add liquidity to the AMM: ${
|
|
303
|
-
await addLiquidityTx.wait({ timeout: this.config.txMinedWaitSeconds });
|
|
331
|
+
this.log.info(`Sent tx to add liquidity to the AMM: ${addLiquidityReceipt.txHash.toString()}`);
|
|
304
332
|
this.log.info(`Liquidity added`);
|
|
305
333
|
|
|
306
334
|
const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
|
|
@@ -313,18 +341,22 @@ export class BotFactory {
|
|
|
313
341
|
name: string,
|
|
314
342
|
deploy: DeployMethod<T>,
|
|
315
343
|
deployOpts: DeployOptions,
|
|
316
|
-
): Promise<
|
|
317
|
-
const
|
|
318
|
-
|
|
344
|
+
): Promise<ContractInstanceWithAddress> {
|
|
345
|
+
const instance = await deploy.getInstance(deployOpts);
|
|
346
|
+
const address = instance.address;
|
|
347
|
+
const metadata = await this.wallet.getContractMetadata(address);
|
|
348
|
+
if (metadata.isContractPublished) {
|
|
319
349
|
this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
|
|
320
|
-
|
|
350
|
+
await deploy.register();
|
|
321
351
|
} else {
|
|
322
352
|
this.log.info(`Deploying contract ${name} at ${address.toString()}`);
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
353
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
354
|
+
const txHash = await deploy.send({ ...deployOpts, wait: NO_WAIT });
|
|
355
|
+
this.log.info(`Sent contract ${name} setup tx with hash ${txHash.toString()}`);
|
|
356
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
357
|
+
});
|
|
327
358
|
}
|
|
359
|
+
return instance;
|
|
328
360
|
}
|
|
329
361
|
|
|
330
362
|
/**
|
|
@@ -360,10 +392,12 @@ export class BotFactory {
|
|
|
360
392
|
this.log.info(`Skipping minting as ${minter.toString()} has enough tokens`);
|
|
361
393
|
return;
|
|
362
394
|
}
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
395
|
+
|
|
396
|
+
await this.withNoMinTxsPerBlock(async () => {
|
|
397
|
+
const txHash = await new BatchCall(token.wallet, calls).send({ from: minter, wait: NO_WAIT });
|
|
398
|
+
this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
|
|
399
|
+
return waitForTx(this.aztecNode, txHash, { timeout: this.config.txMinedWaitSeconds });
|
|
400
|
+
});
|
|
367
401
|
}
|
|
368
402
|
|
|
369
403
|
/**
|