@aztec/bot 4.0.0-nightly.20250907 → 4.0.0-nightly.20260107
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 +9 -9
- package/dest/amm_bot.d.ts.map +1 -1
- package/dest/amm_bot.js +23 -20
- package/dest/base_bot.d.ts +11 -8
- package/dest/base_bot.d.ts.map +1 -1
- package/dest/base_bot.js +18 -18
- package/dest/bot.d.ts +8 -9
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +11 -12
- package/dest/config.d.ts +53 -50
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +27 -25
- package/dest/factory.d.ts +14 -32
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +137 -115
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/interface.d.ts +2 -2
- package/dest/interface.d.ts.map +1 -1
- package/dest/interface.js +1 -1
- package/dest/rpc.d.ts +1 -1
- package/dest/runner.d.ts +12 -13
- package/dest/runner.d.ts.map +1 -1
- package/dest/runner.js +429 -61
- package/dest/store/bot_store.d.ts +44 -0
- package/dest/store/bot_store.d.ts.map +1 -0
- package/dest/store/bot_store.js +107 -0
- package/dest/store/index.d.ts +2 -0
- package/dest/store/index.d.ts.map +1 -0
- package/dest/store/index.js +1 -0
- package/dest/utils.d.ts +4 -4
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +5 -5
- package/package.json +16 -13
- package/src/amm_bot.ts +39 -31
- package/src/base_bot.ts +24 -22
- package/src/bot.ts +25 -14
- package/src/config.ts +64 -64
- package/src/factory.ts +172 -157
- package/src/index.ts +1 -0
- package/src/interface.ts +1 -1
- package/src/runner.ts +19 -23
- package/src/store/bot_store.ts +141 -0
- package/src/store/index.ts +1 -0
- package/src/utils.ts +10 -5
package/src/factory.ts
CHANGED
|
@@ -1,122 +1,97 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
|
|
2
|
+
import { getInitialTestAccountsData } from '@aztec/accounts/testing';
|
|
3
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
3
4
|
import {
|
|
4
|
-
type AccountWallet,
|
|
5
|
-
AztecAddress,
|
|
6
5
|
BatchCall,
|
|
7
6
|
ContractBase,
|
|
8
7
|
ContractFunctionInteraction,
|
|
9
8
|
type DeployMethod,
|
|
10
9
|
type DeployOptions,
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
} from '@aztec/aztec.js';
|
|
18
|
-
import { createEthereumChain
|
|
19
|
-
import {
|
|
10
|
+
} from '@aztec/aztec.js/contracts';
|
|
11
|
+
import { L1FeeJuicePortalManager } from '@aztec/aztec.js/ethereum';
|
|
12
|
+
import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
|
|
13
|
+
import { FeeJuicePaymentMethodWithClaim } from '@aztec/aztec.js/fee';
|
|
14
|
+
import { deriveKeys } from '@aztec/aztec.js/keys';
|
|
15
|
+
import { createLogger } from '@aztec/aztec.js/log';
|
|
16
|
+
import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
17
|
+
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
18
|
+
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
19
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
20
20
|
import { Timer } from '@aztec/foundation/timer';
|
|
21
21
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
22
22
|
import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
23
23
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
24
|
+
import type { ContractInstanceWithAddress } from '@aztec/stdlib/contract';
|
|
25
|
+
import { GasSettings } from '@aztec/stdlib/gas';
|
|
24
26
|
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
25
27
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
26
|
-
import {
|
|
28
|
+
import { TestWallet } from '@aztec/test-wallet/server';
|
|
27
29
|
|
|
28
|
-
import { type BotConfig, SupportedTokenContracts
|
|
30
|
+
import { type BotConfig, SupportedTokenContracts } from './config.js';
|
|
31
|
+
import type { BotStore } from './store/index.js';
|
|
29
32
|
import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
|
|
30
33
|
|
|
31
34
|
const MINT_BALANCE = 1e12;
|
|
32
35
|
const MIN_BALANCE = 1e3;
|
|
33
36
|
|
|
34
37
|
export class BotFactory {
|
|
35
|
-
private pxe: PXE;
|
|
36
|
-
private node?: AztecNode;
|
|
37
|
-
private nodeAdmin?: AztecNodeAdmin;
|
|
38
38
|
private log = createLogger('bot');
|
|
39
39
|
|
|
40
40
|
constructor(
|
|
41
41
|
private readonly config: BotConfig,
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
);
|
|
48
|
-
}
|
|
49
|
-
if (config.senderPrivateKey && config.senderPrivateKey.getValue() && !dependencies.node) {
|
|
50
|
-
throw new Error(
|
|
51
|
-
`Either a node client or node url must be provided for bridging L1 fee juice to deploy an account with private key`,
|
|
52
|
-
);
|
|
53
|
-
}
|
|
54
|
-
if (!dependencies.pxe && !config.pxeUrl) {
|
|
55
|
-
throw new Error(`Either a PXE client or a PXE URL must be provided`);
|
|
56
|
-
}
|
|
57
|
-
|
|
58
|
-
this.node = dependencies.node;
|
|
59
|
-
this.nodeAdmin = dependencies.nodeAdmin;
|
|
60
|
-
|
|
61
|
-
if (dependencies.pxe) {
|
|
62
|
-
this.log.info(`Using local PXE`);
|
|
63
|
-
this.pxe = dependencies.pxe;
|
|
64
|
-
return;
|
|
65
|
-
}
|
|
66
|
-
this.log.info(`Using remote PXE at ${config.pxeUrl!}`);
|
|
67
|
-
this.pxe = createPXEClient(config.pxeUrl!, getVersions(), makeTracedFetch([1, 2, 3], false));
|
|
68
|
-
}
|
|
42
|
+
private readonly wallet: TestWallet,
|
|
43
|
+
private readonly store: BotStore,
|
|
44
|
+
private readonly aztecNode: AztecNode,
|
|
45
|
+
private readonly aztecNodeAdmin?: AztecNodeAdmin,
|
|
46
|
+
) {}
|
|
69
47
|
|
|
70
48
|
/**
|
|
71
49
|
* Initializes a new bot by setting up the sender account, registering the recipient,
|
|
72
50
|
* deploying the token contract, and minting tokens if necessary.
|
|
73
51
|
*/
|
|
74
|
-
public async setup() {
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
52
|
+
public async setup(): Promise<{
|
|
53
|
+
wallet: TestWallet;
|
|
54
|
+
defaultAccountAddress: AztecAddress;
|
|
55
|
+
token: TokenContract | PrivateTokenContract;
|
|
56
|
+
node: AztecNode;
|
|
57
|
+
recipient: AztecAddress;
|
|
58
|
+
}> {
|
|
59
|
+
const recipient = (await this.wallet.createAccount()).address;
|
|
60
|
+
const defaultAccountAddress = await this.setupAccount();
|
|
61
|
+
const token = await this.setupToken(defaultAccountAddress);
|
|
79
62
|
await this.mintTokens(token, defaultAccountAddress);
|
|
80
|
-
return { wallet, defaultAccountAddress, token,
|
|
63
|
+
return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
|
|
81
64
|
}
|
|
82
65
|
|
|
83
|
-
public async setupAmm() {
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
);
|
|
93
|
-
const token1 = await this.setupTokenContract(
|
|
94
|
-
wallet,
|
|
95
|
-
wallet.getAddress(),
|
|
96
|
-
this.config.tokenSalt,
|
|
97
|
-
'BotToken1',
|
|
98
|
-
'BOT1',
|
|
99
|
-
);
|
|
66
|
+
public async setupAmm(): Promise<{
|
|
67
|
+
wallet: TestWallet;
|
|
68
|
+
defaultAccountAddress: AztecAddress;
|
|
69
|
+
amm: AMMContract;
|
|
70
|
+
token0: TokenContract;
|
|
71
|
+
token1: TokenContract;
|
|
72
|
+
node: AztecNode;
|
|
73
|
+
}> {
|
|
74
|
+
const defaultAccountAddress = await this.setupAccount();
|
|
75
|
+
const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
|
|
76
|
+
const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
|
|
100
77
|
const liquidityToken = await this.setupTokenContract(
|
|
101
|
-
|
|
102
|
-
wallet.getAddress(),
|
|
78
|
+
defaultAccountAddress,
|
|
103
79
|
this.config.tokenSalt,
|
|
104
80
|
'BotLPToken',
|
|
105
81
|
'BOTLP',
|
|
106
82
|
);
|
|
107
83
|
const amm = await this.setupAmmContract(
|
|
108
|
-
|
|
109
|
-
wallet.getAddress(),
|
|
84
|
+
defaultAccountAddress,
|
|
110
85
|
this.config.tokenSalt,
|
|
111
86
|
token0,
|
|
112
87
|
token1,
|
|
113
88
|
liquidityToken,
|
|
114
89
|
);
|
|
115
90
|
|
|
116
|
-
await this.fundAmm(
|
|
91
|
+
await this.fundAmm(defaultAccountAddress, defaultAccountAddress, amm, token0, token1, liquidityToken);
|
|
117
92
|
this.log.info(`AMM initialized and funded`);
|
|
118
93
|
|
|
119
|
-
return { wallet, defaultAccountAddress, amm, token0, token1,
|
|
94
|
+
return { wallet: this.wallet, defaultAccountAddress, amm, token0, token1, node: this.aztecNode };
|
|
120
95
|
}
|
|
121
96
|
|
|
122
97
|
/**
|
|
@@ -126,62 +101,63 @@ export class BotFactory {
|
|
|
126
101
|
private async setupAccount() {
|
|
127
102
|
const privateKey = this.config.senderPrivateKey?.getValue();
|
|
128
103
|
if (privateKey) {
|
|
104
|
+
this.log.info(`Setting up account with provided private key`);
|
|
129
105
|
return await this.setupAccountWithPrivateKey(privateKey);
|
|
130
106
|
} else {
|
|
107
|
+
this.log.info(`Setting up test account`);
|
|
131
108
|
return await this.setupTestAccount();
|
|
132
109
|
}
|
|
133
110
|
}
|
|
134
111
|
|
|
135
|
-
private async setupAccountWithPrivateKey(
|
|
112
|
+
private async setupAccountWithPrivateKey(secret: Fr) {
|
|
136
113
|
const salt = this.config.senderSalt ?? Fr.ONE;
|
|
137
|
-
const signingKey = deriveSigningKey(
|
|
138
|
-
const
|
|
139
|
-
|
|
114
|
+
const signingKey = deriveSigningKey(secret);
|
|
115
|
+
const accountData = {
|
|
116
|
+
secret,
|
|
117
|
+
salt,
|
|
118
|
+
contract: new SchnorrAccountContract(signingKey!),
|
|
119
|
+
};
|
|
120
|
+
const accountManager = await this.wallet.createAccount(accountData);
|
|
121
|
+
const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
|
|
140
122
|
if (isInit) {
|
|
141
|
-
this.log.info(`Account at ${
|
|
123
|
+
this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
|
|
142
124
|
const timer = new Timer();
|
|
143
|
-
const
|
|
144
|
-
this.log.info(`Account at ${
|
|
145
|
-
|
|
125
|
+
const address = accountManager.address;
|
|
126
|
+
this.log.info(`Account at ${address} registered. duration=${timer.ms()}`);
|
|
127
|
+
await this.store.deleteBridgeClaim(address);
|
|
128
|
+
return address;
|
|
146
129
|
} else {
|
|
147
|
-
const address =
|
|
130
|
+
const address = accountManager.address;
|
|
148
131
|
this.log.info(`Deploying account at ${address}`);
|
|
149
132
|
|
|
150
|
-
const claim = await this.
|
|
133
|
+
const claim = await this.getOrCreateBridgeClaim(address);
|
|
151
134
|
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
135
|
+
const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
|
|
136
|
+
const deployMethod = await accountManager.getDeployMethod();
|
|
137
|
+
const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
|
|
138
|
+
const gasSettings = GasSettings.default({ maxFeesPerGas });
|
|
139
|
+
const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
|
|
156
140
|
const txHash = await sentTx.getTxHash();
|
|
157
|
-
// docs:end:claim_and_deploy
|
|
158
141
|
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
|
|
159
142
|
await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
|
|
160
143
|
this.log.info(`Account deployed at ${address}`);
|
|
161
|
-
return wallet;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
144
|
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
} else {
|
|
170
|
-
this.log.info('Registering funded test account');
|
|
171
|
-
const [account] = await getInitialTestAccounts();
|
|
172
|
-
const manager = await getSchnorrAccount(this.pxe, account.secret, account.signingKey, account.salt);
|
|
173
|
-
wallet = await manager.register();
|
|
174
|
-
this.log.info(`Funded test account registered: ${wallet.getAddress()}`);
|
|
145
|
+
// Clean up the consumed bridge claim
|
|
146
|
+
await this.store.deleteBridgeClaim(address);
|
|
147
|
+
|
|
148
|
+
return accountManager.address;
|
|
175
149
|
}
|
|
176
|
-
return wallet;
|
|
177
150
|
}
|
|
178
151
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
152
|
+
private async setupTestAccount() {
|
|
153
|
+
const [initialAccountData] = await getInitialTestAccountsData();
|
|
154
|
+
const accountData = {
|
|
155
|
+
secret: initialAccountData.secret,
|
|
156
|
+
salt: initialAccountData.salt,
|
|
157
|
+
contract: new SchnorrAccountContract(initialAccountData.signingKey),
|
|
158
|
+
};
|
|
159
|
+
const accountManager = await this.wallet.createAccount(accountData);
|
|
160
|
+
return accountManager.address;
|
|
185
161
|
}
|
|
186
162
|
|
|
187
163
|
/**
|
|
@@ -189,26 +165,34 @@ export class BotFactory {
|
|
|
189
165
|
* @param wallet - Wallet to deploy the token contract from.
|
|
190
166
|
* @returns The TokenContract instance.
|
|
191
167
|
*/
|
|
192
|
-
private async setupToken(
|
|
168
|
+
private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
193
169
|
let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
|
|
170
|
+
let tokenInstance: ContractInstanceWithAddress | undefined;
|
|
194
171
|
const deployOpts: DeployOptions = {
|
|
195
172
|
from: sender,
|
|
196
173
|
contractAddressSalt: this.config.tokenSalt,
|
|
197
174
|
universalDeploy: true,
|
|
198
175
|
};
|
|
199
176
|
if (this.config.contract === SupportedTokenContracts.TokenContract) {
|
|
200
|
-
deploy = TokenContract.deploy(wallet, sender, 'BotToken', 'BOT', 18);
|
|
177
|
+
deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
|
|
201
178
|
} else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
|
|
202
|
-
|
|
179
|
+
// Generate keys for the contract since PrivateToken uses SinglePrivateMutable which requires keys
|
|
180
|
+
const tokenSecretKey = Fr.random();
|
|
181
|
+
const tokenPublicKeys = (await deriveKeys(tokenSecretKey)).publicKeys;
|
|
182
|
+
deploy = PrivateTokenContract.deployWithPublicKeys(tokenPublicKeys, this.wallet, MINT_BALANCE, sender);
|
|
203
183
|
deployOpts.skipInstancePublication = true;
|
|
204
184
|
deployOpts.skipClassPublication = true;
|
|
205
185
|
deployOpts.skipInitialization = false;
|
|
186
|
+
|
|
187
|
+
// Register the contract with the secret key before deployment
|
|
188
|
+
tokenInstance = await deploy.getInstance(deployOpts);
|
|
189
|
+
await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
|
|
206
190
|
} else {
|
|
207
191
|
throw new Error(`Unsupported token contract type: ${this.config.contract}`);
|
|
208
192
|
}
|
|
209
193
|
|
|
210
|
-
const address = (await deploy.getInstance(deployOpts)).address;
|
|
211
|
-
if ((await this.
|
|
194
|
+
const address = tokenInstance?.address ?? (await deploy.getInstance(deployOpts)).address;
|
|
195
|
+
if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
|
|
212
196
|
this.log.info(`Token at ${address.toString()} already deployed`);
|
|
213
197
|
return deploy.register();
|
|
214
198
|
} else {
|
|
@@ -226,7 +210,6 @@ export class BotFactory {
|
|
|
226
210
|
* @returns The TokenContract instance.
|
|
227
211
|
*/
|
|
228
212
|
private setupTokenContract(
|
|
229
|
-
wallet: AccountWallet,
|
|
230
213
|
deployer: AztecAddress,
|
|
231
214
|
contractAddressSalt: Fr,
|
|
232
215
|
name: string,
|
|
@@ -234,12 +217,11 @@ export class BotFactory {
|
|
|
234
217
|
decimals = 18,
|
|
235
218
|
): Promise<TokenContract> {
|
|
236
219
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
237
|
-
const deploy = TokenContract.deploy(wallet, deployer, name, ticker, decimals);
|
|
220
|
+
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
|
|
238
221
|
return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
|
|
239
222
|
}
|
|
240
223
|
|
|
241
224
|
private async setupAmmContract(
|
|
242
|
-
wallet: AccountWallet,
|
|
243
225
|
deployer: AztecAddress,
|
|
244
226
|
contractAddressSalt: Fr,
|
|
245
227
|
token0: TokenContract,
|
|
@@ -247,7 +229,7 @@ export class BotFactory {
|
|
|
247
229
|
lpToken: TokenContract,
|
|
248
230
|
): Promise<AMMContract> {
|
|
249
231
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
250
|
-
const deploy = AMMContract.deploy(wallet, token0.address, token1.address, lpToken.address);
|
|
232
|
+
const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
|
|
251
233
|
const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
|
|
252
234
|
|
|
253
235
|
this.log.info(`AMM deployed at ${amm.address}`);
|
|
@@ -260,7 +242,7 @@ export class BotFactory {
|
|
|
260
242
|
}
|
|
261
243
|
|
|
262
244
|
private async fundAmm(
|
|
263
|
-
|
|
245
|
+
defaultAccountAddress: AztecAddress,
|
|
264
246
|
liquidityProvider: AztecAddress,
|
|
265
247
|
amm: AMMContract,
|
|
266
248
|
token0: TokenContract,
|
|
@@ -289,26 +271,30 @@ export class BotFactory {
|
|
|
289
271
|
);
|
|
290
272
|
|
|
291
273
|
// Add authwitnesses for the transfers in AMM::add_liquidity function
|
|
292
|
-
const token0Authwit = await wallet.createAuthWit({
|
|
274
|
+
const token0Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
|
|
293
275
|
caller: amm.address,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
276
|
+
call: await token0.methods
|
|
277
|
+
.transfer_to_public_and_prepare_private_balance_increase(
|
|
278
|
+
liquidityProvider,
|
|
279
|
+
amm.address,
|
|
280
|
+
amount0Max,
|
|
281
|
+
authwitNonce,
|
|
282
|
+
)
|
|
283
|
+
.getFunctionCall(),
|
|
300
284
|
});
|
|
301
|
-
const token1Authwit = await wallet.createAuthWit({
|
|
285
|
+
const token1Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
|
|
302
286
|
caller: amm.address,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
287
|
+
call: await token1.methods
|
|
288
|
+
.transfer_to_public_and_prepare_private_balance_increase(
|
|
289
|
+
liquidityProvider,
|
|
290
|
+
amm.address,
|
|
291
|
+
amount1Max,
|
|
292
|
+
authwitNonce,
|
|
293
|
+
)
|
|
294
|
+
.getFunctionCall(),
|
|
309
295
|
});
|
|
310
296
|
|
|
311
|
-
const mintTx = new BatchCall(wallet, [
|
|
297
|
+
const mintTx = new BatchCall(this.wallet, [
|
|
312
298
|
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
313
299
|
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
314
300
|
]).send({ from: liquidityProvider });
|
|
@@ -329,7 +315,7 @@ export class BotFactory {
|
|
|
329
315
|
|
|
330
316
|
const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
|
|
331
317
|
this.log.info(
|
|
332
|
-
`Updated private balances of ${
|
|
318
|
+
`Updated private balances of ${defaultAccountAddress} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`,
|
|
333
319
|
);
|
|
334
320
|
}
|
|
335
321
|
|
|
@@ -339,7 +325,7 @@ export class BotFactory {
|
|
|
339
325
|
deployOpts: DeployOptions,
|
|
340
326
|
): Promise<T> {
|
|
341
327
|
const address = (await deploy.getInstance(deployOpts)).address;
|
|
342
|
-
if ((await this.
|
|
328
|
+
if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
|
|
343
329
|
this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
|
|
344
330
|
return deploy.register();
|
|
345
331
|
} else {
|
|
@@ -390,7 +376,40 @@ export class BotFactory {
|
|
|
390
376
|
await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
|
|
391
377
|
}
|
|
392
378
|
|
|
393
|
-
|
|
379
|
+
/**
|
|
380
|
+
* Gets or creates a bridge claim for the recipient.
|
|
381
|
+
* Checks if a claim already exists in the store and reuses it if valid.
|
|
382
|
+
* Only creates a new bridge if fee juice balance is below threshold.
|
|
383
|
+
*/
|
|
384
|
+
private async getOrCreateBridgeClaim(recipient: AztecAddress): Promise<L2AmountClaim> {
|
|
385
|
+
// Check if we have an existing claim in the store
|
|
386
|
+
const existingClaim = await this.store.getBridgeClaim(recipient);
|
|
387
|
+
if (existingClaim) {
|
|
388
|
+
this.log.info(`Found existing bridge claim for ${recipient.toString()}, checking validity...`);
|
|
389
|
+
|
|
390
|
+
// Check if the message is ready on L2
|
|
391
|
+
try {
|
|
392
|
+
const messageHash = Fr.fromHexString(existingClaim.claim.messageHash);
|
|
393
|
+
await this.withNoMinTxsPerBlock(() =>
|
|
394
|
+
waitForL1ToL2MessageReady(this.aztecNode, messageHash, {
|
|
395
|
+
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
|
|
396
|
+
forPublicConsumption: false,
|
|
397
|
+
}),
|
|
398
|
+
);
|
|
399
|
+
return existingClaim.claim;
|
|
400
|
+
} catch (err) {
|
|
401
|
+
this.log.warn(`Failed to verify existing claim, creating new one: ${err}`);
|
|
402
|
+
await this.store.deleteBridgeClaim(recipient);
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
const claim = await this.bridgeL1FeeJuice(recipient);
|
|
407
|
+
await this.store.saveBridgeClaim(recipient, claim);
|
|
408
|
+
|
|
409
|
+
return claim;
|
|
410
|
+
}
|
|
411
|
+
|
|
412
|
+
private async bridgeL1FeeJuice(recipient: AztecAddress): Promise<L2AmountClaim> {
|
|
394
413
|
const l1RpcUrls = this.config.l1RpcUrls;
|
|
395
414
|
if (!l1RpcUrls?.length) {
|
|
396
415
|
throw new Error('L1 Rpc url is required to bridge the fee juice to fund the deployment of the account.');
|
|
@@ -402,43 +421,39 @@ export class BotFactory {
|
|
|
402
421
|
);
|
|
403
422
|
}
|
|
404
423
|
|
|
405
|
-
const { l1ChainId } = await this.
|
|
424
|
+
const { l1ChainId } = await this.aztecNode.getNodeInfo();
|
|
406
425
|
const chain = createEthereumChain(l1RpcUrls, l1ChainId);
|
|
407
426
|
const extendedClient = createExtendedL1Client(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
|
|
408
427
|
|
|
409
|
-
const portal = await L1FeeJuicePortalManager.new(this.
|
|
428
|
+
const portal = await L1FeeJuicePortalManager.new(this.aztecNode, extendedClient, this.log);
|
|
410
429
|
const mintAmount = await portal.getTokenManager().getMintAmount();
|
|
411
430
|
const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true /* mint */);
|
|
412
431
|
|
|
413
|
-
|
|
414
|
-
|
|
432
|
+
await this.withNoMinTxsPerBlock(() =>
|
|
433
|
+
waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(claim.messageHash), {
|
|
434
|
+
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
|
|
435
|
+
forPublicConsumption: false,
|
|
436
|
+
}),
|
|
437
|
+
);
|
|
415
438
|
|
|
416
439
|
this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
|
|
417
440
|
|
|
418
|
-
|
|
419
|
-
await this.advanceL2Block();
|
|
420
|
-
await this.advanceL2Block();
|
|
421
|
-
|
|
422
|
-
return claim;
|
|
441
|
+
return claim as L2AmountClaim;
|
|
423
442
|
}
|
|
424
443
|
|
|
425
444
|
private async withNoMinTxsPerBlock<T>(fn: () => Promise<T>): Promise<T> {
|
|
426
|
-
if (!this.
|
|
445
|
+
if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
|
|
446
|
+
this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
|
|
427
447
|
return fn();
|
|
428
448
|
}
|
|
429
|
-
const { minTxsPerBlock } = await this.
|
|
430
|
-
|
|
449
|
+
const { minTxsPerBlock } = await this.aztecNodeAdmin.getConfig();
|
|
450
|
+
this.log.warn(`Setting sequencer minTxsPerBlock to 0 from ${minTxsPerBlock} to flush setup transactions`);
|
|
451
|
+
await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 });
|
|
431
452
|
try {
|
|
432
453
|
return await fn();
|
|
433
454
|
} finally {
|
|
434
|
-
|
|
455
|
+
this.log.warn(`Restoring sequencer minTxsPerBlock to ${minTxsPerBlock}`);
|
|
456
|
+
await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
|
|
435
457
|
}
|
|
436
458
|
}
|
|
437
|
-
|
|
438
|
-
private async advanceL2Block() {
|
|
439
|
-
await this.withNoMinTxsPerBlock(async () => {
|
|
440
|
-
const initialBlockNumber = await this.node!.getBlockNumber();
|
|
441
|
-
await retryUntil(async () => (await this.node!.getBlockNumber()) >= initialBlockNumber + 1);
|
|
442
|
-
});
|
|
443
|
-
}
|
|
444
459
|
}
|
package/src/index.ts
CHANGED
package/src/interface.ts
CHANGED
package/src/runner.ts
CHANGED
|
@@ -1,21 +1,21 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
|
+
import type { AztecNode } from '@aztec/aztec.js/node';
|
|
2
3
|
import { omit } from '@aztec/foundation/collection';
|
|
3
4
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
4
|
-
import {
|
|
5
|
-
import { type TelemetryClient, type Traceable, type Tracer,
|
|
5
|
+
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
6
|
+
import { type TelemetryClient, type Traceable, type Tracer, trackSpan } from '@aztec/telemetry-client';
|
|
7
|
+
import type { TestWallet } from '@aztec/test-wallet/server';
|
|
6
8
|
|
|
7
9
|
import { AmmBot } from './amm_bot.js';
|
|
8
10
|
import type { BaseBot } from './base_bot.js';
|
|
9
11
|
import { Bot } from './bot.js';
|
|
10
|
-
import {
|
|
12
|
+
import type { BotConfig } from './config.js';
|
|
11
13
|
import type { BotInfo, BotRunnerApi } from './interface.js';
|
|
14
|
+
import { BotStore } from './store/index.js';
|
|
12
15
|
|
|
13
16
|
export class BotRunner implements BotRunnerApi, Traceable {
|
|
14
17
|
private log = createLogger('bot');
|
|
15
18
|
private bot?: Promise<BaseBot>;
|
|
16
|
-
private pxe?: PXE;
|
|
17
|
-
private node: AztecNode;
|
|
18
|
-
private nodeAdmin?: AztecNodeAdmin;
|
|
19
19
|
private runningPromise: RunningPromise;
|
|
20
20
|
private consecutiveErrors = 0;
|
|
21
21
|
private healthy = true;
|
|
@@ -24,19 +24,14 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
24
24
|
|
|
25
25
|
public constructor(
|
|
26
26
|
private config: BotConfig,
|
|
27
|
-
|
|
27
|
+
private readonly wallet: TestWallet,
|
|
28
|
+
private readonly aztecNode: AztecNode,
|
|
29
|
+
private readonly telemetry: TelemetryClient,
|
|
30
|
+
private readonly aztecNodeAdmin: AztecNodeAdmin | undefined,
|
|
31
|
+
private readonly store: BotStore,
|
|
28
32
|
) {
|
|
29
|
-
this.tracer =
|
|
30
|
-
|
|
31
|
-
if (!dependencies.node && !config.nodeUrl) {
|
|
32
|
-
throw new Error(`Missing node URL in config or dependencies`);
|
|
33
|
-
}
|
|
34
|
-
const versions = getVersions();
|
|
35
|
-
const fetch = makeTracedFetch([1, 2, 3], true);
|
|
36
|
-
this.node = dependencies.node ?? createAztecNodeClient(config.nodeUrl!, versions, fetch);
|
|
37
|
-
this.nodeAdmin =
|
|
38
|
-
dependencies.nodeAdmin ??
|
|
39
|
-
(config.nodeAdminUrl ? createAztecNodeAdminClient(config.nodeAdminUrl, versions, fetch) : undefined);
|
|
33
|
+
this.tracer = telemetry.getTracer('Bot');
|
|
34
|
+
|
|
40
35
|
this.runningPromise = new RunningPromise(() => this.#work(), this.log, config.txIntervalSeconds * 1000);
|
|
41
36
|
}
|
|
42
37
|
|
|
@@ -74,6 +69,7 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
74
69
|
this.log.verbose(`Stopping bot`);
|
|
75
70
|
await this.runningPromise.stop();
|
|
76
71
|
}
|
|
72
|
+
await this.store.close();
|
|
77
73
|
this.log.info(`Stopped bot`);
|
|
78
74
|
}
|
|
79
75
|
|
|
@@ -144,15 +140,15 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
144
140
|
if (!this.bot) {
|
|
145
141
|
throw new Error(`Bot is not initialized`);
|
|
146
142
|
}
|
|
147
|
-
const botAddress = await this.bot.then(b => b.
|
|
143
|
+
const botAddress = await this.bot.then(b => b.defaultAccountAddress);
|
|
148
144
|
return { botAddress };
|
|
149
145
|
}
|
|
150
146
|
|
|
151
147
|
async #createBot() {
|
|
152
148
|
try {
|
|
153
149
|
this.bot = this.config.ammTxs
|
|
154
|
-
? AmmBot.create(this.config,
|
|
155
|
-
: Bot.create(this.config,
|
|
150
|
+
? AmmBot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store)
|
|
151
|
+
: Bot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
156
152
|
await this.bot;
|
|
157
153
|
} catch (err) {
|
|
158
154
|
this.log.error(`Error setting up bot: ${err}`);
|
|
@@ -163,7 +159,7 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
163
159
|
@trackSpan('Bot.work')
|
|
164
160
|
async #work() {
|
|
165
161
|
if (this.config.maxPendingTxs > 0) {
|
|
166
|
-
const pendingTxCount = await this.
|
|
162
|
+
const pendingTxCount = await this.aztecNode.getPendingTxCount();
|
|
167
163
|
if (pendingTxCount >= this.config.maxPendingTxs) {
|
|
168
164
|
this.log.verbose(`Not sending bot tx since node has ${pendingTxCount} pending txs`);
|
|
169
165
|
return;
|