@aztec/bot 2.1.0-rc.9 → 3.0.0-devnet.2
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 +8 -8
- package/dest/amm_bot.d.ts.map +1 -1
- package/dest/amm_bot.js +23 -20
- package/dest/base_bot.d.ts +10 -7
- package/dest/base_bot.d.ts.map +1 -1
- package/dest/base_bot.js +18 -18
- package/dest/bot.d.ts +7 -8
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +11 -12
- package/dest/config.d.ts +19 -15
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +22 -20
- package/dest/factory.d.ts +18 -17
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +122 -102
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -0
- package/dest/interface.d.ts +1 -1
- package/dest/interface.d.ts.map +1 -1
- package/dest/interface.js +1 -1
- package/dest/runner.d.ts +11 -12
- package/dest/runner.d.ts.map +1 -1
- package/dest/runner.js +18 -32
- 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 +3 -3
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +5 -5
- package/package.json +13 -11
- package/src/amm_bot.ts +39 -31
- package/src/base_bot.ts +24 -22
- package/src/bot.ts +25 -14
- package/src/config.ts +20 -22
- package/src/factory.ts +138 -140
- 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/config.ts
CHANGED
|
@@ -6,12 +6,14 @@ import {
|
|
|
6
6
|
getDefaultConfig,
|
|
7
7
|
numberConfigHelper,
|
|
8
8
|
optionalNumberConfigHelper,
|
|
9
|
+
pickConfigMappings,
|
|
9
10
|
secretFrConfigHelper,
|
|
10
11
|
secretStringConfigHelper,
|
|
11
12
|
} from '@aztec/foundation/config';
|
|
12
13
|
import { Fr } from '@aztec/foundation/fields';
|
|
14
|
+
import { type DataStoreConfig, dataConfigMappings } from '@aztec/kv-store/config';
|
|
13
15
|
import { getVKTreeRoot } from '@aztec/noir-protocol-circuits-types/vk-tree';
|
|
14
|
-
import {
|
|
16
|
+
import { protocolContractsHash } from '@aztec/protocol-contracts';
|
|
15
17
|
import { type ZodFor, schemas } from '@aztec/stdlib/schemas';
|
|
16
18
|
import type { ComponentsVersions } from '@aztec/stdlib/versioning';
|
|
17
19
|
|
|
@@ -30,8 +32,6 @@ export type BotConfig = {
|
|
|
30
32
|
nodeUrl: string | undefined;
|
|
31
33
|
/** The URL to the Aztec node admin API to force-flush txs if configured. */
|
|
32
34
|
nodeAdminUrl: string | undefined;
|
|
33
|
-
/** URL to the PXE for sending txs, or undefined if an in-proc PXE is used. */
|
|
34
|
-
pxeUrl: string | undefined;
|
|
35
35
|
/** Url of the ethereum host. */
|
|
36
36
|
l1RpcUrls: string[] | undefined;
|
|
37
37
|
/** The mnemonic for the account to bridge fee juice from L1. */
|
|
@@ -44,8 +44,6 @@ export type BotConfig = {
|
|
|
44
44
|
senderPrivateKey: SecretValue<Fr> | undefined;
|
|
45
45
|
/** Optional salt to use to instantiate the sender account */
|
|
46
46
|
senderSalt: Fr | undefined;
|
|
47
|
-
/** Encryption secret for a recipient account. */
|
|
48
|
-
recipientEncryptionSecret: SecretValue<Fr>;
|
|
49
47
|
/** Salt for the token contract instantiation. */
|
|
50
48
|
tokenSalt: Fr;
|
|
51
49
|
/** Every how many seconds should a new tx be sent. */
|
|
@@ -56,6 +54,8 @@ export type BotConfig = {
|
|
|
56
54
|
publicTransfersPerTx: number;
|
|
57
55
|
/** How to handle fee payments. */
|
|
58
56
|
feePaymentMethod: 'fee_juice';
|
|
57
|
+
/** 'How much is the bot willing to overpay vs. the current base fee' */
|
|
58
|
+
baseFeePadding: 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. */
|
|
@@ -78,25 +78,24 @@ export type BotConfig = {
|
|
|
78
78
|
stopWhenUnhealthy: boolean;
|
|
79
79
|
/** Deploy an AMM contract and do swaps instead of transfers */
|
|
80
80
|
ammTxs: boolean;
|
|
81
|
-
}
|
|
81
|
+
} & Pick<DataStoreConfig, 'dataDirectory' | 'dataStoreMapSizeKb'>;
|
|
82
82
|
|
|
83
83
|
export const BotConfigSchema = z
|
|
84
84
|
.object({
|
|
85
85
|
nodeUrl: z.string().optional(),
|
|
86
86
|
nodeAdminUrl: z.string().optional(),
|
|
87
|
-
pxeUrl: z.string().optional(),
|
|
88
87
|
l1RpcUrls: z.array(z.string()).optional(),
|
|
89
88
|
l1Mnemonic: schemas.SecretValue(z.string()).optional(),
|
|
90
89
|
l1PrivateKey: schemas.SecretValue(z.string()).optional(),
|
|
91
90
|
l1ToL2MessageTimeoutSeconds: z.number(),
|
|
92
91
|
senderPrivateKey: schemas.SecretValue(schemas.Fr).optional(),
|
|
93
92
|
senderSalt: schemas.Fr.optional(),
|
|
94
|
-
recipientEncryptionSecret: schemas.SecretValue(schemas.Fr),
|
|
95
93
|
tokenSalt: schemas.Fr,
|
|
96
94
|
txIntervalSeconds: z.number(),
|
|
97
95
|
privateTransfersPerTx: z.number().int().nonnegative(),
|
|
98
96
|
publicTransfersPerTx: z.number().int().nonnegative(),
|
|
99
97
|
feePaymentMethod: z.literal('fee_juice'),
|
|
98
|
+
baseFeePadding: z.number().int().nonnegative(),
|
|
100
99
|
noStart: z.boolean(),
|
|
101
100
|
txMinedWaitSeconds: z.number(),
|
|
102
101
|
followChain: z.enum(BotFollowChain),
|
|
@@ -108,11 +107,12 @@ export const BotConfigSchema = z
|
|
|
108
107
|
maxConsecutiveErrors: z.number().int().nonnegative(),
|
|
109
108
|
stopWhenUnhealthy: z.boolean(),
|
|
110
109
|
ammTxs: z.boolean().default(false),
|
|
110
|
+
dataDirectory: z.string().optional(),
|
|
111
|
+
dataStoreMapSizeKb: z.number().optional(),
|
|
111
112
|
})
|
|
112
113
|
.transform(config => ({
|
|
113
114
|
nodeUrl: undefined,
|
|
114
115
|
nodeAdminUrl: undefined,
|
|
115
|
-
pxeUrl: undefined,
|
|
116
116
|
l1RpcUrls: undefined,
|
|
117
117
|
senderSalt: undefined,
|
|
118
118
|
l2GasLimit: undefined,
|
|
@@ -120,6 +120,8 @@ export const BotConfigSchema = z
|
|
|
120
120
|
l1Mnemonic: undefined,
|
|
121
121
|
l1PrivateKey: undefined,
|
|
122
122
|
senderPrivateKey: undefined,
|
|
123
|
+
dataDirectory: undefined,
|
|
124
|
+
dataStoreMapSizeKb: 1_024 * 1_024,
|
|
123
125
|
...config,
|
|
124
126
|
})) satisfies ZodFor<BotConfig>;
|
|
125
127
|
|
|
@@ -132,10 +134,6 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
|
132
134
|
env: 'AZTEC_NODE_ADMIN_URL',
|
|
133
135
|
description: 'The URL to the Aztec node admin API to force-flush txs if configured.',
|
|
134
136
|
},
|
|
135
|
-
pxeUrl: {
|
|
136
|
-
env: 'BOT_PXE_URL',
|
|
137
|
-
description: 'URL to the PXE for sending txs, or undefined if an in-proc PXE is used.',
|
|
138
|
-
},
|
|
139
137
|
l1RpcUrls: {
|
|
140
138
|
env: 'ETHEREUM_HOSTS',
|
|
141
139
|
description: 'URL of the ethereum host.',
|
|
@@ -163,18 +161,12 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
|
163
161
|
},
|
|
164
162
|
senderSalt: {
|
|
165
163
|
env: 'BOT_ACCOUNT_SALT',
|
|
166
|
-
description: 'The salt to use to
|
|
164
|
+
description: 'The salt to use to deploy the sender account.',
|
|
167
165
|
parseEnv: (val: string) => (val ? Fr.fromHexString(val) : undefined),
|
|
168
166
|
},
|
|
169
|
-
recipientEncryptionSecret: {
|
|
170
|
-
env: 'BOT_RECIPIENT_ENCRYPTION_SECRET',
|
|
171
|
-
description: 'Encryption secret for a recipient account.',
|
|
172
|
-
printDefault: sv => sv?.getValue(),
|
|
173
|
-
...secretFrConfigHelper(Fr.fromHexString('0xcafecafe')),
|
|
174
|
-
},
|
|
175
167
|
tokenSalt: {
|
|
176
168
|
env: 'BOT_TOKEN_SALT',
|
|
177
|
-
description: '
|
|
169
|
+
description: 'The salt to use to deploy the token contract.',
|
|
178
170
|
parseEnv: (val: string) => Fr.fromHexString(val),
|
|
179
171
|
defaultValue: Fr.fromHexString('1'),
|
|
180
172
|
},
|
|
@@ -199,6 +191,11 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
|
199
191
|
parseEnv: val => (val as 'fee_juice') || undefined,
|
|
200
192
|
defaultValue: 'fee_juice',
|
|
201
193
|
},
|
|
194
|
+
baseFeePadding: {
|
|
195
|
+
env: 'BOT_BASE_FEE_PADDING',
|
|
196
|
+
description: 'How much is the bot willing to overpay vs. the current base fee',
|
|
197
|
+
...numberConfigHelper(3),
|
|
198
|
+
},
|
|
202
199
|
noStart: {
|
|
203
200
|
env: 'BOT_NO_START',
|
|
204
201
|
description: 'True to not automatically setup or start the bot on initialization.',
|
|
@@ -270,6 +267,7 @@ export const botConfigMappings: ConfigMappingsType<BotConfig> = {
|
|
|
270
267
|
description: 'Deploy an AMM and send swaps to it',
|
|
271
268
|
...booleanConfigHelper(false),
|
|
272
269
|
},
|
|
270
|
+
...pickConfigMappings(dataConfigMappings, ['dataStoreMapSizeKb', 'dataDirectory']),
|
|
273
271
|
};
|
|
274
272
|
|
|
275
273
|
export function getBotConfigFromEnv(): BotConfig {
|
|
@@ -282,7 +280,7 @@ export function getBotDefaultConfig(): BotConfig {
|
|
|
282
280
|
|
|
283
281
|
export function getVersions(): Partial<ComponentsVersions> {
|
|
284
282
|
return {
|
|
285
|
-
|
|
283
|
+
l2ProtocolContractsHash: protocolContractsHash.toString(),
|
|
286
284
|
l2CircuitsVkTreeRoot: getVKTreeRoot().toString(),
|
|
287
285
|
};
|
|
288
286
|
}
|
package/src/factory.ts
CHANGED
|
@@ -1,122 +1,81 @@
|
|
|
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';
|
|
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 { createLogger } from '@aztec/aztec.js/log';
|
|
15
|
+
import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
18
16
|
import { createEthereumChain, createExtendedL1Client } from '@aztec/ethereum';
|
|
19
17
|
import { Fr } from '@aztec/foundation/fields';
|
|
20
18
|
import { Timer } from '@aztec/foundation/timer';
|
|
21
19
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
22
20
|
import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
23
21
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
22
|
+
import { GasSettings } from '@aztec/stdlib/gas';
|
|
24
23
|
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
25
24
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
26
|
-
import {
|
|
25
|
+
import { TestWallet } from '@aztec/test-wallet/server';
|
|
27
26
|
|
|
28
|
-
import { type BotConfig, SupportedTokenContracts
|
|
27
|
+
import { type BotConfig, SupportedTokenContracts } from './config.js';
|
|
28
|
+
import type { BotStore } from './store/index.js';
|
|
29
29
|
import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
|
|
30
30
|
|
|
31
31
|
const MINT_BALANCE = 1e12;
|
|
32
32
|
const MIN_BALANCE = 1e3;
|
|
33
33
|
|
|
34
34
|
export class BotFactory {
|
|
35
|
-
private pxe: PXE;
|
|
36
|
-
private node?: AztecNode;
|
|
37
|
-
private nodeAdmin?: AztecNodeAdmin;
|
|
38
35
|
private log = createLogger('bot');
|
|
39
36
|
|
|
40
37
|
constructor(
|
|
41
38
|
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
|
-
}
|
|
39
|
+
private readonly wallet: TestWallet,
|
|
40
|
+
private readonly store: BotStore,
|
|
41
|
+
private readonly aztecNode: AztecNode,
|
|
42
|
+
private readonly aztecNodeAdmin?: AztecNodeAdmin,
|
|
43
|
+
) {}
|
|
69
44
|
|
|
70
45
|
/**
|
|
71
46
|
* Initializes a new bot by setting up the sender account, registering the recipient,
|
|
72
47
|
* deploying the token contract, and minting tokens if necessary.
|
|
73
48
|
*/
|
|
74
49
|
public async setup() {
|
|
75
|
-
const recipient = await this.
|
|
76
|
-
const
|
|
77
|
-
const
|
|
78
|
-
const token = await this.setupToken(wallet, defaultAccountAddress);
|
|
50
|
+
const recipient = (await this.wallet.createAccount()).address;
|
|
51
|
+
const defaultAccountAddress = await this.setupAccount();
|
|
52
|
+
const token = await this.setupToken(defaultAccountAddress);
|
|
79
53
|
await this.mintTokens(token, defaultAccountAddress);
|
|
80
|
-
return { wallet, defaultAccountAddress, token,
|
|
54
|
+
return { wallet: this.wallet, defaultAccountAddress, token, node: this.aztecNode, recipient };
|
|
81
55
|
}
|
|
82
56
|
|
|
83
57
|
public async setupAmm() {
|
|
84
|
-
const
|
|
85
|
-
const
|
|
86
|
-
const
|
|
87
|
-
wallet,
|
|
88
|
-
wallet.getAddress(),
|
|
89
|
-
this.config.tokenSalt,
|
|
90
|
-
'BotToken0',
|
|
91
|
-
'BOT0',
|
|
92
|
-
);
|
|
93
|
-
const token1 = await this.setupTokenContract(
|
|
94
|
-
wallet,
|
|
95
|
-
wallet.getAddress(),
|
|
96
|
-
this.config.tokenSalt,
|
|
97
|
-
'BotToken1',
|
|
98
|
-
'BOT1',
|
|
99
|
-
);
|
|
58
|
+
const defaultAccountAddress = await this.setupAccount();
|
|
59
|
+
const token0 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken0', 'BOT0');
|
|
60
|
+
const token1 = await this.setupTokenContract(defaultAccountAddress, this.config.tokenSalt, 'BotToken1', 'BOT1');
|
|
100
61
|
const liquidityToken = await this.setupTokenContract(
|
|
101
|
-
|
|
102
|
-
wallet.getAddress(),
|
|
62
|
+
defaultAccountAddress,
|
|
103
63
|
this.config.tokenSalt,
|
|
104
64
|
'BotLPToken',
|
|
105
65
|
'BOTLP',
|
|
106
66
|
);
|
|
107
67
|
const amm = await this.setupAmmContract(
|
|
108
|
-
|
|
109
|
-
wallet.getAddress(),
|
|
68
|
+
defaultAccountAddress,
|
|
110
69
|
this.config.tokenSalt,
|
|
111
70
|
token0,
|
|
112
71
|
token1,
|
|
113
72
|
liquidityToken,
|
|
114
73
|
);
|
|
115
74
|
|
|
116
|
-
await this.fundAmm(
|
|
75
|
+
await this.fundAmm(defaultAccountAddress, defaultAccountAddress, amm, token0, token1, liquidityToken);
|
|
117
76
|
this.log.info(`AMM initialized and funded`);
|
|
118
77
|
|
|
119
|
-
return { wallet, defaultAccountAddress, amm, token0, token1,
|
|
78
|
+
return { wallet: this.wallet, defaultAccountAddress, amm, token0, token1, node: this.aztecNode };
|
|
120
79
|
}
|
|
121
80
|
|
|
122
81
|
/**
|
|
@@ -126,62 +85,63 @@ export class BotFactory {
|
|
|
126
85
|
private async setupAccount() {
|
|
127
86
|
const privateKey = this.config.senderPrivateKey?.getValue();
|
|
128
87
|
if (privateKey) {
|
|
88
|
+
this.log.info(`Setting up account with provided private key`);
|
|
129
89
|
return await this.setupAccountWithPrivateKey(privateKey);
|
|
130
90
|
} else {
|
|
91
|
+
this.log.info(`Setting up test account`);
|
|
131
92
|
return await this.setupTestAccount();
|
|
132
93
|
}
|
|
133
94
|
}
|
|
134
95
|
|
|
135
|
-
private async setupAccountWithPrivateKey(
|
|
96
|
+
private async setupAccountWithPrivateKey(secret: Fr) {
|
|
136
97
|
const salt = this.config.senderSalt ?? Fr.ONE;
|
|
137
|
-
const signingKey = deriveSigningKey(
|
|
138
|
-
const
|
|
139
|
-
|
|
98
|
+
const signingKey = deriveSigningKey(secret);
|
|
99
|
+
const accountData = {
|
|
100
|
+
secret,
|
|
101
|
+
salt,
|
|
102
|
+
contract: new SchnorrAccountContract(signingKey!),
|
|
103
|
+
};
|
|
104
|
+
const accountManager = await this.wallet.createAccount(accountData);
|
|
105
|
+
const isInit = (await this.wallet.getContractMetadata(accountManager.address)).isContractInitialized;
|
|
140
106
|
if (isInit) {
|
|
141
|
-
this.log.info(`Account at ${
|
|
107
|
+
this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
|
|
142
108
|
const timer = new Timer();
|
|
143
|
-
const
|
|
144
|
-
this.log.info(`Account at ${
|
|
145
|
-
|
|
109
|
+
const address = accountManager.address;
|
|
110
|
+
this.log.info(`Account at ${address} registered. duration=${timer.ms()}`);
|
|
111
|
+
await this.store.deleteBridgeClaim(address);
|
|
112
|
+
return address;
|
|
146
113
|
} else {
|
|
147
|
-
const address =
|
|
114
|
+
const address = accountManager.address;
|
|
148
115
|
this.log.info(`Deploying account at ${address}`);
|
|
149
116
|
|
|
150
|
-
const claim = await this.
|
|
117
|
+
const claim = await this.getOrCreateBridgeClaim(address);
|
|
151
118
|
|
|
152
|
-
|
|
153
|
-
const
|
|
154
|
-
const
|
|
155
|
-
const
|
|
119
|
+
const paymentMethod = new FeeJuicePaymentMethodWithClaim(accountManager.address, claim);
|
|
120
|
+
const deployMethod = await accountManager.getDeployMethod();
|
|
121
|
+
const maxFeesPerGas = (await this.aztecNode.getCurrentBaseFees()).mul(1 + this.config.baseFeePadding);
|
|
122
|
+
const gasSettings = GasSettings.default({ maxFeesPerGas });
|
|
123
|
+
const sentTx = deployMethod.send({ from: AztecAddress.ZERO, fee: { gasSettings, paymentMethod } });
|
|
156
124
|
const txHash = await sentTx.getTxHash();
|
|
157
|
-
// docs:end:claim_and_deploy
|
|
158
125
|
this.log.info(`Sent tx for account deployment with hash ${txHash.toString()}`);
|
|
159
126
|
await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
|
|
160
127
|
this.log.info(`Account deployed at ${address}`);
|
|
161
|
-
return wallet;
|
|
162
|
-
}
|
|
163
|
-
}
|
|
164
128
|
|
|
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()}`);
|
|
129
|
+
// Clean up the consumed bridge claim
|
|
130
|
+
await this.store.deleteBridgeClaim(address);
|
|
131
|
+
|
|
132
|
+
return accountManager.address;
|
|
175
133
|
}
|
|
176
|
-
return wallet;
|
|
177
134
|
}
|
|
178
135
|
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
136
|
+
private async setupTestAccount() {
|
|
137
|
+
const [initialAccountData] = await getInitialTestAccountsData();
|
|
138
|
+
const accountData = {
|
|
139
|
+
secret: initialAccountData.secret,
|
|
140
|
+
salt: initialAccountData.salt,
|
|
141
|
+
contract: new SchnorrAccountContract(initialAccountData.signingKey),
|
|
142
|
+
};
|
|
143
|
+
const accountManager = await this.wallet.createAccount(accountData);
|
|
144
|
+
return accountManager.address;
|
|
185
145
|
}
|
|
186
146
|
|
|
187
147
|
/**
|
|
@@ -189,7 +149,7 @@ export class BotFactory {
|
|
|
189
149
|
* @param wallet - Wallet to deploy the token contract from.
|
|
190
150
|
* @returns The TokenContract instance.
|
|
191
151
|
*/
|
|
192
|
-
private async setupToken(
|
|
152
|
+
private async setupToken(sender: AztecAddress): Promise<TokenContract | PrivateTokenContract> {
|
|
193
153
|
let deploy: DeployMethod<TokenContract | PrivateTokenContract>;
|
|
194
154
|
const deployOpts: DeployOptions = {
|
|
195
155
|
from: sender,
|
|
@@ -197,9 +157,9 @@ export class BotFactory {
|
|
|
197
157
|
universalDeploy: true,
|
|
198
158
|
};
|
|
199
159
|
if (this.config.contract === SupportedTokenContracts.TokenContract) {
|
|
200
|
-
deploy = TokenContract.deploy(wallet, sender, 'BotToken', 'BOT', 18);
|
|
160
|
+
deploy = TokenContract.deploy(this.wallet, sender, 'BotToken', 'BOT', 18);
|
|
201
161
|
} else if (this.config.contract === SupportedTokenContracts.PrivateTokenContract) {
|
|
202
|
-
deploy = PrivateTokenContract.deploy(wallet, MINT_BALANCE, sender);
|
|
162
|
+
deploy = PrivateTokenContract.deploy(this.wallet, MINT_BALANCE, sender);
|
|
203
163
|
deployOpts.skipInstancePublication = true;
|
|
204
164
|
deployOpts.skipClassPublication = true;
|
|
205
165
|
deployOpts.skipInitialization = false;
|
|
@@ -208,7 +168,7 @@ export class BotFactory {
|
|
|
208
168
|
}
|
|
209
169
|
|
|
210
170
|
const address = (await deploy.getInstance(deployOpts)).address;
|
|
211
|
-
if ((await this.
|
|
171
|
+
if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
|
|
212
172
|
this.log.info(`Token at ${address.toString()} already deployed`);
|
|
213
173
|
return deploy.register();
|
|
214
174
|
} else {
|
|
@@ -226,7 +186,6 @@ export class BotFactory {
|
|
|
226
186
|
* @returns The TokenContract instance.
|
|
227
187
|
*/
|
|
228
188
|
private setupTokenContract(
|
|
229
|
-
wallet: AccountWallet,
|
|
230
189
|
deployer: AztecAddress,
|
|
231
190
|
contractAddressSalt: Fr,
|
|
232
191
|
name: string,
|
|
@@ -234,12 +193,11 @@ export class BotFactory {
|
|
|
234
193
|
decimals = 18,
|
|
235
194
|
): Promise<TokenContract> {
|
|
236
195
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
237
|
-
const deploy = TokenContract.deploy(wallet, deployer, name, ticker, decimals);
|
|
196
|
+
const deploy = TokenContract.deploy(this.wallet, deployer, name, ticker, decimals);
|
|
238
197
|
return this.registerOrDeployContract('Token - ' + name, deploy, deployOpts);
|
|
239
198
|
}
|
|
240
199
|
|
|
241
200
|
private async setupAmmContract(
|
|
242
|
-
wallet: AccountWallet,
|
|
243
201
|
deployer: AztecAddress,
|
|
244
202
|
contractAddressSalt: Fr,
|
|
245
203
|
token0: TokenContract,
|
|
@@ -247,7 +205,7 @@ export class BotFactory {
|
|
|
247
205
|
lpToken: TokenContract,
|
|
248
206
|
): Promise<AMMContract> {
|
|
249
207
|
const deployOpts: DeployOptions = { from: deployer, contractAddressSalt, universalDeploy: true };
|
|
250
|
-
const deploy = AMMContract.deploy(wallet, token0.address, token1.address, lpToken.address);
|
|
208
|
+
const deploy = AMMContract.deploy(this.wallet, token0.address, token1.address, lpToken.address);
|
|
251
209
|
const amm = await this.registerOrDeployContract('AMM', deploy, deployOpts);
|
|
252
210
|
|
|
253
211
|
this.log.info(`AMM deployed at ${amm.address}`);
|
|
@@ -260,7 +218,7 @@ export class BotFactory {
|
|
|
260
218
|
}
|
|
261
219
|
|
|
262
220
|
private async fundAmm(
|
|
263
|
-
|
|
221
|
+
defaultAccountAddress: AztecAddress,
|
|
264
222
|
liquidityProvider: AztecAddress,
|
|
265
223
|
amm: AMMContract,
|
|
266
224
|
token0: TokenContract,
|
|
@@ -289,26 +247,30 @@ export class BotFactory {
|
|
|
289
247
|
);
|
|
290
248
|
|
|
291
249
|
// Add authwitnesses for the transfers in AMM::add_liquidity function
|
|
292
|
-
const token0Authwit = await wallet.createAuthWit({
|
|
250
|
+
const token0Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
|
|
293
251
|
caller: amm.address,
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
252
|
+
call: await token0.methods
|
|
253
|
+
.transfer_to_public_and_prepare_private_balance_increase(
|
|
254
|
+
liquidityProvider,
|
|
255
|
+
amm.address,
|
|
256
|
+
amount0Max,
|
|
257
|
+
authwitNonce,
|
|
258
|
+
)
|
|
259
|
+
.getFunctionCall(),
|
|
300
260
|
});
|
|
301
|
-
const token1Authwit = await wallet.createAuthWit({
|
|
261
|
+
const token1Authwit = await this.wallet.createAuthWit(defaultAccountAddress, {
|
|
302
262
|
caller: amm.address,
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
263
|
+
call: await token1.methods
|
|
264
|
+
.transfer_to_public_and_prepare_private_balance_increase(
|
|
265
|
+
liquidityProvider,
|
|
266
|
+
amm.address,
|
|
267
|
+
amount1Max,
|
|
268
|
+
authwitNonce,
|
|
269
|
+
)
|
|
270
|
+
.getFunctionCall(),
|
|
309
271
|
});
|
|
310
272
|
|
|
311
|
-
const mintTx = new BatchCall(wallet, [
|
|
273
|
+
const mintTx = new BatchCall(this.wallet, [
|
|
312
274
|
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
313
275
|
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
314
276
|
]).send({ from: liquidityProvider });
|
|
@@ -329,7 +291,7 @@ export class BotFactory {
|
|
|
329
291
|
|
|
330
292
|
const [newT0Bal, newT1Bal, newLPBal] = await getPrivateBalances();
|
|
331
293
|
this.log.info(
|
|
332
|
-
`Updated private balances of ${
|
|
294
|
+
`Updated private balances of ${defaultAccountAddress} after minting and funding AMM: token0=${newT0Bal}, token1=${newT1Bal}, lp=${newLPBal}`,
|
|
333
295
|
);
|
|
334
296
|
}
|
|
335
297
|
|
|
@@ -339,7 +301,7 @@ export class BotFactory {
|
|
|
339
301
|
deployOpts: DeployOptions,
|
|
340
302
|
): Promise<T> {
|
|
341
303
|
const address = (await deploy.getInstance(deployOpts)).address;
|
|
342
|
-
if ((await this.
|
|
304
|
+
if ((await this.wallet.getContractMetadata(address)).isContractPublished) {
|
|
343
305
|
this.log.info(`Contract ${name} at ${address.toString()} already deployed`);
|
|
344
306
|
return deploy.register();
|
|
345
307
|
} else {
|
|
@@ -390,7 +352,40 @@ export class BotFactory {
|
|
|
390
352
|
await this.withNoMinTxsPerBlock(() => sentTx.wait({ timeout: this.config.txMinedWaitSeconds }));
|
|
391
353
|
}
|
|
392
354
|
|
|
393
|
-
|
|
355
|
+
/**
|
|
356
|
+
* Gets or creates a bridge claim for the recipient.
|
|
357
|
+
* Checks if a claim already exists in the store and reuses it if valid.
|
|
358
|
+
* Only creates a new bridge if fee juice balance is below threshold.
|
|
359
|
+
*/
|
|
360
|
+
private async getOrCreateBridgeClaim(recipient: AztecAddress): Promise<L2AmountClaim> {
|
|
361
|
+
// Check if we have an existing claim in the store
|
|
362
|
+
const existingClaim = await this.store.getBridgeClaim(recipient);
|
|
363
|
+
if (existingClaim) {
|
|
364
|
+
this.log.info(`Found existing bridge claim for ${recipient.toString()}, checking validity...`);
|
|
365
|
+
|
|
366
|
+
// Check if the message is ready on L2
|
|
367
|
+
try {
|
|
368
|
+
const messageHash = Fr.fromHexString(existingClaim.claim.messageHash);
|
|
369
|
+
await this.withNoMinTxsPerBlock(() =>
|
|
370
|
+
waitForL1ToL2MessageReady(this.aztecNode, messageHash, {
|
|
371
|
+
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
|
|
372
|
+
forPublicConsumption: false,
|
|
373
|
+
}),
|
|
374
|
+
);
|
|
375
|
+
return existingClaim.claim;
|
|
376
|
+
} catch (err) {
|
|
377
|
+
this.log.warn(`Failed to verify existing claim, creating new one: ${err}`);
|
|
378
|
+
await this.store.deleteBridgeClaim(recipient);
|
|
379
|
+
}
|
|
380
|
+
}
|
|
381
|
+
|
|
382
|
+
const claim = await this.bridgeL1FeeJuice(recipient);
|
|
383
|
+
await this.store.saveBridgeClaim(recipient, claim);
|
|
384
|
+
|
|
385
|
+
return claim;
|
|
386
|
+
}
|
|
387
|
+
|
|
388
|
+
private async bridgeL1FeeJuice(recipient: AztecAddress): Promise<L2AmountClaim> {
|
|
394
389
|
const l1RpcUrls = this.config.l1RpcUrls;
|
|
395
390
|
if (!l1RpcUrls?.length) {
|
|
396
391
|
throw new Error('L1 Rpc url is required to bridge the fee juice to fund the deployment of the account.');
|
|
@@ -402,16 +397,16 @@ export class BotFactory {
|
|
|
402
397
|
);
|
|
403
398
|
}
|
|
404
399
|
|
|
405
|
-
const { l1ChainId } = await this.
|
|
400
|
+
const { l1ChainId } = await this.aztecNode.getNodeInfo();
|
|
406
401
|
const chain = createEthereumChain(l1RpcUrls, l1ChainId);
|
|
407
402
|
const extendedClient = createExtendedL1Client(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
|
|
408
403
|
|
|
409
|
-
const portal = await L1FeeJuicePortalManager.new(this.
|
|
404
|
+
const portal = await L1FeeJuicePortalManager.new(this.aztecNode, extendedClient, this.log);
|
|
410
405
|
const mintAmount = await portal.getTokenManager().getMintAmount();
|
|
411
406
|
const claim = await portal.bridgeTokensPublic(recipient, mintAmount, true /* mint */);
|
|
412
407
|
|
|
413
408
|
await this.withNoMinTxsPerBlock(() =>
|
|
414
|
-
waitForL1ToL2MessageReady(this.
|
|
409
|
+
waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(claim.messageHash), {
|
|
415
410
|
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
|
|
416
411
|
forPublicConsumption: false,
|
|
417
412
|
}),
|
|
@@ -419,19 +414,22 @@ export class BotFactory {
|
|
|
419
414
|
|
|
420
415
|
this.log.info(`Created a claim for ${mintAmount} L1 fee juice to ${recipient}.`, claim);
|
|
421
416
|
|
|
422
|
-
return claim;
|
|
417
|
+
return claim as L2AmountClaim;
|
|
423
418
|
}
|
|
424
419
|
|
|
425
420
|
private async withNoMinTxsPerBlock<T>(fn: () => Promise<T>): Promise<T> {
|
|
426
|
-
if (!this.
|
|
421
|
+
if (!this.aztecNodeAdmin || !this.config.flushSetupTransactions) {
|
|
422
|
+
this.log.verbose(`No node admin client or flushing not requested (not setting minTxsPerBlock to 0)`);
|
|
427
423
|
return fn();
|
|
428
424
|
}
|
|
429
|
-
const { minTxsPerBlock } = await this.
|
|
430
|
-
|
|
425
|
+
const { minTxsPerBlock } = await this.aztecNodeAdmin.getConfig();
|
|
426
|
+
this.log.warn(`Setting sequencer minTxsPerBlock to 0 from ${minTxsPerBlock} to flush setup transactions`);
|
|
427
|
+
await this.aztecNodeAdmin.setConfig({ minTxsPerBlock: 0 });
|
|
431
428
|
try {
|
|
432
429
|
return await fn();
|
|
433
430
|
} finally {
|
|
434
|
-
|
|
431
|
+
this.log.warn(`Restoring sequencer minTxsPerBlock to ${minTxsPerBlock}`);
|
|
432
|
+
await this.aztecNodeAdmin.setConfig({ minTxsPerBlock });
|
|
435
433
|
}
|
|
436
434
|
}
|
|
437
435
|
}
|
package/src/index.ts
CHANGED
package/src/interface.ts
CHANGED