@aztec/bot 0.0.1-commit.d6f2b3f94 → 0.0.1-commit.db765a8
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 +4 -4
- package/dest/amm_bot.d.ts.map +1 -1
- package/dest/amm_bot.js +23 -16
- package/dest/base_bot.d.ts +4 -4
- package/dest/base_bot.d.ts.map +1 -1
- package/dest/base_bot.js +9 -10
- package/dest/bot.d.ts +4 -4
- package/dest/bot.d.ts.map +1 -1
- package/dest/bot.js +4 -3
- package/dest/config.d.ts +30 -14
- package/dest/config.d.ts.map +1 -1
- package/dest/config.js +36 -8
- 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 +141 -0
- package/dest/factory.d.ts +20 -5
- package/dest/factory.d.ts.map +1 -1
- package/dest/factory.js +93 -24
- package/dest/index.d.ts +2 -1
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +1 -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/runner.d.ts +3 -3
- package/dest/runner.d.ts.map +1 -1
- package/dest/runner.js +17 -1
- package/dest/store/bot_store.d.ts +30 -5
- package/dest/store/bot_store.d.ts.map +1 -1
- package/dest/store/bot_store.js +37 -6
- package/dest/store/index.d.ts +2 -2
- package/dest/store/index.d.ts.map +1 -1
- package/dest/utils.js +3 -3
- package/package.json +16 -13
- package/src/amm_bot.ts +23 -18
- package/src/base_bot.ts +8 -18
- package/src/bot.ts +7 -6
- package/src/config.ts +39 -10
- package/src/cross_chain_bot.ts +210 -0
- package/src/factory.ts +137 -30
- package/src/index.ts +1 -0
- package/src/l1_to_l2_seeding.ts +79 -0
- package/src/runner.ts +18 -5
- package/src/store/bot_store.ts +60 -5
- package/src/store/index.ts +1 -1
- package/src/utils.ts +3 -3
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* CrossChainBot exercises L2->L1 and L1->L2 messaging.
|
|
3
|
+
*
|
|
4
|
+
* createAndSendTx onTxMined
|
|
5
|
+
* ────────────────────────────────────── ──────────────────────────────
|
|
6
|
+
*
|
|
7
|
+
* 1. SEED (fire-and-forget) 3. VERIFY L2->L1
|
|
8
|
+
* if store has fewer pending messages Query getTxEffect, confirm
|
|
9
|
+
* than seedCount and no seed is the expected L2->L1 messages
|
|
10
|
+
* in-flight: appeared in tx effects.
|
|
11
|
+
* * kick off L1 inbox tx
|
|
12
|
+
* * store msg on completion
|
|
13
|
+
*
|
|
14
|
+
* 2. BUILD & SEND BATCH
|
|
15
|
+
* Always:
|
|
16
|
+
* N x create_l2_to_l1_message
|
|
17
|
+
* (random content, fixed
|
|
18
|
+
* L1 recipient)
|
|
19
|
+
* If a ready L1->L2 msg exists:
|
|
20
|
+
* 1 x consume_message_from_
|
|
21
|
+
* arbitrary_sender_public
|
|
22
|
+
* delete consumed msg from store
|
|
23
|
+
* Send batch tx (no wait)
|
|
24
|
+
*
|
|
25
|
+
*/ import { BatchCall, NO_WAIT } from '@aztec/aztec.js/contracts';
|
|
26
|
+
import { isL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
27
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
28
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
29
|
+
import { BaseBot } from './base_bot.js';
|
|
30
|
+
import { BotFactory } from './factory.js';
|
|
31
|
+
import { seedL1ToL2Message } from './l1_to_l2_seeding.js';
|
|
32
|
+
/** Stale message threshold: messages older than this are removed. */ const STALE_MESSAGE_THRESHOLD_MS = 2 * 60 * 60 * 1000; // 2 hours
|
|
33
|
+
/** Bot that exercises both L2→L1 and L1→L2 cross-chain messaging. */ export class CrossChainBot extends BaseBot {
|
|
34
|
+
contract;
|
|
35
|
+
l1Client;
|
|
36
|
+
l1Recipient;
|
|
37
|
+
inboxAddress;
|
|
38
|
+
rollupVersion;
|
|
39
|
+
store;
|
|
40
|
+
l2ToL1Sent;
|
|
41
|
+
l1ToL2Consumed;
|
|
42
|
+
pendingSeedPromise;
|
|
43
|
+
constructor(node, wallet, defaultAccountAddress, contract, l1Client, l1Recipient, inboxAddress, rollupVersion, store, config){
|
|
44
|
+
super(node, wallet, defaultAccountAddress, config), this.contract = contract, this.l1Client = l1Client, this.l1Recipient = l1Recipient, this.inboxAddress = inboxAddress, this.rollupVersion = rollupVersion, this.store = store, this.l2ToL1Sent = 0, this.l1ToL2Consumed = 0;
|
|
45
|
+
}
|
|
46
|
+
static async create(config, wallet, aztecNode, aztecNodeAdmin, store) {
|
|
47
|
+
if (config.followChain === 'NONE') {
|
|
48
|
+
throw new Error(`CrossChainBot requires followChain to be set (got NONE)`);
|
|
49
|
+
}
|
|
50
|
+
const factory = new BotFactory(config, wallet, store, aztecNode, aztecNodeAdmin);
|
|
51
|
+
const { defaultAccountAddress, contract, l1Client, rollupVersion } = await factory.setupCrossChain();
|
|
52
|
+
const l1Recipient = EthAddress.fromString(l1Client.account.address);
|
|
53
|
+
const { l1ContractAddresses } = await aztecNode.getNodeInfo();
|
|
54
|
+
const inboxAddress = EthAddress.fromString(l1ContractAddresses.inboxAddress.toString());
|
|
55
|
+
return new CrossChainBot(aztecNode, wallet, defaultAccountAddress, contract, l1Client, l1Recipient, inboxAddress, rollupVersion, store, config);
|
|
56
|
+
}
|
|
57
|
+
async createAndSendTx(logCtx) {
|
|
58
|
+
const pendingMessages = await this.store.getUnconsumedL1ToL2Messages();
|
|
59
|
+
// Send an L1→L2 message if we're below the threshold and not already seeding one
|
|
60
|
+
if (pendingMessages.length < this.config.l1ToL2SeedCount && !this.pendingSeedPromise) {
|
|
61
|
+
this.pendingSeedPromise = this.seedNewL1ToL2Message().catch((err)=>this.log.warn(`Failed to seed L1→L2 message: ${err}`, logCtx)).finally(()=>{
|
|
62
|
+
this.pendingSeedPromise = undefined;
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
// Build batch: always L2→L1, optionally consume L1→L2
|
|
66
|
+
const calls = [];
|
|
67
|
+
// L2→L1: create messages with random content
|
|
68
|
+
for(let i = 0; i < this.config.l2ToL1MessagesPerTx; i++){
|
|
69
|
+
calls.push(this.contract.methods.create_l2_to_l1_message_arbitrary_recipient_public(Fr.random(), this.l1Recipient));
|
|
70
|
+
}
|
|
71
|
+
// L1→L2: consume oldest ready message if available
|
|
72
|
+
const readyMsg = await this.getReadyL1ToL2Message(pendingMessages);
|
|
73
|
+
if (readyMsg) {
|
|
74
|
+
calls.push(this.contract.methods.consume_message_from_arbitrary_sender_public(Fr.fromHexString(readyMsg.content), Fr.fromHexString(readyMsg.secret), EthAddress.fromString(readyMsg.sender), new Fr(BigInt(readyMsg.globalLeafIndex))));
|
|
75
|
+
// Delete consumed message immediately so it works with FOLLOW_CHAIN=NONE
|
|
76
|
+
await this.store.deleteL1ToL2Message(readyMsg.msgHash);
|
|
77
|
+
this.l1ToL2Consumed++;
|
|
78
|
+
} else {
|
|
79
|
+
this.log.warn(`No ready L1→L2 message to consume`, {
|
|
80
|
+
...logCtx,
|
|
81
|
+
pendingCount: pendingMessages.length
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
const batch = new BatchCall(this.wallet, calls);
|
|
85
|
+
const opts = await this.getSendMethodOpts(batch);
|
|
86
|
+
this.log.verbose(`Sending cross-chain batch with ${calls.length} calls`, logCtx);
|
|
87
|
+
const { txHash } = await batch.send({
|
|
88
|
+
...opts,
|
|
89
|
+
wait: NO_WAIT
|
|
90
|
+
});
|
|
91
|
+
return txHash;
|
|
92
|
+
}
|
|
93
|
+
async onTxMined(receipt, logCtx) {
|
|
94
|
+
// Verify L2→L1 messages appeared in this tx's effects
|
|
95
|
+
const indexed = await this.node.getTxEffect(receipt.txHash);
|
|
96
|
+
if (indexed) {
|
|
97
|
+
const l2ToL1Msgs = indexed.data.l2ToL1Msgs.filter((m)=>!m.isZero());
|
|
98
|
+
if (l2ToL1Msgs.length >= this.config.l2ToL1MessagesPerTx) {
|
|
99
|
+
this.l2ToL1Sent += l2ToL1Msgs.length;
|
|
100
|
+
} else {
|
|
101
|
+
this.log.error(`Expected ${this.config.l2ToL1MessagesPerTx} L2→L1 messages but found ${l2ToL1Msgs.length}`, {
|
|
102
|
+
...logCtx,
|
|
103
|
+
blockNumber: receipt.blockNumber,
|
|
104
|
+
txHash: receipt.txHash.toString()
|
|
105
|
+
});
|
|
106
|
+
}
|
|
107
|
+
}
|
|
108
|
+
const pendingCount = (await this.store.getUnconsumedL1ToL2Messages()).length;
|
|
109
|
+
this.log.info(`CrossChainBot txs mined`, {
|
|
110
|
+
...logCtx,
|
|
111
|
+
l2ToL1Sent: this.l2ToL1Sent,
|
|
112
|
+
l1ToL2Consumed: this.l1ToL2Consumed,
|
|
113
|
+
l1ToL2Pending: pendingCount
|
|
114
|
+
});
|
|
115
|
+
}
|
|
116
|
+
/** Finds the oldest pending message that is ready for consumption. */ async getReadyL1ToL2Message(pendingMessages) {
|
|
117
|
+
const now = Date.now();
|
|
118
|
+
for (const msg of pendingMessages){
|
|
119
|
+
const ready = await isL1ToL2MessageReady(this.node, Fr.fromHexString(msg.msgHash), {
|
|
120
|
+
// Use forPublicConsumption: false so we wait until blockNumber >= messageBlockNumber.
|
|
121
|
+
// With forPublicConsumption: true, the check returns true one block early (the sequencer
|
|
122
|
+
// includes L1→L2 messages before executing the block's txs), but gas estimation simulates
|
|
123
|
+
// against the current world state which doesn't yet have the message.
|
|
124
|
+
// See https://linear.app/aztec-labs/issue/A-548 for details.
|
|
125
|
+
forPublicConsumption: false
|
|
126
|
+
});
|
|
127
|
+
if (ready) {
|
|
128
|
+
return msg;
|
|
129
|
+
}
|
|
130
|
+
// Time-based stale detection: if the message is old and still not ready, remove it
|
|
131
|
+
if (now - msg.timestamp > STALE_MESSAGE_THRESHOLD_MS) {
|
|
132
|
+
await this.store.deleteL1ToL2Message(msg.msgHash);
|
|
133
|
+
this.log.warn(`Removed stale L1→L2 message ${msg.msgHash}`);
|
|
134
|
+
}
|
|
135
|
+
}
|
|
136
|
+
return undefined;
|
|
137
|
+
}
|
|
138
|
+
/** Seeds a new L1→L2 message on L1 and stores it. */ async seedNewL1ToL2Message() {
|
|
139
|
+
await seedL1ToL2Message(this.l1Client, this.inboxAddress, this.contract.address, this.rollupVersion, this.store, this.log);
|
|
140
|
+
}
|
|
141
|
+
}
|
package/dest/factory.d.ts
CHANGED
|
@@ -1,9 +1,11 @@
|
|
|
1
1
|
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
2
|
+
import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
|
|
2
3
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
3
4
|
import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
4
5
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
6
|
+
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
|
|
5
7
|
import type { AztecNode, AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
6
|
-
import {
|
|
8
|
+
import { EmbeddedWallet } from '@aztec/wallets/embedded';
|
|
7
9
|
import { type BotConfig } from './config.js';
|
|
8
10
|
import type { BotStore } from './store/index.js';
|
|
9
11
|
export declare class BotFactory {
|
|
@@ -13,26 +15,39 @@ export declare class BotFactory {
|
|
|
13
15
|
private readonly aztecNode;
|
|
14
16
|
private readonly aztecNodeAdmin?;
|
|
15
17
|
private log;
|
|
16
|
-
constructor(config: BotConfig, wallet:
|
|
18
|
+
constructor(config: BotConfig, wallet: EmbeddedWallet, store: BotStore, aztecNode: AztecNode, aztecNodeAdmin?: AztecNodeAdmin | undefined);
|
|
17
19
|
/**
|
|
18
20
|
* Initializes a new bot by setting up the sender account, registering the recipient,
|
|
19
21
|
* deploying the token contract, and minting tokens if necessary.
|
|
20
22
|
*/
|
|
21
23
|
setup(): Promise<{
|
|
22
|
-
wallet:
|
|
24
|
+
wallet: EmbeddedWallet;
|
|
23
25
|
defaultAccountAddress: AztecAddress;
|
|
24
26
|
token: TokenContract | PrivateTokenContract;
|
|
25
27
|
node: AztecNode;
|
|
26
28
|
recipient: AztecAddress;
|
|
27
29
|
}>;
|
|
28
30
|
setupAmm(): Promise<{
|
|
29
|
-
wallet:
|
|
31
|
+
wallet: EmbeddedWallet;
|
|
30
32
|
defaultAccountAddress: AztecAddress;
|
|
31
33
|
amm: AMMContract;
|
|
32
34
|
token0: TokenContract;
|
|
33
35
|
token1: TokenContract;
|
|
34
36
|
node: AztecNode;
|
|
35
37
|
}>;
|
|
38
|
+
/**
|
|
39
|
+
* Initializes the cross-chain bot by deploying TestContract, creating an L1 client,
|
|
40
|
+
* seeding initial L1→L2 messages, and waiting for the first to be ready.
|
|
41
|
+
*/
|
|
42
|
+
setupCrossChain(): Promise<{
|
|
43
|
+
wallet: EmbeddedWallet;
|
|
44
|
+
defaultAccountAddress: AztecAddress;
|
|
45
|
+
contract: TestContract;
|
|
46
|
+
node: AztecNode;
|
|
47
|
+
l1Client: ExtendedViemWalletClient;
|
|
48
|
+
rollupVersion: bigint;
|
|
49
|
+
}>;
|
|
50
|
+
private setupTestContract;
|
|
36
51
|
private setupAccount;
|
|
37
52
|
private setupAccountWithPrivateKey;
|
|
38
53
|
private setupTestAccount;
|
|
@@ -46,4 +61,4 @@ export declare class BotFactory {
|
|
|
46
61
|
private bridgeL1FeeJuice;
|
|
47
62
|
private withNoMinTxsPerBlock;
|
|
48
63
|
}
|
|
49
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
64
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiZmFjdG9yeS5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2ZhY3RvcnkudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLDJCQUEyQixDQUFDO0FBbUJ6RCxPQUFPLEtBQUssRUFBRSx3QkFBd0IsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBSXRFLE9BQU8sRUFBRSxXQUFXLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUMzRCxPQUFPLEVBQUUsb0JBQW9CLEVBQUUsTUFBTSx1Q0FBdUMsQ0FBQztBQUM3RSxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sZ0NBQWdDLENBQUM7QUFDL0QsT0FBTyxFQUFFLFlBQVksRUFBRSxNQUFNLG9DQUFvQyxDQUFDO0FBR2xFLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxjQUFjLEVBQUUsTUFBTSxpQ0FBaUMsQ0FBQztBQUVqRixPQUFPLEVBQUUsY0FBYyxFQUFFLE1BQU0seUJBQXlCLENBQUM7QUFFekQsT0FBTyxFQUFFLEtBQUssU0FBUyxFQUEyQixNQUFNLGFBQWEsQ0FBQztBQUV0RSxPQUFPLEtBQUssRUFBRSxRQUFRLEVBQUUsTUFBTSxrQkFBa0IsQ0FBQztBQU1qRCxxQkFBYSxVQUFVO0lBSW5CLE9BQU8sQ0FBQyxRQUFRLENBQUMsTUFBTTtJQUN2QixPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFDdkIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBQ3RCLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztJQUMxQixPQUFPLENBQUMsUUFBUSxDQUFDLGNBQWMsQ0FBQztJQVBsQyxPQUFPLENBQUMsR0FBRyxDQUF1QjtJQUVsQyxZQUNtQixNQUFNLEVBQUUsU0FBUyxFQUNqQixNQUFNLEVBQUUsY0FBYyxFQUN0QixLQUFLLEVBQUUsUUFBUSxFQUNmLFNBQVMsRUFBRSxTQUFTLEVBQ3BCLGNBQWMsQ0FBQyw0QkFBZ0IsRUFLakQ7SUFFRDs7O09BR0c7SUFDVSxLQUFLLElBQUksT0FBTyxDQUFDO1FBQzVCLE1BQU0sRUFBRSxjQUFjLENBQUM7UUFDdkIscUJBQXFCLEVBQUUsWUFBWSxDQUFDO1FBQ3BDLEtBQUssRUFBRSxhQUFhLEdBQUcsb0JBQW9CLENBQUM7UUFDNUMsSUFBSSxFQUFFLFNBQVMsQ0FBQztRQUNoQixTQUFTLEVBQUUsWUFBWSxDQUFDO0tBQ3pCLENBQUMsQ0FNRDtJQUVZLFFBQVEsSUFBSSxPQUFPLENBQUM7UUFDL0IsTUFBTSxFQUFFLGNBQWMsQ0FBQztRQUN2QixxQkFBcUIsRUFBRSxZQUFZLENBQUM7UUFDcEMsR0FBRyxFQUFFLFdBQVcsQ0FBQztRQUNqQixNQUFNLEVBQUUsYUFBYSxDQUFDO1FBQ3RCLE1BQU0sRUFBRSxhQUFhLENBQUM7UUFDdEIsSUFBSSxFQUFFLFNBQVMsQ0FBQztLQUNqQixDQUFDLENBc0JEO0lBRUQ7OztPQUdHO0lBQ1UsZUFBZSxJQUFJLE9BQU8sQ0FBQztRQUN0QyxNQUFNLEVBQUUsY0FBYyxDQUFDO1FBQ3ZCLHFCQUFxQixFQUFFLFlBQVksQ0FBQztRQUNwQyxRQUFRLEVBQUUsWUFBWSxDQUFDO1FBQ3ZCLElBQUksRUFBRSxTQUFTLENBQUM7UUFDaEIsUUFBUSxFQUFFLHdCQUF3QixDQUFDO1FBQ25DLGFBQWEsRUFBRSxNQUFNLENBQUM7S0FDdkIsQ0FBQyxDQWdFRDtZQUVhLGlCQUFpQjtZQWVqQixZQUFZO1lBV1osMEJBQTBCO1lBeUMxQixnQkFBZ0I7WUFlaEIsVUFBVTtZQXNEVixrQkFBa0I7WUFhbEIsZ0JBQWdCO1lBc0JoQixPQUFPO1lBc0ZQLHdCQUF3QjtZQTBCeEIsVUFBVTtZQWdEVixzQkFBc0I7WUE0QnRCLGdCQUFnQjtZQWdDaEIsb0JBQW9CO0NBZW5DIn0=
|
package/dest/factory.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"
|
|
1
|
+
{"version":3,"file":"factory.d.ts","sourceRoot":"","sources":["../src/factory.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,2BAA2B,CAAC;AAmBzD,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAItE,OAAO,EAAE,WAAW,EAAE,MAAM,8BAA8B,CAAC;AAC3D,OAAO,EAAE,oBAAoB,EAAE,MAAM,uCAAuC,CAAC;AAC7E,OAAO,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/D,OAAO,EAAE,YAAY,EAAE,MAAM,oCAAoC,CAAC;AAGlE,OAAO,KAAK,EAAE,SAAS,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AAEjF,OAAO,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAEzD,OAAO,EAAE,KAAK,SAAS,EAA2B,MAAM,aAAa,CAAC;AAEtE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAMjD,qBAAa,UAAU;IAInB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,KAAK;IACtB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAC;IAPlC,OAAO,CAAC,GAAG,CAAuB;IAElC,YACmB,MAAM,EAAE,SAAS,EACjB,MAAM,EAAE,cAAc,EACtB,KAAK,EAAE,QAAQ,EACf,SAAS,EAAE,SAAS,EACpB,cAAc,CAAC,4BAAgB,EAKjD;IAED;;;OAGG;IACU,KAAK,IAAI,OAAO,CAAC;QAC5B,MAAM,EAAE,cAAc,CAAC;QACvB,qBAAqB,EAAE,YAAY,CAAC;QACpC,KAAK,EAAE,aAAa,GAAG,oBAAoB,CAAC;QAC5C,IAAI,EAAE,SAAS,CAAC;QAChB,SAAS,EAAE,YAAY,CAAC;KACzB,CAAC,CAMD;IAEY,QAAQ,IAAI,OAAO,CAAC;QAC/B,MAAM,EAAE,cAAc,CAAC;QACvB,qBAAqB,EAAE,YAAY,CAAC;QACpC,GAAG,EAAE,WAAW,CAAC;QACjB,MAAM,EAAE,aAAa,CAAC;QACtB,MAAM,EAAE,aAAa,CAAC;QACtB,IAAI,EAAE,SAAS,CAAC;KACjB,CAAC,CAsBD;IAED;;;OAGG;IACU,eAAe,IAAI,OAAO,CAAC;QACtC,MAAM,EAAE,cAAc,CAAC;QACvB,qBAAqB,EAAE,YAAY,CAAC;QACpC,QAAQ,EAAE,YAAY,CAAC;QACvB,IAAI,EAAE,SAAS,CAAC;QAChB,QAAQ,EAAE,wBAAwB,CAAC;QACnC,aAAa,EAAE,MAAM,CAAC;KACvB,CAAC,CAgED;YAEa,iBAAiB;YAejB,YAAY;YAWZ,0BAA0B;YAyC1B,gBAAgB;YAehB,UAAU;YAsDV,kBAAkB;YAalB,gBAAgB;YAsBhB,OAAO;YAsFP,wBAAwB;YA0BxB,UAAU;YAgDV,sBAAsB;YA4BtB,gBAAgB;YAgChB,oBAAoB;CAenC"}
|
package/dest/factory.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import { SchnorrAccountContract } from '@aztec/accounts/schnorr';
|
|
2
1
|
import { getInitialTestAccountsData } from '@aztec/accounts/testing';
|
|
3
2
|
import { AztecAddress } from '@aztec/aztec.js/addresses';
|
|
4
3
|
import { BatchCall, NO_WAIT } from '@aztec/aztec.js/contracts';
|
|
@@ -10,14 +9,18 @@ import { waitForL1ToL2MessageReady } from '@aztec/aztec.js/messaging';
|
|
|
10
9
|
import { waitForTx } from '@aztec/aztec.js/node';
|
|
11
10
|
import { createEthereumChain } from '@aztec/ethereum/chain';
|
|
12
11
|
import { createExtendedL1Client } from '@aztec/ethereum/client';
|
|
12
|
+
import { RollupContract } from '@aztec/ethereum/contracts';
|
|
13
13
|
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
14
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
14
15
|
import { Timer } from '@aztec/foundation/timer';
|
|
15
16
|
import { AMMContract } from '@aztec/noir-contracts.js/AMM';
|
|
16
17
|
import { PrivateTokenContract } from '@aztec/noir-contracts.js/PrivateToken';
|
|
17
18
|
import { TokenContract } from '@aztec/noir-contracts.js/Token';
|
|
19
|
+
import { TestContract } from '@aztec/noir-test-contracts.js/Test';
|
|
18
20
|
import { GasSettings } from '@aztec/stdlib/gas';
|
|
19
21
|
import { deriveSigningKey } from '@aztec/stdlib/keys';
|
|
20
22
|
import { SupportedTokenContracts } from './config.js';
|
|
23
|
+
import { seedL1ToL2Message } from './l1_to_l2_seeding.js';
|
|
21
24
|
import { getBalances, getPrivateBalance, isStandardTokenContract } from './utils.js';
|
|
22
25
|
const MINT_BALANCE = 1e12;
|
|
23
26
|
const MIN_BALANCE = 1e3;
|
|
@@ -35,13 +38,16 @@ export class BotFactory {
|
|
|
35
38
|
this.aztecNode = aztecNode;
|
|
36
39
|
this.aztecNodeAdmin = aztecNodeAdmin;
|
|
37
40
|
this.log = createLogger('bot');
|
|
41
|
+
// Set fee padding on the wallet so that all transactions during setup
|
|
42
|
+
// (token deploy, minting, etc.) use the configured padding, not the default.
|
|
43
|
+
this.wallet.setMinFeePadding(config.minFeePadding);
|
|
38
44
|
}
|
|
39
45
|
/**
|
|
40
46
|
* Initializes a new bot by setting up the sender account, registering the recipient,
|
|
41
47
|
* deploying the token contract, and minting tokens if necessary.
|
|
42
48
|
*/ async setup() {
|
|
43
49
|
const defaultAccountAddress = await this.setupAccount();
|
|
44
|
-
const recipient = (await this.wallet.
|
|
50
|
+
const recipient = (await this.wallet.createSchnorrAccount(Fr.random(), Fr.random())).address;
|
|
45
51
|
const token = await this.setupToken(defaultAccountAddress);
|
|
46
52
|
await this.mintTokens(token, defaultAccountAddress);
|
|
47
53
|
return {
|
|
@@ -70,6 +76,70 @@ export class BotFactory {
|
|
|
70
76
|
};
|
|
71
77
|
}
|
|
72
78
|
/**
|
|
79
|
+
* Initializes the cross-chain bot by deploying TestContract, creating an L1 client,
|
|
80
|
+
* seeding initial L1→L2 messages, and waiting for the first to be ready.
|
|
81
|
+
*/ async setupCrossChain() {
|
|
82
|
+
const defaultAccountAddress = await this.setupAccount();
|
|
83
|
+
// Create L1 client (same pattern as bridgeL1FeeJuice)
|
|
84
|
+
const l1RpcUrls = this.config.l1RpcUrls;
|
|
85
|
+
if (!l1RpcUrls?.length) {
|
|
86
|
+
throw new Error('L1 RPC URLs required for cross-chain bot');
|
|
87
|
+
}
|
|
88
|
+
const mnemonicOrPrivateKey = this.config.l1PrivateKey?.getValue() ?? this.config.l1Mnemonic?.getValue();
|
|
89
|
+
if (!mnemonicOrPrivateKey) {
|
|
90
|
+
throw new Error('L1 mnemonic or private key required for cross-chain bot');
|
|
91
|
+
}
|
|
92
|
+
const { l1ChainId, l1ContractAddresses } = await this.aztecNode.getNodeInfo();
|
|
93
|
+
const chain = createEthereumChain(l1RpcUrls, l1ChainId);
|
|
94
|
+
const l1Client = createExtendedL1Client(chain.rpcUrls, mnemonicOrPrivateKey, chain.chainInfo);
|
|
95
|
+
// Fetch Rollup version (needed for Inbox L2Actor struct)
|
|
96
|
+
const rollupContract = new RollupContract(l1Client, l1ContractAddresses.rollupAddress.toString());
|
|
97
|
+
const rollupVersion = await rollupContract.getVersion();
|
|
98
|
+
// Deploy TestContract
|
|
99
|
+
const contract = await this.setupTestContract(defaultAccountAddress);
|
|
100
|
+
// Recover any pending messages from store (clean up stale ones first)
|
|
101
|
+
await this.store.cleanupOldPendingMessages();
|
|
102
|
+
const pendingMessages = await this.store.getUnconsumedL1ToL2Messages();
|
|
103
|
+
// Seed initial L1→L2 messages if pipeline is empty
|
|
104
|
+
const seedCount = Math.max(0, this.config.l1ToL2SeedCount - pendingMessages.length);
|
|
105
|
+
for(let i = 0; i < seedCount; i++){
|
|
106
|
+
await seedL1ToL2Message(l1Client, EthAddress.fromString(l1ContractAddresses.inboxAddress.toString()), contract.address, rollupVersion, this.store, this.log);
|
|
107
|
+
}
|
|
108
|
+
// Block until at least one message is ready
|
|
109
|
+
const allMessages = await this.store.getUnconsumedL1ToL2Messages();
|
|
110
|
+
if (allMessages.length > 0) {
|
|
111
|
+
this.log.info(`Waiting for first L1→L2 message to be ready...`);
|
|
112
|
+
const firstMsg = allMessages[0];
|
|
113
|
+
await waitForL1ToL2MessageReady(this.aztecNode, Fr.fromHexString(firstMsg.msgHash), {
|
|
114
|
+
timeoutSeconds: this.config.l1ToL2MessageTimeoutSeconds,
|
|
115
|
+
// Use forPublicConsumption: false so we wait until the message is in the current world
|
|
116
|
+
// state. With true, it returns one block early which causes gas estimation simulation to
|
|
117
|
+
// fail since it runs against the current state.
|
|
118
|
+
// See https://linear.app/aztec-labs/issue/A-548 for details.
|
|
119
|
+
forPublicConsumption: false
|
|
120
|
+
});
|
|
121
|
+
this.log.info(`First L1→L2 message is ready`);
|
|
122
|
+
}
|
|
123
|
+
return {
|
|
124
|
+
wallet: this.wallet,
|
|
125
|
+
defaultAccountAddress,
|
|
126
|
+
contract,
|
|
127
|
+
node: this.aztecNode,
|
|
128
|
+
l1Client,
|
|
129
|
+
rollupVersion
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
async setupTestContract(deployer) {
|
|
133
|
+
const deployOpts = {
|
|
134
|
+
from: deployer,
|
|
135
|
+
contractAddressSalt: this.config.tokenSalt,
|
|
136
|
+
universalDeploy: true
|
|
137
|
+
};
|
|
138
|
+
const deploy = TestContract.deploy(this.wallet);
|
|
139
|
+
const instance = await this.registerOrDeployContract('TestContract', deploy, deployOpts);
|
|
140
|
+
return TestContract.at(instance.address, this.wallet);
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
73
143
|
* Checks if the sender account contract is initialized, and initializes it if necessary.
|
|
74
144
|
* @returns The sender wallet.
|
|
75
145
|
*/ async setupAccount() {
|
|
@@ -85,12 +155,7 @@ export class BotFactory {
|
|
|
85
155
|
async setupAccountWithPrivateKey(secret) {
|
|
86
156
|
const salt = this.config.senderSalt ?? Fr.ONE;
|
|
87
157
|
const signingKey = deriveSigningKey(secret);
|
|
88
|
-
const
|
|
89
|
-
secret,
|
|
90
|
-
salt,
|
|
91
|
-
contract: new SchnorrAccountContract(signingKey)
|
|
92
|
-
};
|
|
93
|
-
const accountManager = await this.wallet.createAccount(accountData);
|
|
158
|
+
const accountManager = await this.wallet.createSchnorrAccount(secret, salt, signingKey);
|
|
94
159
|
const metadata = await this.wallet.getContractMetadata(accountManager.address);
|
|
95
160
|
if (metadata.isContractInitialized) {
|
|
96
161
|
this.log.info(`Account at ${accountManager.address.toString()} already initialized`);
|
|
@@ -110,7 +175,7 @@ export class BotFactory {
|
|
|
110
175
|
maxFeesPerGas
|
|
111
176
|
});
|
|
112
177
|
await this.withNoMinTxsPerBlock(async ()=>{
|
|
113
|
-
const txHash = await deployMethod.send({
|
|
178
|
+
const { txHash } = await deployMethod.send({
|
|
114
179
|
from: AztecAddress.ZERO,
|
|
115
180
|
fee: {
|
|
116
181
|
gasSettings,
|
|
@@ -131,12 +196,7 @@ export class BotFactory {
|
|
|
131
196
|
}
|
|
132
197
|
async setupTestAccount() {
|
|
133
198
|
const [initialAccountData] = await getInitialTestAccountsData();
|
|
134
|
-
const
|
|
135
|
-
secret: initialAccountData.secret,
|
|
136
|
-
salt: initialAccountData.salt,
|
|
137
|
-
contract: new SchnorrAccountContract(initialAccountData.signingKey)
|
|
138
|
-
};
|
|
139
|
-
const accountManager = await this.wallet.createAccount(accountData);
|
|
199
|
+
const accountManager = await this.wallet.createSchnorrAccount(initialAccountData.secret, initialAccountData.salt, initialAccountData.signingKey);
|
|
140
200
|
return accountManager.address;
|
|
141
201
|
}
|
|
142
202
|
/**
|
|
@@ -168,6 +228,10 @@ export class BotFactory {
|
|
|
168
228
|
tokenInstance = await deploy.getInstance(deployOpts);
|
|
169
229
|
token = PrivateTokenContract.at(tokenInstance.address, this.wallet);
|
|
170
230
|
await this.wallet.registerContract(tokenInstance, PrivateTokenContract.artifact, tokenSecretKey);
|
|
231
|
+
// The contract constructor initializes private storage vars that need the contract's own nullifier key.
|
|
232
|
+
deployOpts.additionalScopes = [
|
|
233
|
+
tokenInstance.address
|
|
234
|
+
];
|
|
171
235
|
} else {
|
|
172
236
|
throw new Error(`Unsupported token contract type: ${this.config.contract}`);
|
|
173
237
|
}
|
|
@@ -178,7 +242,7 @@ export class BotFactory {
|
|
|
178
242
|
await deploy.register();
|
|
179
243
|
} else {
|
|
180
244
|
this.log.info(`Deploying token contract at ${address.toString()}`);
|
|
181
|
-
const txHash = await deploy.send({
|
|
245
|
+
const { txHash } = await deploy.send({
|
|
182
246
|
...deployOpts,
|
|
183
247
|
wait: NO_WAIT
|
|
184
248
|
});
|
|
@@ -216,7 +280,7 @@ export class BotFactory {
|
|
|
216
280
|
const instance = await this.registerOrDeployContract('AMM', deploy, deployOpts);
|
|
217
281
|
const amm = AMMContract.at(instance.address, this.wallet);
|
|
218
282
|
this.log.info(`AMM deployed at ${amm.address}`);
|
|
219
|
-
const minterReceipt = await lpToken.methods.set_minter(amm.address, true).send({
|
|
283
|
+
const { receipt: minterReceipt } = await lpToken.methods.set_minter(amm.address, true).send({
|
|
220
284
|
from: deployer,
|
|
221
285
|
wait: {
|
|
222
286
|
timeout: this.config.txMinedWaitSeconds
|
|
@@ -230,13 +294,13 @@ export class BotFactory {
|
|
|
230
294
|
const getPrivateBalances = ()=>Promise.all([
|
|
231
295
|
token0.methods.balance_of_private(liquidityProvider).simulate({
|
|
232
296
|
from: liquidityProvider
|
|
233
|
-
}),
|
|
297
|
+
}).then((r)=>r.result),
|
|
234
298
|
token1.methods.balance_of_private(liquidityProvider).simulate({
|
|
235
299
|
from: liquidityProvider
|
|
236
|
-
}),
|
|
300
|
+
}).then((r)=>r.result),
|
|
237
301
|
lpToken.methods.balance_of_private(liquidityProvider).simulate({
|
|
238
302
|
from: liquidityProvider
|
|
239
|
-
})
|
|
303
|
+
}).then((r)=>r.result)
|
|
240
304
|
]);
|
|
241
305
|
const authwitNonce = Fr.random();
|
|
242
306
|
// keep some tokens for swapping
|
|
@@ -255,7 +319,7 @@ export class BotFactory {
|
|
|
255
319
|
caller: amm.address,
|
|
256
320
|
call: await token1.methods.transfer_to_public_and_prepare_private_balance_increase(liquidityProvider, amm.address, amount1Max, authwitNonce).getFunctionCall()
|
|
257
321
|
});
|
|
258
|
-
const mintReceipt = await new BatchCall(this.wallet, [
|
|
322
|
+
const { receipt: mintReceipt } = await new BatchCall(this.wallet, [
|
|
259
323
|
token0.methods.mint_to_private(liquidityProvider, MINT_BALANCE),
|
|
260
324
|
token1.methods.mint_to_private(liquidityProvider, MINT_BALANCE)
|
|
261
325
|
]).send({
|
|
@@ -265,7 +329,7 @@ export class BotFactory {
|
|
|
265
329
|
}
|
|
266
330
|
});
|
|
267
331
|
this.log.info(`Sent mint tx: ${mintReceipt.txHash.toString()}`);
|
|
268
|
-
const addLiquidityReceipt = await amm.methods.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce).send({
|
|
332
|
+
const { receipt: addLiquidityReceipt } = await amm.methods.add_liquidity(amount0Max, amount1Max, amount0Min, amount1Min, authwitNonce).send({
|
|
269
333
|
from: liquidityProvider,
|
|
270
334
|
authWitnesses: [
|
|
271
335
|
token0Authwit,
|
|
@@ -290,7 +354,7 @@ export class BotFactory {
|
|
|
290
354
|
} else {
|
|
291
355
|
this.log.info(`Deploying contract ${name} at ${address.toString()}`);
|
|
292
356
|
await this.withNoMinTxsPerBlock(async ()=>{
|
|
293
|
-
const txHash = await deploy.send({
|
|
357
|
+
const { txHash } = await deploy.send({
|
|
294
358
|
...deployOpts,
|
|
295
359
|
wait: NO_WAIT
|
|
296
360
|
});
|
|
@@ -327,9 +391,14 @@ export class BotFactory {
|
|
|
327
391
|
this.log.info(`Skipping minting as ${minter.toString()} has enough tokens`);
|
|
328
392
|
return;
|
|
329
393
|
}
|
|
394
|
+
// PrivateToken's mint accesses contract-level private storage vars (admin, total_supply).
|
|
395
|
+
const additionalScopes = isStandardToken ? undefined : [
|
|
396
|
+
token.address
|
|
397
|
+
];
|
|
330
398
|
await this.withNoMinTxsPerBlock(async ()=>{
|
|
331
|
-
const txHash = await new BatchCall(token.wallet, calls).send({
|
|
399
|
+
const { txHash } = await new BatchCall(token.wallet, calls).send({
|
|
332
400
|
from: minter,
|
|
401
|
+
additionalScopes,
|
|
333
402
|
wait: NO_WAIT
|
|
334
403
|
});
|
|
335
404
|
this.log.info(`Sent token mint tx with hash ${txHash.toString()}`);
|
package/dest/index.d.ts
CHANGED
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
export { Bot } from './bot.js';
|
|
2
2
|
export { AmmBot } from './amm_bot.js';
|
|
3
|
+
export { CrossChainBot } from './cross_chain_bot.js';
|
|
3
4
|
export { BotRunner } from './runner.js';
|
|
4
5
|
export { BotStore } from './store/bot_store.js';
|
|
5
6
|
export { type BotConfig, getBotConfigFromEnv, getBotDefaultConfig, botConfigMappings, SupportedTokenContracts, } from './config.js';
|
|
6
7
|
export { getBotRunnerApiHandler } from './rpc.js';
|
|
7
8
|
export * from './interface.js';
|
|
8
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguZC50cyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL3NyYy9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsR0FBRyxFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQy9CLE9BQU8sRUFBRSxNQUFNLEVBQUUsTUFBTSxjQUFjLENBQUM7QUFDdEMsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ3JELE9BQU8sRUFBRSxTQUFTLEVBQUUsTUFBTSxhQUFhLENBQUM7QUFDeEMsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBQ2hELE9BQU8sRUFDTCxLQUFLLFNBQVMsRUFDZCxtQkFBbUIsRUFDbkIsbUJBQW1CLEVBQ25CLGlCQUFpQixFQUNqQix1QkFBdUIsR0FDeEIsTUFBTSxhQUFhLENBQUM7QUFDckIsT0FBTyxFQUFFLHNCQUFzQixFQUFFLE1BQU0sVUFBVSxDQUFDO0FBQ2xELGNBQWMsZ0JBQWdCLENBQUMifQ==
|
package/dest/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EACL,KAAK,SAAS,EACd,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,cAAc,gBAAgB,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,GAAG,EAAE,MAAM,UAAU,CAAC;AAC/B,OAAO,EAAE,MAAM,EAAE,MAAM,cAAc,CAAC;AACtC,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AACxC,OAAO,EAAE,QAAQ,EAAE,MAAM,sBAAsB,CAAC;AAChD,OAAO,EACL,KAAK,SAAS,EACd,mBAAmB,EACnB,mBAAmB,EACnB,iBAAiB,EACjB,uBAAuB,GACxB,MAAM,aAAa,CAAC;AACrB,OAAO,EAAE,sBAAsB,EAAE,MAAM,UAAU,CAAC;AAClD,cAAc,gBAAgB,CAAC"}
|
package/dest/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
export { Bot } from './bot.js';
|
|
2
2
|
export { AmmBot } from './amm_bot.js';
|
|
3
|
+
export { CrossChainBot } from './cross_chain_bot.js';
|
|
3
4
|
export { BotRunner } from './runner.js';
|
|
4
5
|
export { BotStore } from './store/bot_store.js';
|
|
5
6
|
export { getBotConfigFromEnv, getBotDefaultConfig, botConfigMappings, SupportedTokenContracts } from './config.js';
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import type { ExtendedViemWalletClient } from '@aztec/ethereum/types';
|
|
2
|
+
import { EthAddress } from '@aztec/foundation/eth-address';
|
|
3
|
+
import type { Logger } from '@aztec/foundation/log';
|
|
4
|
+
import type { AztecAddress } from '@aztec/stdlib/aztec-address';
|
|
5
|
+
import type { BotStore, PendingL1ToL2Message } from './store/index.js';
|
|
6
|
+
/** Sends an L1→L2 message via the Inbox contract and stores it. */
|
|
7
|
+
export declare function seedL1ToL2Message(l1Client: ExtendedViemWalletClient, inboxAddress: EthAddress, l2Recipient: AztecAddress, rollupVersion: bigint, store: BotStore, log: Logger): Promise<PendingL1ToL2Message>;
|
|
8
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoibDFfdG9fbDJfc2VlZGluZy5kLnRzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vc3JjL2wxX3RvX2wyX3NlZWRpbmcudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxLQUFLLEVBQUUsd0JBQXdCLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUd0RSxPQUFPLEVBQUUsVUFBVSxFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFDM0QsT0FBTyxLQUFLLEVBQUUsTUFBTSxFQUFFLE1BQU0sdUJBQXVCLENBQUM7QUFFcEQsT0FBTyxLQUFLLEVBQUUsWUFBWSxFQUFFLE1BQU0sNkJBQTZCLENBQUM7QUFJaEUsT0FBTyxLQUFLLEVBQUUsUUFBUSxFQUFFLG9CQUFvQixFQUFFLE1BQU0sa0JBQWtCLENBQUM7QUFFdkUscUVBQW1FO0FBQ25FLHdCQUFzQixpQkFBaUIsQ0FDckMsUUFBUSxFQUFFLHdCQUF3QixFQUNsQyxZQUFZLEVBQUUsVUFBVSxFQUN4QixXQUFXLEVBQUUsWUFBWSxFQUN6QixhQUFhLEVBQUUsTUFBTSxFQUNyQixLQUFLLEVBQUUsUUFBUSxFQUNmLEdBQUcsRUFBRSxNQUFNLEdBQ1YsT0FBTyxDQUFDLG9CQUFvQixDQUFDLENBeUQvQiJ9
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"l1_to_l2_seeding.d.ts","sourceRoot":"","sources":["../src/l1_to_l2_seeding.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,wBAAwB,EAAE,MAAM,uBAAuB,CAAC;AAGtE,OAAO,EAAE,UAAU,EAAE,MAAM,+BAA+B,CAAC;AAC3D,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,uBAAuB,CAAC;AAEpD,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,6BAA6B,CAAC;AAIhE,OAAO,KAAK,EAAE,QAAQ,EAAE,oBAAoB,EAAE,MAAM,kBAAkB,CAAC;AAEvE,qEAAmE;AACnE,wBAAsB,iBAAiB,CACrC,QAAQ,EAAE,wBAAwB,EAClC,YAAY,EAAE,UAAU,EACxB,WAAW,EAAE,YAAY,EACzB,aAAa,EAAE,MAAM,EACrB,KAAK,EAAE,QAAQ,EACf,GAAG,EAAE,MAAM,GACV,OAAO,CAAC,oBAAoB,CAAC,CAyD/B"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { generateClaimSecret } from '@aztec/aztec.js/ethereum';
|
|
2
|
+
import { compactArray } from '@aztec/foundation/collection';
|
|
3
|
+
import { Fr } from '@aztec/foundation/curves/bn254';
|
|
4
|
+
import { InboxAbi } from '@aztec/l1-artifacts';
|
|
5
|
+
import { decodeEventLog, getContract } from 'viem';
|
|
6
|
+
/** Sends an L1→L2 message via the Inbox contract and stores it. */ export async function seedL1ToL2Message(l1Client, inboxAddress, l2Recipient, rollupVersion, store, log) {
|
|
7
|
+
log.info('Seeding L1→L2 message');
|
|
8
|
+
const [secret, secretHash] = await generateClaimSecret(log);
|
|
9
|
+
const content = Fr.random();
|
|
10
|
+
const inbox = getContract({
|
|
11
|
+
address: inboxAddress.toString(),
|
|
12
|
+
abi: InboxAbi,
|
|
13
|
+
client: l1Client
|
|
14
|
+
});
|
|
15
|
+
const txHash = await inbox.write.sendL2Message([
|
|
16
|
+
{
|
|
17
|
+
actor: l2Recipient.toString(),
|
|
18
|
+
version: rollupVersion
|
|
19
|
+
},
|
|
20
|
+
content.toString(),
|
|
21
|
+
secretHash.toString()
|
|
22
|
+
], {
|
|
23
|
+
gas: 1_000_000n
|
|
24
|
+
});
|
|
25
|
+
log.info(`L1→L2 message sent in tx ${txHash}`);
|
|
26
|
+
const txReceipt = await l1Client.waitForTransactionReceipt({
|
|
27
|
+
hash: txHash
|
|
28
|
+
});
|
|
29
|
+
if (txReceipt.status !== 'success') {
|
|
30
|
+
throw new Error(`L1→L2 message tx failed: ${txHash}`);
|
|
31
|
+
}
|
|
32
|
+
// Extract MessageSent event
|
|
33
|
+
const messageSentLogs = compactArray(txReceipt.logs.filter((l)=>l.address.toLowerCase() === inboxAddress.toString().toLowerCase()).map((l)=>{
|
|
34
|
+
try {
|
|
35
|
+
return decodeEventLog({
|
|
36
|
+
abi: InboxAbi,
|
|
37
|
+
eventName: 'MessageSent',
|
|
38
|
+
data: l.data,
|
|
39
|
+
topics: l.topics
|
|
40
|
+
});
|
|
41
|
+
} catch {
|
|
42
|
+
return undefined;
|
|
43
|
+
}
|
|
44
|
+
}));
|
|
45
|
+
if (messageSentLogs.length !== 1) {
|
|
46
|
+
throw new Error(`Expected 1 MessageSent event, got ${messageSentLogs.length}`);
|
|
47
|
+
}
|
|
48
|
+
const event = messageSentLogs[0];
|
|
49
|
+
const msgHash = event.args.hash;
|
|
50
|
+
const globalLeafIndex = event.args.index;
|
|
51
|
+
const msg = {
|
|
52
|
+
content: content.toString(),
|
|
53
|
+
secret: secret.toString(),
|
|
54
|
+
secretHash: secretHash.toString(),
|
|
55
|
+
msgHash,
|
|
56
|
+
sender: l1Client.account.address,
|
|
57
|
+
globalLeafIndex: globalLeafIndex.toString(),
|
|
58
|
+
timestamp: Date.now()
|
|
59
|
+
};
|
|
60
|
+
await store.savePendingL1ToL2Message(msg);
|
|
61
|
+
log.info(`Seeded L1→L2 message msgHash=${msg.msgHash}`);
|
|
62
|
+
return msg;
|
|
63
|
+
}
|
package/dest/runner.d.ts
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import type { AztecNode } from '@aztec/aztec.js/node';
|
|
2
2
|
import type { AztecNodeAdmin } from '@aztec/stdlib/interfaces/client';
|
|
3
3
|
import { type TelemetryClient, type Traceable, type Tracer } from '@aztec/telemetry-client';
|
|
4
|
-
import type {
|
|
4
|
+
import type { EmbeddedWallet } from '@aztec/wallets/embedded';
|
|
5
5
|
import type { BotConfig } from './config.js';
|
|
6
6
|
import type { BotInfo, BotRunnerApi } from './interface.js';
|
|
7
7
|
import { BotStore } from './store/index.js';
|
|
@@ -19,7 +19,7 @@ export declare class BotRunner implements BotRunnerApi, Traceable {
|
|
|
19
19
|
private consecutiveErrors;
|
|
20
20
|
private healthy;
|
|
21
21
|
readonly tracer: Tracer;
|
|
22
|
-
constructor(config: BotConfig, wallet:
|
|
22
|
+
constructor(config: BotConfig, wallet: EmbeddedWallet, aztecNode: AztecNode, telemetry: TelemetryClient, aztecNodeAdmin: AztecNodeAdmin | undefined, store: BotStore);
|
|
23
23
|
/** Initializes the bot if needed. Blocks until the bot setup is finished. */
|
|
24
24
|
setup(): Promise<void>;
|
|
25
25
|
private doSetup;
|
|
@@ -50,4 +50,4 @@ export declare class BotRunner implements BotRunnerApi, Traceable {
|
|
|
50
50
|
/** Returns the bot sender address. */
|
|
51
51
|
getInfo(): Promise<BotInfo>;
|
|
52
52
|
}
|
|
53
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
53
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicnVubmVyLmQudHMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvcnVubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLHNCQUFzQixDQUFDO0FBR3RELE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLGlDQUFpQyxDQUFDO0FBQ3RFLE9BQU8sRUFBRSxLQUFLLGVBQWUsRUFBRSxLQUFLLFNBQVMsRUFBRSxLQUFLLE1BQU0sRUFBYSxNQUFNLHlCQUF5QixDQUFDO0FBQ3ZHLE9BQU8sS0FBSyxFQUFFLGNBQWMsRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBSzlELE9BQU8sS0FBSyxFQUFFLFNBQVMsRUFBRSxNQUFNLGFBQWEsQ0FBQztBQUU3QyxPQUFPLEtBQUssRUFBRSxPQUFPLEVBQUUsWUFBWSxFQUFFLE1BQU0sZ0JBQWdCLENBQUM7QUFDNUQsT0FBTyxFQUFFLFFBQVEsRUFBRSxNQUFNLGtCQUFrQixDQUFDO0FBRTVDLHFCQUFhLFNBQVUsWUFBVyxZQUFZLEVBQUUsU0FBUzs7SUFVckQsT0FBTyxDQUFDLE1BQU07SUFDZCxPQUFPLENBQUMsUUFBUSxDQUFDLE1BQU07SUFDdkIsT0FBTyxDQUFDLFFBQVEsQ0FBQyxTQUFTO0lBQzFCLE9BQU8sQ0FBQyxRQUFRLENBQUMsU0FBUztJQUMxQixPQUFPLENBQUMsUUFBUSxDQUFDLGNBQWM7SUFDL0IsT0FBTyxDQUFDLFFBQVEsQ0FBQyxLQUFLO0lBZHhCLE9BQU8sQ0FBQyxHQUFHLENBQXVCO0lBQ2xDLE9BQU8sQ0FBQyxHQUFHLENBQUMsQ0FBbUI7SUFDL0IsT0FBTyxDQUFDLGNBQWMsQ0FBaUI7SUFDdkMsT0FBTyxDQUFDLGlCQUFpQixDQUFLO0lBQzlCLE9BQU8sQ0FBQyxPQUFPLENBQVE7SUFFdkIsU0FBZ0IsTUFBTSxFQUFFLE1BQU0sQ0FBQztJQUUvQixZQUNVLE1BQU0sRUFBRSxTQUFTLEVBQ1IsTUFBTSxFQUFFLGNBQWMsRUFDdEIsU0FBUyxFQUFFLFNBQVMsRUFDcEIsU0FBUyxFQUFFLGVBQWUsRUFDMUIsY0FBYyxFQUFFLGNBQWMsR0FBRyxTQUFTLEVBQzFDLEtBQUssRUFBRSxRQUFRLEVBS2pDO0lBRUQsNkVBQTZFO0lBQ2hFLEtBQUssa0JBSWpCO1lBR2EsT0FBTztJQU1yQjs7O09BR0c7SUFDVSxLQUFLLGtCQU1qQjtJQUVEOztPQUVHO0lBQ1UsSUFBSSxrQkFPaEI7SUFFTSxTQUFTLFlBRWY7SUFFRCwwQ0FBMEM7SUFDbkMsU0FBUyxZQUVmO0lBRUQ7OztPQUdHO0lBQ1UsTUFBTSxDQUFDLE1BQU0sRUFBRSxTQUFTLGlCQWFwQztJQUVEOzs7T0FHRztJQUNVLEdBQUcsa0JBc0JmO0lBRUQscURBQXFEO0lBQzlDLFNBQVMsdUJBR2Y7SUFFRCxzQ0FBc0M7SUFDekIsT0FBTyxJQUFJLE9BQU8sQ0FBQyxPQUFPLENBQUMsQ0FNdkM7Q0FtREYifQ==
|
package/dest/runner.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAa,MAAM,yBAAyB,CAAC;AACvG,OAAO,KAAK,EAAE,
|
|
1
|
+
{"version":3,"file":"runner.d.ts","sourceRoot":"","sources":["../src/runner.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,sBAAsB,CAAC;AAGtD,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,iCAAiC,CAAC;AACtE,OAAO,EAAE,KAAK,eAAe,EAAE,KAAK,SAAS,EAAE,KAAK,MAAM,EAAa,MAAM,yBAAyB,CAAC;AACvG,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,yBAAyB,CAAC;AAK9D,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,aAAa,CAAC;AAE7C,OAAO,KAAK,EAAE,OAAO,EAAE,YAAY,EAAE,MAAM,gBAAgB,CAAC;AAC5D,OAAO,EAAE,QAAQ,EAAE,MAAM,kBAAkB,CAAC;AAE5C,qBAAa,SAAU,YAAW,YAAY,EAAE,SAAS;;IAUrD,OAAO,CAAC,MAAM;IACd,OAAO,CAAC,QAAQ,CAAC,MAAM;IACvB,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,SAAS;IAC1B,OAAO,CAAC,QAAQ,CAAC,cAAc;IAC/B,OAAO,CAAC,QAAQ,CAAC,KAAK;IAdxB,OAAO,CAAC,GAAG,CAAuB;IAClC,OAAO,CAAC,GAAG,CAAC,CAAmB;IAC/B,OAAO,CAAC,cAAc,CAAiB;IACvC,OAAO,CAAC,iBAAiB,CAAK;IAC9B,OAAO,CAAC,OAAO,CAAQ;IAEvB,SAAgB,MAAM,EAAE,MAAM,CAAC;IAE/B,YACU,MAAM,EAAE,SAAS,EACR,MAAM,EAAE,cAAc,EACtB,SAAS,EAAE,SAAS,EACpB,SAAS,EAAE,eAAe,EAC1B,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,KAAK,EAAE,QAAQ,EAKjC;IAED,6EAA6E;IAChE,KAAK,kBAIjB;YAGa,OAAO;IAMrB;;;OAGG;IACU,KAAK,kBAMjB;IAED;;OAEG;IACU,IAAI,kBAOhB;IAEM,SAAS,YAEf;IAED,0CAA0C;IACnC,SAAS,YAEf;IAED;;;OAGG;IACU,MAAM,CAAC,MAAM,EAAE,SAAS,iBAapC;IAED;;;OAGG;IACU,GAAG,kBAsBf;IAED,qDAAqD;IAC9C,SAAS,uBAGf;IAED,sCAAsC;IACzB,OAAO,IAAI,OAAO,CAAC,OAAO,CAAC,CAMvC;CAmDF"}
|
package/dest/runner.js
CHANGED
|
@@ -377,6 +377,7 @@ import { RunningPromise } from '@aztec/foundation/running-promise';
|
|
|
377
377
|
import { trackSpan } from '@aztec/telemetry-client';
|
|
378
378
|
import { AmmBot } from './amm_bot.js';
|
|
379
379
|
import { Bot } from './bot.js';
|
|
380
|
+
import { CrossChainBot } from './cross_chain_bot.js';
|
|
380
381
|
_dec = trackSpan('Bot.setup'), _dec1 = trackSpan('Bot.work');
|
|
381
382
|
export class BotRunner {
|
|
382
383
|
config;
|
|
@@ -535,7 +536,22 @@ export class BotRunner {
|
|
|
535
536
|
}
|
|
536
537
|
async #createBot() {
|
|
537
538
|
try {
|
|
538
|
-
|
|
539
|
+
switch(this.config.botMode){
|
|
540
|
+
case 'crosschain':
|
|
541
|
+
this.bot = CrossChainBot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
542
|
+
break;
|
|
543
|
+
case 'amm':
|
|
544
|
+
this.bot = AmmBot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
545
|
+
break;
|
|
546
|
+
case 'transfer':
|
|
547
|
+
this.bot = Bot.create(this.config, this.wallet, this.aztecNode, this.aztecNodeAdmin, this.store);
|
|
548
|
+
break;
|
|
549
|
+
default:
|
|
550
|
+
{
|
|
551
|
+
const _exhaustive = this.config.botMode;
|
|
552
|
+
throw new Error(`Unsupported bot mode: [${_exhaustive}]`);
|
|
553
|
+
}
|
|
554
|
+
}
|
|
539
555
|
await this.bot;
|
|
540
556
|
} catch (err) {
|
|
541
557
|
this.log.error(`Error setting up bot: ${err}`);
|