@aztec/bot 0.0.0-test.1 → 0.0.1-commit.001888fc
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 +32 -0
- package/dest/amm_bot.d.ts.map +1 -0
- package/dest/amm_bot.js +108 -0
- package/dest/base_bot.d.ts +21 -0
- package/dest/base_bot.d.ts.map +1 -0
- package/dest/base_bot.js +79 -0
- package/dest/bot.d.ts +13 -18
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +26 -84
- package/dest/config.d.ts +106 -66
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +89 -39
- package/dest/cross_chain_bot.d.ts +54 -0
- package/dest/cross_chain_bot.d.ts.map +1 -0
- package/dest/cross_chain_bot.js +134 -0
- package/dest/factory.d.ts +43 -29
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +380 -139
- package/dest/index.d.ts +5 -2
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +4 -1
- package/dest/interface.d.ts +12 -1
- package/dest/interface.d.ts.map +1 -1
- package/dest/interface.js +5 -0
- package/dest/l1_to_l2_seeding.d.ts +8 -0
- package/dest/l1_to_l2_seeding.d.ts.map +1 -0
- package/dest/l1_to_l2_seeding.js +63 -0
- package/dest/rpc.d.ts +1 -7
- package/dest/rpc.d.ts.map +1 -1
- package/dest/rpc.js +0 -11
- package/dest/runner.d.ts +15 -11
- package/dest/runner.d.ts.map +1 -1
- package/dest/runner.js +457 -51
- package/dest/store/bot_store.d.ts +69 -0
- package/dest/store/bot_store.d.ts.map +1 -0
- package/dest/store/bot_store.js +138 -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 +8 -5
- package/dest/utils.d.ts.map +1 -1
- package/dest/utils.js +14 -5
- package/package.json +30 -23
- package/src/amm_bot.ts +129 -0
- package/src/base_bot.ts +82 -0
- package/src/bot.ts +52 -101
- package/src/config.ts +129 -71
- package/src/cross_chain_bot.ts +203 -0
- package/src/factory.ts +476 -152
- package/src/index.ts +4 -1
- package/src/interface.ts +9 -0
- package/src/l1_to_l2_seeding.ts +79 -0
- package/src/rpc.ts +0 -13
- package/src/runner.ts +51 -21
- package/src/store/bot_store.ts +196 -0
- package/src/store/index.ts +1 -0
- package/src/utils.ts +17 -6
package/src/index.ts
CHANGED
|
@@ -1,5 +1,8 @@
|
|
|
1
1
|
export { Bot } from './bot.js';
|
|
2
|
+
export { AmmBot } from './amm_bot.js';
|
|
3
|
+
export { CrossChainBot } from './cross_chain_bot.js';
|
|
2
4
|
export { BotRunner } from './runner.js';
|
|
5
|
+
export { BotStore } from './store/bot_store.js';
|
|
3
6
|
export {
|
|
4
7
|
type BotConfig,
|
|
5
8
|
getBotConfigFromEnv,
|
|
@@ -7,5 +10,5 @@ export {
|
|
|
7
10
|
botConfigMappings,
|
|
8
11
|
SupportedTokenContracts,
|
|
9
12
|
} from './config.js';
|
|
10
|
-
export {
|
|
13
|
+
export { getBotRunnerApiHandler } from './rpc.js';
|
|
11
14
|
export * from './interface.js';
|
package/src/interface.ts
CHANGED
|
@@ -1,15 +1,23 @@
|
|
|
1
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
1
2
|
import type { ApiSchemaFor } from '@aztec/stdlib/schemas';
|
|
2
3
|
|
|
3
4
|
import { z } from 'zod';
|
|
4
5
|
|
|
5
6
|
import { type BotConfig, BotConfigSchema } from './config.js';
|
|
6
7
|
|
|
8
|
+
export const BotInfoSchema = z.object({
|
|
9
|
+
botAddress: AztecAddress.schema,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
export type BotInfo = z.infer<typeof BotInfoSchema>;
|
|
13
|
+
|
|
7
14
|
export interface BotRunnerApi {
|
|
8
15
|
start(): Promise<void>;
|
|
9
16
|
stop(): Promise<void>;
|
|
10
17
|
run(): Promise<void>;
|
|
11
18
|
setup(): Promise<void>;
|
|
12
19
|
getConfig(): Promise<BotConfig>;
|
|
20
|
+
getInfo(): Promise<BotInfo>;
|
|
13
21
|
update(config: BotConfig): Promise<void>;
|
|
14
22
|
}
|
|
15
23
|
|
|
@@ -18,6 +26,7 @@ export const BotRunnerApiSchema: ApiSchemaFor<BotRunnerApi> = {
|
|
|
18
26
|
stop: z.function().args().returns(z.void()),
|
|
19
27
|
run: z.function().args().returns(z.void()),
|
|
20
28
|
setup: z.function().args().returns(z.void()),
|
|
29
|
+
getInfo: z.function().args().returns(BotInfoSchema),
|
|
21
30
|
getConfig: z.function().args().returns(BotConfigSchema),
|
|
22
31
|
update: z.function().args(BotConfigSchema).returns(z.void()),
|
|
23
32
|
};
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import { generateClaimSecret } from '@aztec/aztec.js/ethereum';
|
|
2
|
+
import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
|
|
3
|
+
import { compactArray } from '@aztec/foundation/collection';
|
|
4
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
5
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
6
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
7
|
+
import { InboxAbi } from '@aztec/l1-artifacts';
|
|
8
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
9
|
+
|
|
10
|
+
import { decodeEventLog, getContract } from 'viem';
|
|
11
|
+
|
|
12
|
+
import type { BotStore, PendingL1ToL2Message } from './store/index.js';
|
|
13
|
+
|
|
14
|
+
/** Sends an L1→L2 message via the Inbox contract and stores it. */
|
|
15
|
+
export async function seedL1ToL2Message(
|
|
16
|
+
l1Client: ExtendedViemWalletClient,
|
|
17
|
+
inboxAddress: EthAddress,
|
|
18
|
+
l2Recipient: AztecAddress,
|
|
19
|
+
rollupVersion: bigint,
|
|
20
|
+
store: BotStore,
|
|
21
|
+
log: Logger,
|
|
22
|
+
): Promise<PendingL1ToL2Message> {
|
|
23
|
+
log.info('Seeding L1→L2 message');
|
|
24
|
+
const [secret, secretHash] = await generateClaimSecret(log);
|
|
25
|
+
const content = Fr.random();
|
|
26
|
+
|
|
27
|
+
const inbox = getContract({
|
|
28
|
+
address: inboxAddress.toString(),
|
|
29
|
+
abi: InboxAbi,
|
|
30
|
+
client: l1Client,
|
|
31
|
+
});
|
|
32
|
+
|
|
33
|
+
const txHash = await inbox.write.sendL2Message(
|
|
34
|
+
[{ actor: l2Recipient.toString(), version: rollupVersion }, content.toString(), secretHash.toString()],
|
|
35
|
+
{ gas: 1_000_000n },
|
|
36
|
+
);
|
|
37
|
+
log.info(`L1→L2 message sent in tx ${txHash}`);
|
|
38
|
+
|
|
39
|
+
const txReceipt = await l1Client.waitForTransactionReceipt({ hash: txHash });
|
|
40
|
+
if (txReceipt.status !== 'success') {
|
|
41
|
+
throw new Error(`L1→L2 message tx failed: ${txHash}`);
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Extract MessageSent event
|
|
45
|
+
const messageSentLogs = compactArray(
|
|
46
|
+
txReceipt.logs
|
|
47
|
+
.filter(l => l.address.toLowerCase() === inboxAddress.toString().toLowerCase())
|
|
48
|
+
.map(l => {
|
|
49
|
+
try {
|
|
50
|
+
return decodeEventLog({ abi: InboxAbi, eventName: 'MessageSent', data: l.data, topics: l.topics });
|
|
51
|
+
} catch {
|
|
52
|
+
return undefined;
|
|
53
|
+
}
|
|
54
|
+
}),
|
|
55
|
+
);
|
|
56
|
+
|
|
57
|
+
if (messageSentLogs.length !== 1) {
|
|
58
|
+
throw new Error(`Expected 1 MessageSent event, got ${messageSentLogs.length}`);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
const event = messageSentLogs[0];
|
|
62
|
+
|
|
63
|
+
const msgHash = event.args.hash;
|
|
64
|
+
const globalLeafIndex = event.args.index;
|
|
65
|
+
|
|
66
|
+
const msg: PendingL1ToL2Message = {
|
|
67
|
+
content: content.toString(),
|
|
68
|
+
secret: secret.toString(),
|
|
69
|
+
secretHash: secretHash.toString(),
|
|
70
|
+
msgHash,
|
|
71
|
+
sender: l1Client.account!.address,
|
|
72
|
+
globalLeafIndex: globalLeafIndex.toString(),
|
|
73
|
+
timestamp: Date.now(),
|
|
74
|
+
};
|
|
75
|
+
|
|
76
|
+
await store.savePendingL1ToL2Message(msg);
|
|
77
|
+
log.info(`Seeded L1→L2 message msgHash=${msg.msgHash}`);
|
|
78
|
+
return msg;
|
|
79
|
+
}
|
package/src/rpc.ts
CHANGED
|
@@ -1,21 +1,8 @@
|
|
|
1
1
|
import type { ApiHandler } from '@aztec/foundation/json-rpc/server';
|
|
2
|
-
import { createTracedJsonRpcServer } from '@aztec/telemetry-client';
|
|
3
2
|
|
|
4
3
|
import { BotRunnerApiSchema } from './interface.js';
|
|
5
4
|
import type { BotRunner } from './runner.js';
|
|
6
5
|
|
|
7
|
-
/**
|
|
8
|
-
* Wraps a bot runner with a JSON RPC HTTP server.
|
|
9
|
-
* @param botRunner - The BotRunner.
|
|
10
|
-
* @returns An JSON-RPC HTTP server
|
|
11
|
-
*/
|
|
12
|
-
export function createBotRunnerRpcServer(botRunner: BotRunner) {
|
|
13
|
-
createTracedJsonRpcServer(botRunner, BotRunnerApiSchema, {
|
|
14
|
-
http200OnError: false,
|
|
15
|
-
healthCheck: botRunner.isHealthy.bind(botRunner),
|
|
16
|
-
});
|
|
17
|
-
}
|
|
18
|
-
|
|
19
6
|
export function getBotRunnerApiHandler(botRunner: BotRunner): ApiHandler {
|
|
20
7
|
return [botRunner, BotRunnerApiSchema, botRunner.isHealthy.bind(botRunner)];
|
|
21
8
|
}
|
package/src/runner.ts
CHANGED
|
@@ -1,16 +1,22 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { createLogger } from '@aztec/aztec.js/log';
|
|
2
|
+
import type { AztecNode } from '@aztec/aztec.js/node';
|
|
3
|
+
import { omit } from '@aztec/foundation/collection';
|
|
2
4
|
import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
3
|
-
import
|
|
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 { EmbeddedWallet } from '@aztec/wallets/embedded';
|
|
4
8
|
|
|
9
|
+
import { AmmBot } from './amm_bot.js';
|
|
10
|
+
import type { BaseBot } from './base_bot.js';
|
|
5
11
|
import { Bot } from './bot.js';
|
|
6
|
-
import {
|
|
7
|
-
import
|
|
12
|
+
import type { BotConfig } from './config.js';
|
|
13
|
+
import { CrossChainBot } from './cross_chain_bot.js';
|
|
14
|
+
import type { BotInfo, BotRunnerApi } from './interface.js';
|
|
15
|
+
import { BotStore } from './store/index.js';
|
|
8
16
|
|
|
9
17
|
export class BotRunner implements BotRunnerApi, Traceable {
|
|
10
18
|
private log = createLogger('bot');
|
|
11
|
-
private bot?: Promise<
|
|
12
|
-
private pxe?: PXE;
|
|
13
|
-
private node: AztecNode;
|
|
19
|
+
private bot?: Promise<BaseBot>;
|
|
14
20
|
private runningPromise: RunningPromise;
|
|
15
21
|
private consecutiveErrors = 0;
|
|
16
22
|
private healthy = true;
|
|
@@ -19,15 +25,14 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
19
25
|
|
|
20
26
|
public constructor(
|
|
21
27
|
private config: BotConfig,
|
|
22
|
-
|
|
28
|
+
private readonly wallet: EmbeddedWallet,
|
|
29
|
+
private readonly aztecNode: AztecNode,
|
|
30
|
+
private readonly telemetry: TelemetryClient,
|
|
31
|
+
private readonly aztecNodeAdmin: AztecNodeAdmin | undefined,
|
|
32
|
+
private readonly store: BotStore,
|
|
23
33
|
) {
|
|
24
|
-
this.tracer =
|
|
25
|
-
|
|
26
|
-
if (!dependencies.node && !config.nodeUrl) {
|
|
27
|
-
throw new Error(`Missing node URL in config or dependencies`);
|
|
28
|
-
}
|
|
29
|
-
this.node =
|
|
30
|
-
dependencies.node ?? createAztecNodeClient(config.nodeUrl!, getVersions(), makeTracedFetch([1, 2, 3], true));
|
|
34
|
+
this.tracer = telemetry.getTracer('Bot');
|
|
35
|
+
|
|
31
36
|
this.runningPromise = new RunningPromise(() => this.#work(), this.log, config.txIntervalSeconds * 1000);
|
|
32
37
|
}
|
|
33
38
|
|
|
@@ -65,6 +70,7 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
65
70
|
this.log.verbose(`Stopping bot`);
|
|
66
71
|
await this.runningPromise.stop();
|
|
67
72
|
}
|
|
73
|
+
await this.store.close();
|
|
68
74
|
this.log.info(`Stopped bot`);
|
|
69
75
|
}
|
|
70
76
|
|
|
@@ -126,12 +132,36 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
126
132
|
|
|
127
133
|
/** Returns the current configuration for the bot. */
|
|
128
134
|
public getConfig() {
|
|
129
|
-
|
|
135
|
+
const redacted = omit(this.config, 'l1Mnemonic', 'l1PrivateKey', 'senderPrivateKey');
|
|
136
|
+
return Promise.resolve(redacted as BotConfig);
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
/** Returns the bot sender address. */
|
|
140
|
+
public async getInfo(): Promise<BotInfo> {
|
|
141
|
+
if (!this.bot) {
|
|
142
|
+
throw new Error(`Bot is not initialized`);
|
|
143
|
+
}
|
|
144
|
+
const botAddress = await this.bot.then(b => b.defaultAccountAddress);
|
|
145
|
+
return { botAddress };
|
|
130
146
|
}
|
|
131
147
|
|
|
132
148
|
async #createBot() {
|
|
133
149
|
try {
|
|
134
|
-
|
|
150
|
+
switch (this.config.botMode) {
|
|
151
|
+
case 'crosschain':
|
|
152
|
+
this.bot = CrossChainBot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
153
|
+
break;
|
|
154
|
+
case 'amm':
|
|
155
|
+
this.bot = AmmBot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
156
|
+
break;
|
|
157
|
+
case 'transfer':
|
|
158
|
+
this.bot = Bot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
159
|
+
break;
|
|
160
|
+
default: {
|
|
161
|
+
const _exhaustive: never = this.config.botMode;
|
|
162
|
+
throw new Error(`Unsupported bot mode: [${_exhaustive}]`);
|
|
163
|
+
}
|
|
164
|
+
}
|
|
135
165
|
await this.bot;
|
|
136
166
|
} catch (err) {
|
|
137
167
|
this.log.error(`Error setting up bot: ${err}`);
|
|
@@ -142,16 +172,16 @@ export class BotRunner implements BotRunnerApi, Traceable {
|
|
|
142
172
|
@trackSpan('Bot.work')
|
|
143
173
|
async #work() {
|
|
144
174
|
if (this.config.maxPendingTxs > 0) {
|
|
145
|
-
const
|
|
146
|
-
if (
|
|
147
|
-
this.log.verbose(`Not sending bot tx since node has ${
|
|
175
|
+
const pendingTxCount = await this.aztecNode.getPendingTxCount();
|
|
176
|
+
if (pendingTxCount >= this.config.maxPendingTxs) {
|
|
177
|
+
this.log.verbose(`Not sending bot tx since node has ${pendingTxCount} pending txs`);
|
|
148
178
|
return;
|
|
149
179
|
}
|
|
150
180
|
}
|
|
151
181
|
|
|
152
182
|
try {
|
|
153
183
|
await this.run();
|
|
154
|
-
} catch
|
|
184
|
+
} catch {
|
|
155
185
|
// Already logged in run()
|
|
156
186
|
if (this.config.maxConsecutiveErrors > 0 && this.consecutiveErrors >= this.config.maxConsecutiveErrors) {
|
|
157
187
|
this.log.error(`Too many errors bot is unhealthy`);
|
|
@@ -0,0 +1,196 @@
|
|
|
1
|
+
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
2
|
+
import type { L2AmountClaim } from '@aztec/aztec.js/ethereum';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { type Logger, createLogger } from '@aztec/foundation/log';
|
|
5
|
+
import { DateProvider } from '@aztec/foundation/timer';
|
|
6
|
+
import type { AztecAsyncKVStore, AztecAsyncMap } from '@aztec/kv-store';
|
|
7
|
+
|
|
8
|
+
export interface BridgeClaimData {
|
|
9
|
+
claim: L2AmountClaim;
|
|
10
|
+
timestamp: number;
|
|
11
|
+
recipient: string;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
export interface PendingL1ToL2Message {
|
|
15
|
+
/** Random content field sent in the message. */
|
|
16
|
+
content: string;
|
|
17
|
+
/** Secret for consuming the message. */
|
|
18
|
+
secret: string;
|
|
19
|
+
/** Hash of the secret. */
|
|
20
|
+
secretHash: string;
|
|
21
|
+
/** Hash of the L1→L2 message. */
|
|
22
|
+
msgHash: string;
|
|
23
|
+
/** L1 sender address (hex). */
|
|
24
|
+
sender: string;
|
|
25
|
+
/** Global leaf index in the L1→L2 message tree. */
|
|
26
|
+
globalLeafIndex: string;
|
|
27
|
+
/** Timestamp when the message was seeded. */
|
|
28
|
+
timestamp: number;
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
/**
|
|
32
|
+
* Simple data store for the bot to persist L1 bridge claims.
|
|
33
|
+
*/
|
|
34
|
+
export class BotStore {
|
|
35
|
+
public static readonly SCHEMA_VERSION = 1;
|
|
36
|
+
private readonly bridgeClaims: AztecAsyncMap<string, string>;
|
|
37
|
+
private readonly pendingL1ToL2: AztecAsyncMap<string, string>;
|
|
38
|
+
|
|
39
|
+
constructor(
|
|
40
|
+
private readonly store: AztecAsyncKVStore,
|
|
41
|
+
private readonly log: Logger = createLogger('bot:store'),
|
|
42
|
+
private readonly dateProvider: DateProvider = new DateProvider(),
|
|
43
|
+
) {
|
|
44
|
+
this.bridgeClaims = store.openMap<string, string>('bridge_claims');
|
|
45
|
+
this.pendingL1ToL2 = store.openMap<string, string>('pending_l1_to_l2');
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Saves a bridge claim for a recipient.
|
|
50
|
+
*/
|
|
51
|
+
public async saveBridgeClaim(recipient: AztecAddress, claim: L2AmountClaim): Promise<void> {
|
|
52
|
+
// Convert Fr fields and BigInts to strings for JSON serialization
|
|
53
|
+
const serializableClaim = {
|
|
54
|
+
claimAmount: claim.claimAmount.toString(),
|
|
55
|
+
claimSecret: claim.claimSecret.toString(),
|
|
56
|
+
claimSecretHash: claim.claimSecretHash.toString(),
|
|
57
|
+
messageHash: claim.messageHash,
|
|
58
|
+
messageLeafIndex: claim.messageLeafIndex.toString(),
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
const data = {
|
|
62
|
+
claim: serializableClaim,
|
|
63
|
+
timestamp: this.dateProvider.now(),
|
|
64
|
+
recipient: recipient.toString(),
|
|
65
|
+
};
|
|
66
|
+
|
|
67
|
+
await this.bridgeClaims.set(recipient.toString(), JSON.stringify(data));
|
|
68
|
+
this.log.info(`Saved bridge claim for ${recipient.toString()}`);
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
/**
|
|
72
|
+
* Gets a bridge claim for a recipient if it exists.
|
|
73
|
+
*/
|
|
74
|
+
public async getBridgeClaim(recipient: AztecAddress): Promise<BridgeClaimData | undefined> {
|
|
75
|
+
const data = await this.bridgeClaims.getAsync(recipient.toString());
|
|
76
|
+
if (!data) {
|
|
77
|
+
return undefined;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
const parsed = JSON.parse(data);
|
|
81
|
+
|
|
82
|
+
// Reconstruct L2AmountClaim from serialized data
|
|
83
|
+
const claim: L2AmountClaim = {
|
|
84
|
+
claimAmount: BigInt(parsed.claim.claimAmount),
|
|
85
|
+
claimSecret: Fr.fromString(parsed.claim.claimSecret),
|
|
86
|
+
claimSecretHash: Fr.fromString(parsed.claim.claimSecretHash),
|
|
87
|
+
messageHash: parsed.claim.messageHash,
|
|
88
|
+
messageLeafIndex: BigInt(parsed.claim.messageLeafIndex),
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
return {
|
|
92
|
+
claim,
|
|
93
|
+
timestamp: parsed.timestamp,
|
|
94
|
+
recipient: parsed.recipient,
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
/**
|
|
99
|
+
* Deletes a bridge claim for a recipient.
|
|
100
|
+
*/
|
|
101
|
+
public async deleteBridgeClaim(recipient: AztecAddress): Promise<void> {
|
|
102
|
+
await this.bridgeClaims.delete(recipient.toString());
|
|
103
|
+
this.log.info(`Deleted bridge claim for ${recipient.toString()}`);
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
/**
|
|
107
|
+
* Gets all stored bridge claims.
|
|
108
|
+
*/
|
|
109
|
+
public async getAllBridgeClaims(): Promise<BridgeClaimData[]> {
|
|
110
|
+
const claims: BridgeClaimData[] = [];
|
|
111
|
+
const entries = this.bridgeClaims.entriesAsync();
|
|
112
|
+
|
|
113
|
+
for await (const [_, data] of entries) {
|
|
114
|
+
const parsed = JSON.parse(data);
|
|
115
|
+
|
|
116
|
+
// Reconstruct L2AmountClaim from serialized data
|
|
117
|
+
const claim: L2AmountClaim = {
|
|
118
|
+
claimAmount: BigInt(parsed.claim.claimAmount),
|
|
119
|
+
claimSecret: Fr.fromString(parsed.claim.claimSecret),
|
|
120
|
+
claimSecretHash: Fr.fromString(parsed.claim.claimSecretHash),
|
|
121
|
+
messageHash: parsed.claim.messageHash,
|
|
122
|
+
messageLeafIndex: BigInt(parsed.claim.messageLeafIndex),
|
|
123
|
+
};
|
|
124
|
+
|
|
125
|
+
claims.push({
|
|
126
|
+
claim,
|
|
127
|
+
timestamp: parsed.timestamp,
|
|
128
|
+
recipient: parsed.recipient,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
return claims;
|
|
133
|
+
}
|
|
134
|
+
|
|
135
|
+
/**
|
|
136
|
+
* Cleans up old bridge claims (older than 24 hours).
|
|
137
|
+
*/
|
|
138
|
+
public async cleanupOldClaims(maxAgeMs: number = 24 * 60 * 60 * 1000): Promise<number> {
|
|
139
|
+
const now = this.dateProvider.now();
|
|
140
|
+
let cleanedCount = 0;
|
|
141
|
+
const entries = this.bridgeClaims.entriesAsync();
|
|
142
|
+
|
|
143
|
+
for await (const [key, data] of entries) {
|
|
144
|
+
const parsed = JSON.parse(data);
|
|
145
|
+
if (now - parsed.timestamp > maxAgeMs) {
|
|
146
|
+
await this.bridgeClaims.delete(key);
|
|
147
|
+
cleanedCount++;
|
|
148
|
+
this.log.info(`Cleaned up old bridge claim for ${parsed.recipient}`);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
return cleanedCount;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
/** Saves a pending L1→L2 message keyed by msgHash. */
|
|
156
|
+
public async savePendingL1ToL2Message(msg: PendingL1ToL2Message): Promise<void> {
|
|
157
|
+
await this.pendingL1ToL2.set(msg.msgHash, JSON.stringify(msg));
|
|
158
|
+
this.log.info(`Saved pending L1→L2 message ${msg.msgHash}`);
|
|
159
|
+
}
|
|
160
|
+
|
|
161
|
+
/** Returns all unconsumed pending L1→L2 messages. */
|
|
162
|
+
public async getUnconsumedL1ToL2Messages(): Promise<PendingL1ToL2Message[]> {
|
|
163
|
+
const messages: PendingL1ToL2Message[] = [];
|
|
164
|
+
for await (const [_, data] of this.pendingL1ToL2.entriesAsync()) {
|
|
165
|
+
messages.push(JSON.parse(data));
|
|
166
|
+
}
|
|
167
|
+
return messages;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
/** Deletes a consumed L1→L2 message from the store. */
|
|
171
|
+
public async deleteL1ToL2Message(msgHash: string): Promise<void> {
|
|
172
|
+
await this.pendingL1ToL2.delete(msgHash);
|
|
173
|
+
this.log.info(`Deleted consumed L1→L2 message ${msgHash}`);
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
/** Cleans up pending L1→L2 messages older than maxAgeMs. */
|
|
177
|
+
public async cleanupOldPendingMessages(maxAgeMs: number = 24 * 60 * 60 * 1000): Promise<number> {
|
|
178
|
+
const now = this.dateProvider.now();
|
|
179
|
+
let cleanedCount = 0;
|
|
180
|
+
for await (const [key, data] of this.pendingL1ToL2.entriesAsync()) {
|
|
181
|
+
const parsed = JSON.parse(data);
|
|
182
|
+
if (now - parsed.timestamp > maxAgeMs) {
|
|
183
|
+
await this.pendingL1ToL2.delete(key);
|
|
184
|
+
cleanedCount++;
|
|
185
|
+
this.log.info(`Cleaned up old pending L1→L2 message ${key}`);
|
|
186
|
+
}
|
|
187
|
+
}
|
|
188
|
+
return cleanedCount;
|
|
189
|
+
}
|
|
190
|
+
|
|
191
|
+
/** Closes the store. */
|
|
192
|
+
public async close(): Promise<void> {
|
|
193
|
+
await this.store.close();
|
|
194
|
+
this.log.info('Closed bot data store');
|
|
195
|
+
}
|
|
196
|
+
}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { BotStore, type BridgeClaimData, type PendingL1ToL2Message } from './bot_store.js';
|
package/src/utils.ts
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
import
|
|
1
|
+
import { ContractBase } from '@aztec/aztec.js/contracts';
|
|
2
|
+
import type { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
3
|
+
import type { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
2
4
|
import type { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
3
5
|
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
4
6
|
|
|
@@ -11,17 +13,26 @@ import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
|
11
13
|
export async function getBalances(
|
|
12
14
|
token: TokenContract,
|
|
13
15
|
who: AztecAddress,
|
|
16
|
+
from?: AztecAddress,
|
|
14
17
|
): Promise<{ privateBalance: bigint; publicBalance: bigint }> {
|
|
15
|
-
const privateBalance = await token.methods.balance_of_private(who).simulate();
|
|
16
|
-
const publicBalance = await token.methods.balance_of_public(who).simulate();
|
|
18
|
+
const { result: privateBalance } = await token.methods.balance_of_private(who).simulate({ from: from ?? who });
|
|
19
|
+
const { result: publicBalance } = await token.methods.balance_of_public(who).simulate({ from: from ?? who });
|
|
17
20
|
return { privateBalance, publicBalance };
|
|
18
21
|
}
|
|
19
22
|
|
|
20
|
-
export async function getPrivateBalance(
|
|
21
|
-
|
|
23
|
+
export async function getPrivateBalance(
|
|
24
|
+
token: PrivateTokenContract,
|
|
25
|
+
who: AztecAddress,
|
|
26
|
+
from?: AztecAddress,
|
|
27
|
+
): Promise<bigint> {
|
|
28
|
+
const { result: privateBalance } = await token.methods.get_balance(who).simulate({ from: from ?? who });
|
|
22
29
|
return privateBalance;
|
|
23
30
|
}
|
|
24
31
|
|
|
25
|
-
export function isStandardTokenContract(token:
|
|
32
|
+
export function isStandardTokenContract(token: ContractBase): token is TokenContract {
|
|
26
33
|
return 'mint_to_public' in token.methods;
|
|
27
34
|
}
|
|
35
|
+
|
|
36
|
+
export function isAMMContract(contract: ContractBase): contract is AMMContract {
|
|
37
|
+
return 'add_liquidity' in contract.methods;
|
|
38
|
+
}
|