@aztec/ethereum 0.63.1 → 0.64.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dest/contracts/index.d.ts +2 -0
- package/dest/contracts/index.d.ts.map +1 -0
- package/dest/contracts/index.js +2 -0
- package/dest/contracts/rollup.d.ts +14 -0
- package/dest/contracts/rollup.d.ts.map +1 -0
- package/dest/contracts/rollup.js +55 -0
- package/dest/index.d.ts +1 -0
- package/dest/index.d.ts.map +1 -1
- package/dest/index.js +2 -1
- package/dest/test/index.d.ts +3 -0
- package/dest/test/index.d.ts.map +1 -0
- package/dest/test/index.js +3 -0
- package/dest/test/start_anvil.d.ts +9 -0
- package/dest/test/start_anvil.d.ts.map +1 -0
- package/dest/test/start_anvil.js +30 -0
- package/dest/test/tx_delayer.d.ts +23 -0
- package/dest/test/tx_delayer.d.ts.map +1 -0
- package/dest/test/tx_delayer.js +102 -0
- package/dest/types.d.ts +10 -0
- package/dest/types.d.ts.map +1 -0
- package/dest/types.js +2 -0
- package/package.json +10 -4
- package/src/contracts/index.ts +1 -0
- package/src/contracts/rollup.ts +60 -0
- package/src/index.ts +1 -0
- package/src/test/index.ts +2 -0
- package/src/test/start_anvil.ts +39 -0
- package/src/test/tx_delayer.ts +150 -0
- package/src/types.ts +22 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/contracts/index.ts"],"names":[],"mappings":"AAAA,cAAc,aAAa,CAAC"}
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { type Hex, type PublicClient } from 'viem';
|
|
2
|
+
import { type L1ReaderConfig } from '../l1_reader.js';
|
|
3
|
+
export declare class RollupContract {
|
|
4
|
+
private readonly rollup;
|
|
5
|
+
constructor(client: PublicClient, address: Hex);
|
|
6
|
+
getL1StartBlock(): Promise<bigint>;
|
|
7
|
+
getL1GenesisTime(): Promise<bigint>;
|
|
8
|
+
getBlockNumber(): Promise<bigint>;
|
|
9
|
+
getProvenBlockNumber(): Promise<bigint>;
|
|
10
|
+
getSlotNumber(): Promise<bigint>;
|
|
11
|
+
getEpochNumber(blockNumber?: bigint): Promise<bigint>;
|
|
12
|
+
static getFromConfig(config: L1ReaderConfig): RollupContract;
|
|
13
|
+
}
|
|
14
|
+
//# sourceMappingURL=rollup.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"rollup.d.ts","sourceRoot":"","sources":["../../src/contracts/rollup.ts"],"names":[],"mappings":"AAGA,OAAO,EAGL,KAAK,GAAG,EAER,KAAK,YAAY,EAIlB,MAAM,MAAM,CAAC;AAGd,OAAO,EAAE,KAAK,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAEtD,qBAAa,cAAc;IACzB,OAAO,CAAC,QAAQ,CAAC,MAAM,CAA8E;gBAEzF,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,GAAG;IAK9C,eAAe;IAKf,gBAAgB;IAIhB,cAAc;IAId,oBAAoB;IAIpB,aAAa;IAIP,cAAc,CAAC,WAAW,CAAC,EAAE,MAAM;IAKzC,MAAM,CAAC,aAAa,CAAC,MAAM,EAAE,cAAc;CAQ5C"}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
import { __esDecorate, __runInitializers } from "tslib";
|
|
2
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
3
|
+
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
4
|
+
import { createPublicClient, getContract, http, } from 'viem';
|
|
5
|
+
import { createEthereumChain } from '../ethereum_chain.js';
|
|
6
|
+
let RollupContract = (() => {
|
|
7
|
+
var _a;
|
|
8
|
+
let _instanceExtraInitializers = [];
|
|
9
|
+
let _getL1StartBlock_decorators;
|
|
10
|
+
let _getL1GenesisTime_decorators;
|
|
11
|
+
return _a = class RollupContract {
|
|
12
|
+
constructor(client, address) {
|
|
13
|
+
this.rollup = __runInitializers(this, _instanceExtraInitializers);
|
|
14
|
+
this.rollup = getContract({ address, abi: RollupAbi, client });
|
|
15
|
+
}
|
|
16
|
+
getL1StartBlock() {
|
|
17
|
+
return this.rollup.read.L1_BLOCK_AT_GENESIS();
|
|
18
|
+
}
|
|
19
|
+
getL1GenesisTime() {
|
|
20
|
+
return this.rollup.read.GENESIS_TIME();
|
|
21
|
+
}
|
|
22
|
+
getBlockNumber() {
|
|
23
|
+
return this.rollup.read.getPendingBlockNumber();
|
|
24
|
+
}
|
|
25
|
+
getProvenBlockNumber() {
|
|
26
|
+
return this.rollup.read.getProvenBlockNumber();
|
|
27
|
+
}
|
|
28
|
+
getSlotNumber() {
|
|
29
|
+
return this.rollup.read.getCurrentSlot();
|
|
30
|
+
}
|
|
31
|
+
async getEpochNumber(blockNumber) {
|
|
32
|
+
blockNumber ?? (blockNumber = await this.getBlockNumber());
|
|
33
|
+
return this.rollup.read.getEpochForBlock([BigInt(blockNumber)]);
|
|
34
|
+
}
|
|
35
|
+
static getFromConfig(config) {
|
|
36
|
+
const client = createPublicClient({
|
|
37
|
+
transport: http(config.l1RpcUrl),
|
|
38
|
+
chain: createEthereumChain(config.l1RpcUrl, config.l1ChainId).chainInfo,
|
|
39
|
+
});
|
|
40
|
+
const address = config.l1Contracts.rollupAddress.toString();
|
|
41
|
+
return new _a(client, address);
|
|
42
|
+
}
|
|
43
|
+
},
|
|
44
|
+
(() => {
|
|
45
|
+
const _metadata = typeof Symbol === "function" && Symbol.metadata ? Object.create(null) : void 0;
|
|
46
|
+
_getL1StartBlock_decorators = [memoize];
|
|
47
|
+
_getL1GenesisTime_decorators = [memoize];
|
|
48
|
+
__esDecorate(_a, null, _getL1StartBlock_decorators, { kind: "method", name: "getL1StartBlock", static: false, private: false, access: { has: obj => "getL1StartBlock" in obj, get: obj => obj.getL1StartBlock }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
49
|
+
__esDecorate(_a, null, _getL1GenesisTime_decorators, { kind: "method", name: "getL1GenesisTime", static: false, private: false, access: { has: obj => "getL1GenesisTime" in obj, get: obj => obj.getL1GenesisTime }, metadata: _metadata }, null, _instanceExtraInitializers);
|
|
50
|
+
if (_metadata) Object.defineProperty(_a, Symbol.metadata, { enumerable: true, configurable: true, writable: true, value: _metadata });
|
|
51
|
+
})(),
|
|
52
|
+
_a;
|
|
53
|
+
})();
|
|
54
|
+
export { RollupContract };
|
|
55
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoicm9sbHVwLmpzIiwic291cmNlUm9vdCI6IiIsInNvdXJjZXMiOlsiLi4vLi4vc3JjL2NvbnRyYWN0cy9yb2xsdXAudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IjtBQUFBLE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSw4QkFBOEIsQ0FBQztBQUN2RCxPQUFPLEVBQUUsU0FBUyxFQUFFLE1BQU0scUJBQXFCLENBQUM7QUFFaEQsT0FBTyxFQU1MLGtCQUFrQixFQUNsQixXQUFXLEVBQ1gsSUFBSSxHQUNMLE1BQU0sTUFBTSxDQUFDO0FBRWQsT0FBTyxFQUFFLG1CQUFtQixFQUFFLE1BQU0sc0JBQXNCLENBQUM7SUFHOUMsY0FBYzs7Ozs7c0JBQWQsY0FBYztZQUd6QixZQUFZLE1BQW9CLEVBQUUsT0FBWTtnQkFGN0IsV0FBTSxHQURaLG1EQUFjLENBQzRFO2dCQUduRyxJQUFJLENBQUMsTUFBTSxHQUFHLFdBQVcsQ0FBQyxFQUFFLE9BQU8sRUFBRSxHQUFHLEVBQUUsU0FBUyxFQUFFLE1BQU0sRUFBRSxDQUFDLENBQUM7WUFDakUsQ0FBQztZQUdELGVBQWU7Z0JBQ2IsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxtQkFBbUIsRUFBRSxDQUFDO1lBQ2hELENBQUM7WUFHRCxnQkFBZ0I7Z0JBQ2QsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxZQUFZLEVBQUUsQ0FBQztZQUN6QyxDQUFDO1lBRUQsY0FBYztnQkFDWixPQUFPLElBQUksQ0FBQyxNQUFNLENBQUMsSUFBSSxDQUFDLHFCQUFxQixFQUFFLENBQUM7WUFDbEQsQ0FBQztZQUVELG9CQUFvQjtnQkFDbEIsT0FBTyxJQUFJLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxvQkFBb0IsRUFBRSxDQUFDO1lBQ2pELENBQUM7WUFFRCxhQUFhO2dCQUNYLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsY0FBYyxFQUFFLENBQUM7WUFDM0MsQ0FBQztZQUVELEtBQUssQ0FBQyxjQUFjLENBQUMsV0FBb0I7Z0JBQ3ZDLFdBQVcsS0FBWCxXQUFXLEdBQUssTUFBTSxJQUFJLENBQUMsY0FBYyxFQUFFLEVBQUM7Z0JBQzVDLE9BQU8sSUFBSSxDQUFDLE1BQU0sQ0FBQyxJQUFJLENBQUMsZ0JBQWdCLENBQUMsQ0FBQyxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUMsQ0FBQyxDQUFDO1lBQ2xFLENBQUM7WUFFRCxNQUFNLENBQUMsYUFBYSxDQUFDLE1BQXNCO2dCQUN6QyxNQUFNLE1BQU0sR0FBRyxrQkFBa0IsQ0FBQztvQkFDaEMsU0FBUyxFQUFFLElBQUksQ0FBQyxNQUFNLENBQUMsUUFBUSxDQUFDO29CQUNoQyxLQUFLLEVBQUUsbUJBQW1CLENBQUMsTUFBTSxDQUFDLFFBQVEsRUFBRSxNQUFNLENBQUMsU0FBUyxDQUFDLENBQUMsU0FBUztpQkFDeEUsQ0FBQyxDQUFDO2dCQUNILE1BQU0sT0FBTyxHQUFHLE1BQU0sQ0FBQyxXQUFXLENBQUMsYUFBYSxDQUFDLFFBQVEsRUFBRSxDQUFDO2dCQUM1RCxPQUFPLElBQUksRUFBYyxDQUFDLE1BQU0sRUFBRSxPQUFPLENBQUMsQ0FBQztZQUM3QyxDQUFDOzs7OzJDQWxDQSxPQUFPOzRDQUtQLE9BQU87WUFKUiw4TEFBQSxlQUFlLDZEQUVkO1lBR0QsaU1BQUEsZ0JBQWdCLDZEQUVmOzs7OztTQWZVLGNBQWMifQ==
|
package/dest/index.d.ts
CHANGED
package/dest/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA,cAAc,gBAAgB,CAAC;AAC/B,cAAc,0BAA0B,CAAC;AACzC,cAAc,4BAA4B,CAAC;AAC3C,cAAc,gBAAgB,CAAC;AAC/B,cAAc,qBAAqB,CAAC;AACpC,cAAc,YAAY,CAAC;AAC3B,cAAc,aAAa,CAAC;AAC5B,cAAc,YAAY,CAAC"}
|
package/dest/index.js
CHANGED
|
@@ -5,4 +5,5 @@ export * from './l1_reader.js';
|
|
|
5
5
|
export * from './ethereum_chain.js';
|
|
6
6
|
export * from './utils.js';
|
|
7
7
|
export * from './config.js';
|
|
8
|
-
|
|
8
|
+
export * from './types.js';
|
|
9
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9zcmMvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxnQkFBZ0IsQ0FBQztBQUMvQixjQUFjLDBCQUEwQixDQUFDO0FBQ3pDLGNBQWMsNEJBQTRCLENBQUM7QUFDM0MsY0FBYyxnQkFBZ0IsQ0FBQztBQUMvQixjQUFjLHFCQUFxQixDQUFDO0FBQ3BDLGNBQWMsWUFBWSxDQUFDO0FBQzNCLGNBQWMsYUFBYSxDQUFDO0FBQzVCLGNBQWMsWUFBWSxDQUFDIn0=
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/test/index.ts"],"names":[],"mappings":"AAAA,cAAc,kBAAkB,CAAC;AACjC,cAAc,iBAAiB,CAAC"}
|
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
export * from './start_anvil.js';
|
|
2
|
+
export * from './tx_delayer.js';
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdC9pbmRleC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxjQUFjLGtCQUFrQixDQUFDO0FBQ2pDLGNBQWMsaUJBQWlCLENBQUMifQ==
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
import { type Anvil } from '@viem/anvil';
|
|
2
|
+
/**
|
|
3
|
+
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
4
|
+
*/
|
|
5
|
+
export declare function startAnvil(l1BlockTime?: number): Promise<{
|
|
6
|
+
anvil: Anvil;
|
|
7
|
+
rpcUrl: string;
|
|
8
|
+
}>;
|
|
9
|
+
//# sourceMappingURL=start_anvil.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"start_anvil.d.ts","sourceRoot":"","sources":["../../src/test/start_anvil.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,KAAK,EAAe,MAAM,aAAa,CAAC;AAItD;;GAEG;AACH,wBAAsB,UAAU,CAAC,WAAW,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC;IAAE,KAAK,EAAE,KAAK,CAAC;IAAC,MAAM,EAAE,MAAM,CAAA;CAAE,CAAC,CA4BhG"}
|
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
2
|
+
import { fileURLToPath } from '@aztec/foundation/url';
|
|
3
|
+
import { createAnvil } from '@viem/anvil';
|
|
4
|
+
import getPort from 'get-port';
|
|
5
|
+
import { dirname, resolve } from 'path';
|
|
6
|
+
/**
|
|
7
|
+
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
8
|
+
*/
|
|
9
|
+
export async function startAnvil(l1BlockTime) {
|
|
10
|
+
let ethereumHostPort;
|
|
11
|
+
const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
|
|
12
|
+
// Start anvil.
|
|
13
|
+
// We go via a wrapper script to ensure if the parent dies, anvil dies.
|
|
14
|
+
const anvil = await retry(async () => {
|
|
15
|
+
ethereumHostPort = await getPort();
|
|
16
|
+
const anvil = createAnvil({
|
|
17
|
+
anvilBinary,
|
|
18
|
+
port: ethereumHostPort,
|
|
19
|
+
blockTime: l1BlockTime,
|
|
20
|
+
});
|
|
21
|
+
await anvil.start();
|
|
22
|
+
return anvil;
|
|
23
|
+
}, 'Start anvil', makeBackoff([5, 5, 5]));
|
|
24
|
+
if (!ethereumHostPort) {
|
|
25
|
+
throw new Error('Failed to start anvil');
|
|
26
|
+
}
|
|
27
|
+
const rpcUrl = `http://127.0.0.1:${ethereumHostPort}`;
|
|
28
|
+
return { anvil, rpcUrl };
|
|
29
|
+
}
|
|
30
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoic3RhcnRfYW52aWwuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi8uLi9zcmMvdGVzdC9zdGFydF9hbnZpbC50cyJdLCJuYW1lcyI6W10sIm1hcHBpbmdzIjoiQUFBQSxPQUFPLEVBQUUsV0FBVyxFQUFFLEtBQUssRUFBRSxNQUFNLHlCQUF5QixDQUFDO0FBQzdELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSx1QkFBdUIsQ0FBQztBQUV0RCxPQUFPLEVBQWMsV0FBVyxFQUFFLE1BQU0sYUFBYSxDQUFDO0FBQ3RELE9BQU8sT0FBTyxNQUFNLFVBQVUsQ0FBQztBQUMvQixPQUFPLEVBQUUsT0FBTyxFQUFFLE9BQU8sRUFBRSxNQUFNLE1BQU0sQ0FBQztBQUV4Qzs7R0FFRztBQUNILE1BQU0sQ0FBQyxLQUFLLFVBQVUsVUFBVSxDQUFDLFdBQW9CO0lBQ25ELElBQUksZ0JBQW9DLENBQUM7SUFFekMsTUFBTSxXQUFXLEdBQUcsT0FBTyxDQUFDLE9BQU8sQ0FBQyxhQUFhLENBQUMsTUFBTSxDQUFDLElBQUksQ0FBQyxHQUFHLENBQUMsQ0FBQyxFQUFFLFFBQVEsRUFBRSwrQkFBK0IsQ0FBQyxDQUFDO0lBRWhILGVBQWU7SUFDZix1RUFBdUU7SUFDdkUsTUFBTSxLQUFLLEdBQUcsTUFBTSxLQUFLLENBQ3ZCLEtBQUssSUFBSSxFQUFFO1FBQ1QsZ0JBQWdCLEdBQUcsTUFBTSxPQUFPLEVBQUUsQ0FBQztRQUNuQyxNQUFNLEtBQUssR0FBRyxXQUFXLENBQUM7WUFDeEIsV0FBVztZQUNYLElBQUksRUFBRSxnQkFBZ0I7WUFDdEIsU0FBUyxFQUFFLFdBQVc7U0FDdkIsQ0FBQyxDQUFDO1FBQ0gsTUFBTSxLQUFLLENBQUMsS0FBSyxFQUFFLENBQUM7UUFDcEIsT0FBTyxLQUFLLENBQUM7SUFDZixDQUFDLEVBQ0QsYUFBYSxFQUNiLFdBQVcsQ0FBQyxDQUFDLENBQUMsRUFBRSxDQUFDLEVBQUUsQ0FBQyxDQUFDLENBQUMsQ0FDdkIsQ0FBQztJQUVGLElBQUksQ0FBQyxnQkFBZ0IsRUFBRSxDQUFDO1FBQ3RCLE1BQU0sSUFBSSxLQUFLLENBQUMsdUJBQXVCLENBQUMsQ0FBQztJQUMzQyxDQUFDO0lBRUQsTUFBTSxNQUFNLEdBQUcsb0JBQW9CLGdCQUFnQixFQUFFLENBQUM7SUFDdEQsT0FBTyxFQUFFLEtBQUssRUFBRSxNQUFNLEVBQUUsQ0FBQztBQUMzQixDQUFDIn0=
|
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
import { type DebugLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { type Client, type Hex, type WalletClient } from 'viem';
|
|
3
|
+
export declare function waitUntilBlock<T extends Client>(client: T, blockNumber: number | bigint, logger?: DebugLogger): Promise<boolean>;
|
|
4
|
+
export declare function waitUntilL1Timestamp<T extends Client>(client: T, timestamp: number | bigint, logger?: DebugLogger): Promise<boolean>;
|
|
5
|
+
export interface Delayer {
|
|
6
|
+
/** Returns the list of all txs (not just the delayed ones) sent through the attached client. */
|
|
7
|
+
getTxs(): Hex[];
|
|
8
|
+
/** Delays the next tx to be sent so it lands on the given L1 block number. */
|
|
9
|
+
pauseNextTxUntilBlock(l1BlockNumber: number | bigint | undefined): void;
|
|
10
|
+
/** Delays the next tx to be sent so it lands on the given timestamp. */
|
|
11
|
+
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint | undefined): void;
|
|
12
|
+
}
|
|
13
|
+
/**
|
|
14
|
+
* Returns a new client (without modifying the one passed in) with an injected tx delayer.
|
|
15
|
+
* The delayer can be used to hold off the next tx to be sent until a given block number.
|
|
16
|
+
*/
|
|
17
|
+
export declare function withDelayer<T extends WalletClient>(client: T, opts: {
|
|
18
|
+
ethereumSlotDuration: bigint | number;
|
|
19
|
+
}): {
|
|
20
|
+
client: T;
|
|
21
|
+
delayer: Delayer;
|
|
22
|
+
};
|
|
23
|
+
//# sourceMappingURL=tx_delayer.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"tx_delayer.d.ts","sourceRoot":"","sources":["../../src/test/tx_delayer.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,WAAW,EAAqB,MAAM,uBAAuB,CAAC;AAI5E,OAAO,EACL,KAAK,MAAM,EACX,KAAK,GAAG,EAER,KAAK,YAAY,EAIlB,MAAM,MAAM,CAAC;AAEd,wBAAgB,cAAc,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,WAAW,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,oBAgB7G;AAED,wBAAgB,oBAAoB,CAAC,CAAC,SAAS,MAAM,EAAE,MAAM,EAAE,CAAC,EAAE,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,MAAM,CAAC,EAAE,WAAW,oBAuBjH;AAED,MAAM,WAAW,OAAO;IACtB,gGAAgG;IAChG,MAAM,IAAI,GAAG,EAAE,CAAC;IAChB,8EAA8E;IAC9E,qBAAqB,CAAC,aAAa,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;IACxE,wEAAwE;IACxE,yBAAyB,CAAC,WAAW,EAAE,MAAM,GAAG,MAAM,GAAG,SAAS,GAAG,IAAI,CAAC;CAC3E;AAwBD;;;GAGG;AACH,wBAAgB,WAAW,CAAC,CAAC,SAAS,YAAY,EAChD,MAAM,EAAE,CAAC,EACT,IAAI,EAAE;IAAE,oBAAoB,EAAE,MAAM,GAAG,MAAM,CAAA;CAAE,GAC9C;IAAE,MAAM,EAAE,CAAC,CAAC;IAAC,OAAO,EAAE,OAAO,CAAA;CAAE,CAsDjC"}
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
import { createDebugLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
3
|
+
import { inspect } from 'util';
|
|
4
|
+
import { keccak256, publicActions, walletActions, } from 'viem';
|
|
5
|
+
export function waitUntilBlock(client, blockNumber, logger) {
|
|
6
|
+
const publicClient = 'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
|
|
7
|
+
? client
|
|
8
|
+
: client.extend(publicActions);
|
|
9
|
+
return retryUntil(async () => {
|
|
10
|
+
const currentBlockNumber = await publicClient.getBlockNumber({ cacheTime: 0 });
|
|
11
|
+
logger?.debug(`Block number is ${currentBlockNumber} (waiting until ${blockNumber})`);
|
|
12
|
+
return currentBlockNumber >= BigInt(blockNumber);
|
|
13
|
+
}, `Wait until L1 block ${blockNumber}`, 60, 0.1);
|
|
14
|
+
}
|
|
15
|
+
export function waitUntilL1Timestamp(client, timestamp, logger) {
|
|
16
|
+
const publicClient = 'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
|
|
17
|
+
? client
|
|
18
|
+
: client.extend(publicActions);
|
|
19
|
+
let lastBlock = undefined;
|
|
20
|
+
return retryUntil(async () => {
|
|
21
|
+
const currentBlockNumber = await publicClient.getBlockNumber({ cacheTime: 0 });
|
|
22
|
+
if (currentBlockNumber === lastBlock) {
|
|
23
|
+
return false;
|
|
24
|
+
}
|
|
25
|
+
lastBlock = currentBlockNumber;
|
|
26
|
+
const currentBlock = await publicClient.getBlock({ includeTransactions: false, blockNumber: currentBlockNumber });
|
|
27
|
+
const currentTs = currentBlock.timestamp;
|
|
28
|
+
logger?.debug(`Block timstamp is ${currentTs} (waiting until ${timestamp})`);
|
|
29
|
+
return currentTs >= BigInt(timestamp);
|
|
30
|
+
}, `Wait until L1 timestamp ${timestamp}`, 60, 0.1);
|
|
31
|
+
}
|
|
32
|
+
class DelayerImpl {
|
|
33
|
+
constructor(opts) {
|
|
34
|
+
this.nextWait = undefined;
|
|
35
|
+
this.txs = [];
|
|
36
|
+
this.ethereumSlotDuration = BigInt(opts.ethereumSlotDuration);
|
|
37
|
+
}
|
|
38
|
+
getTxs() {
|
|
39
|
+
return this.txs;
|
|
40
|
+
}
|
|
41
|
+
pauseNextTxUntilBlock(l1BlockNumber) {
|
|
42
|
+
this.nextWait = { l1BlockNumber: BigInt(l1BlockNumber) };
|
|
43
|
+
}
|
|
44
|
+
pauseNextTxUntilTimestamp(l1Timestamp) {
|
|
45
|
+
this.nextWait = { l1Timestamp: BigInt(l1Timestamp) };
|
|
46
|
+
}
|
|
47
|
+
}
|
|
48
|
+
/**
|
|
49
|
+
* Returns a new client (without modifying the one passed in) with an injected tx delayer.
|
|
50
|
+
* The delayer can be used to hold off the next tx to be sent until a given block number.
|
|
51
|
+
*/
|
|
52
|
+
export function withDelayer(client, opts) {
|
|
53
|
+
const logger = createDebugLogger('aztec:ethereum:tx_delayer');
|
|
54
|
+
const delayer = new DelayerImpl(opts);
|
|
55
|
+
const extended = client
|
|
56
|
+
// Tweak sendRawTransaction so it uses the delay defined in the delayer.
|
|
57
|
+
// Note that this will only work with local accounts (ie accounts for which we have the private key).
|
|
58
|
+
// Transactions signed by the node will not be delayed since they use sendTransaction directly,
|
|
59
|
+
// but we do not use them in our codebase at all.
|
|
60
|
+
.extend(client => ({
|
|
61
|
+
async sendRawTransaction(...args) {
|
|
62
|
+
if (delayer.nextWait !== undefined) {
|
|
63
|
+
const waitUntil = delayer.nextWait;
|
|
64
|
+
delayer.nextWait = undefined;
|
|
65
|
+
const publicClient = client;
|
|
66
|
+
const wait = 'l1BlockNumber' in waitUntil
|
|
67
|
+
? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger)
|
|
68
|
+
: waitUntilL1Timestamp(publicClient, waitUntil.l1Timestamp - delayer.ethereumSlotDuration, logger);
|
|
69
|
+
// Compute the tx hash manually so we emulate sendRawTransaction response
|
|
70
|
+
const { serializedTransaction } = args[0];
|
|
71
|
+
const txHash = keccak256(serializedTransaction);
|
|
72
|
+
logger.info(`Delaying tx ${txHash} until ${inspect(waitUntil)}`);
|
|
73
|
+
// Do not await here so we can return the tx hash immediately as if it had been sent on the spot.
|
|
74
|
+
// Instead, delay it so it lands on the desired block number or timestamp, assuming anvil will
|
|
75
|
+
// mine it immediately.
|
|
76
|
+
void wait
|
|
77
|
+
.then(async () => {
|
|
78
|
+
const txHash = await client.sendRawTransaction(...args);
|
|
79
|
+
logger.info(`Sent previously delayed tx ${txHash} to land on ${inspect(waitUntil)}`);
|
|
80
|
+
delayer.txs.push(txHash);
|
|
81
|
+
})
|
|
82
|
+
.catch(err => logger.error(`Error sending tx after delay`, err));
|
|
83
|
+
return Promise.resolve(txHash);
|
|
84
|
+
}
|
|
85
|
+
else {
|
|
86
|
+
const txHash = await client.sendRawTransaction(...args);
|
|
87
|
+
logger.debug(`Sent tx immediately ${txHash}`);
|
|
88
|
+
delayer.txs.push(txHash);
|
|
89
|
+
return txHash;
|
|
90
|
+
}
|
|
91
|
+
},
|
|
92
|
+
}))
|
|
93
|
+
// Re-extend with sendTransaction so it uses the modified sendRawTransaction.
|
|
94
|
+
.extend(client => ({ sendTransaction: walletActions(client).sendTransaction }))
|
|
95
|
+
// And with the actions that depend on the modified sendTransaction
|
|
96
|
+
.extend(client => ({
|
|
97
|
+
writeContract: walletActions(client).writeContract,
|
|
98
|
+
deployContract: walletActions(client).deployContract,
|
|
99
|
+
}));
|
|
100
|
+
return { client: extended, delayer };
|
|
101
|
+
}
|
|
102
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHhfZGVsYXllci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uLy4uL3NyYy90ZXN0L3R4X2RlbGF5ZXIudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFvQixpQkFBaUIsRUFBRSxNQUFNLHVCQUF1QixDQUFDO0FBQzVFLE9BQU8sRUFBRSxVQUFVLEVBQUUsTUFBTSx5QkFBeUIsQ0FBQztBQUVyRCxPQUFPLEVBQUUsT0FBTyxFQUFFLE1BQU0sTUFBTSxDQUFDO0FBQy9CLE9BQU8sRUFLTCxTQUFTLEVBQ1QsYUFBYSxFQUNiLGFBQWEsR0FDZCxNQUFNLE1BQU0sQ0FBQztBQUVkLE1BQU0sVUFBVSxjQUFjLENBQW1CLE1BQVMsRUFBRSxXQUE0QixFQUFFLE1BQW9CO0lBQzVHLE1BQU0sWUFBWSxHQUNoQixnQkFBZ0IsSUFBSSxNQUFNLElBQUksT0FBTyxNQUFNLENBQUMsY0FBYyxLQUFLLFVBQVU7UUFDdkUsQ0FBQyxDQUFFLE1BQWtDO1FBQ3JDLENBQUMsQ0FBQyxNQUFNLENBQUMsTUFBTSxDQUFDLGFBQWEsQ0FBQyxDQUFDO0lBRW5DLE9BQU8sVUFBVSxDQUNmLEtBQUssSUFBSSxFQUFFO1FBQ1QsTUFBTSxrQkFBa0IsR0FBRyxNQUFNLFlBQVksQ0FBQyxjQUFjLENBQUMsRUFBRSxTQUFTLEVBQUUsQ0FBQyxFQUFFLENBQUMsQ0FBQztRQUMvRSxNQUFNLEVBQUUsS0FBSyxDQUFDLG1CQUFtQixrQkFBa0IsbUJBQW1CLFdBQVcsR0FBRyxDQUFDLENBQUM7UUFDdEYsT0FBTyxrQkFBa0IsSUFBSSxNQUFNLENBQUMsV0FBVyxDQUFDLENBQUM7SUFDbkQsQ0FBQyxFQUNELHVCQUF1QixXQUFXLEVBQUUsRUFDcEMsRUFBRSxFQUNGLEdBQUcsQ0FDSixDQUFDO0FBQ0osQ0FBQztBQUVELE1BQU0sVUFBVSxvQkFBb0IsQ0FBbUIsTUFBUyxFQUFFLFNBQTBCLEVBQUUsTUFBb0I7SUFDaEgsTUFBTSxZQUFZLEdBQ2hCLGdCQUFnQixJ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
|
package/dest/types.d.ts
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import { type Chain, type Client, type HttpTransport, type PrivateKeyAccount, type PublicActions, type PublicRpcSchema, type WalletActions, type WalletRpcSchema } from 'viem';
|
|
2
|
+
/**
|
|
3
|
+
* Type for a viem wallet and public client using a local private key.
|
|
4
|
+
* Created as: `createWalletClient({ account: privateKeyToAccount(key), transport: http(url), chain }).extend(publicActions)`
|
|
5
|
+
*/
|
|
6
|
+
export type ViemClient = Client<HttpTransport, Chain, PrivateKeyAccount, [
|
|
7
|
+
...PublicRpcSchema,
|
|
8
|
+
...WalletRpcSchema
|
|
9
|
+
], PublicActions<HttpTransport, Chain> & WalletActions<Chain, PrivateKeyAccount>>;
|
|
10
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,KAAK,KAAK,EACV,KAAK,MAAM,EACX,KAAK,aAAa,EAClB,KAAK,iBAAiB,EACtB,KAAK,aAAa,EAClB,KAAK,eAAe,EACpB,KAAK,aAAa,EAClB,KAAK,eAAe,EACrB,MAAM,MAAM,CAAC;AAEd;;;GAGG;AACH,MAAM,MAAM,UAAU,GAAG,MAAM,CAC7B,aAAa,EACb,KAAK,EACL,iBAAiB,EACjB;IAAC,GAAG,eAAe;IAAE,GAAG,eAAe;CAAC,EACxC,aAAa,CAAC,aAAa,EAAE,KAAK,CAAC,GAAG,aAAa,CAAC,KAAK,EAAE,iBAAiB,CAAC,CAC9E,CAAC"}
|
package/dest/types.js
ADDED
package/package.json
CHANGED
|
@@ -1,8 +1,12 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@aztec/ethereum",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.64.0",
|
|
4
4
|
"type": "module",
|
|
5
|
-
"exports":
|
|
5
|
+
"exports": {
|
|
6
|
+
".": "./dest/index.js",
|
|
7
|
+
"./test": "./dest/test/index.js",
|
|
8
|
+
"./contracts": "./dest/contracts/index.js"
|
|
9
|
+
},
|
|
6
10
|
"typedocOptions": {
|
|
7
11
|
"entryPoints": [
|
|
8
12
|
"./src/index.ts"
|
|
@@ -24,9 +28,11 @@
|
|
|
24
28
|
"../package.common.json"
|
|
25
29
|
],
|
|
26
30
|
"dependencies": {
|
|
27
|
-
"@aztec/foundation": "0.
|
|
28
|
-
"@aztec/l1-artifacts": "0.
|
|
31
|
+
"@aztec/foundation": "0.64.0",
|
|
32
|
+
"@aztec/l1-artifacts": "0.64.0",
|
|
33
|
+
"@viem/anvil": "^0.0.10",
|
|
29
34
|
"dotenv": "^16.0.3",
|
|
35
|
+
"get-port": "^7.1.0",
|
|
30
36
|
"tslib": "^2.4.0",
|
|
31
37
|
"viem": "^2.7.15",
|
|
32
38
|
"zod": "^3.23.8"
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export * from './rollup.js';
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
import { memoize } from '@aztec/foundation/decorators';
|
|
2
|
+
import { RollupAbi } from '@aztec/l1-artifacts';
|
|
3
|
+
|
|
4
|
+
import {
|
|
5
|
+
type Chain,
|
|
6
|
+
type GetContractReturnType,
|
|
7
|
+
type Hex,
|
|
8
|
+
type HttpTransport,
|
|
9
|
+
type PublicClient,
|
|
10
|
+
createPublicClient,
|
|
11
|
+
getContract,
|
|
12
|
+
http,
|
|
13
|
+
} from 'viem';
|
|
14
|
+
|
|
15
|
+
import { createEthereumChain } from '../ethereum_chain.js';
|
|
16
|
+
import { type L1ReaderConfig } from '../l1_reader.js';
|
|
17
|
+
|
|
18
|
+
export class RollupContract {
|
|
19
|
+
private readonly rollup: GetContractReturnType<typeof RollupAbi, PublicClient<HttpTransport, Chain>>;
|
|
20
|
+
|
|
21
|
+
constructor(client: PublicClient, address: Hex) {
|
|
22
|
+
this.rollup = getContract({ address, abi: RollupAbi, client });
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
@memoize
|
|
26
|
+
getL1StartBlock() {
|
|
27
|
+
return this.rollup.read.L1_BLOCK_AT_GENESIS();
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
@memoize
|
|
31
|
+
getL1GenesisTime() {
|
|
32
|
+
return this.rollup.read.GENESIS_TIME();
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
getBlockNumber() {
|
|
36
|
+
return this.rollup.read.getPendingBlockNumber();
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getProvenBlockNumber() {
|
|
40
|
+
return this.rollup.read.getProvenBlockNumber();
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
getSlotNumber() {
|
|
44
|
+
return this.rollup.read.getCurrentSlot();
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
async getEpochNumber(blockNumber?: bigint) {
|
|
48
|
+
blockNumber ??= await this.getBlockNumber();
|
|
49
|
+
return this.rollup.read.getEpochForBlock([BigInt(blockNumber)]);
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
static getFromConfig(config: L1ReaderConfig) {
|
|
53
|
+
const client = createPublicClient({
|
|
54
|
+
transport: http(config.l1RpcUrl),
|
|
55
|
+
chain: createEthereumChain(config.l1RpcUrl, config.l1ChainId).chainInfo,
|
|
56
|
+
});
|
|
57
|
+
const address = config.l1Contracts.rollupAddress.toString();
|
|
58
|
+
return new RollupContract(client, address);
|
|
59
|
+
}
|
|
60
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { makeBackoff, retry } from '@aztec/foundation/retry';
|
|
2
|
+
import { fileURLToPath } from '@aztec/foundation/url';
|
|
3
|
+
|
|
4
|
+
import { type Anvil, createAnvil } from '@viem/anvil';
|
|
5
|
+
import getPort from 'get-port';
|
|
6
|
+
import { dirname, resolve } from 'path';
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* Ensures there's a running Anvil instance and returns the RPC URL.
|
|
10
|
+
*/
|
|
11
|
+
export async function startAnvil(l1BlockTime?: number): Promise<{ anvil: Anvil; rpcUrl: string }> {
|
|
12
|
+
let ethereumHostPort: number | undefined;
|
|
13
|
+
|
|
14
|
+
const anvilBinary = resolve(dirname(fileURLToPath(import.meta.url)), '../../', 'scripts/anvil_kill_wrapper.sh');
|
|
15
|
+
|
|
16
|
+
// Start anvil.
|
|
17
|
+
// We go via a wrapper script to ensure if the parent dies, anvil dies.
|
|
18
|
+
const anvil = await retry(
|
|
19
|
+
async () => {
|
|
20
|
+
ethereumHostPort = await getPort();
|
|
21
|
+
const anvil = createAnvil({
|
|
22
|
+
anvilBinary,
|
|
23
|
+
port: ethereumHostPort,
|
|
24
|
+
blockTime: l1BlockTime,
|
|
25
|
+
});
|
|
26
|
+
await anvil.start();
|
|
27
|
+
return anvil;
|
|
28
|
+
},
|
|
29
|
+
'Start anvil',
|
|
30
|
+
makeBackoff([5, 5, 5]),
|
|
31
|
+
);
|
|
32
|
+
|
|
33
|
+
if (!ethereumHostPort) {
|
|
34
|
+
throw new Error('Failed to start anvil');
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const rpcUrl = `http://127.0.0.1:${ethereumHostPort}`;
|
|
38
|
+
return { anvil, rpcUrl };
|
|
39
|
+
}
|
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
import { type DebugLogger, createDebugLogger } from '@aztec/foundation/log';
|
|
2
|
+
import { retryUntil } from '@aztec/foundation/retry';
|
|
3
|
+
|
|
4
|
+
import { inspect } from 'util';
|
|
5
|
+
import {
|
|
6
|
+
type Client,
|
|
7
|
+
type Hex,
|
|
8
|
+
type PublicClient,
|
|
9
|
+
type WalletClient,
|
|
10
|
+
keccak256,
|
|
11
|
+
publicActions,
|
|
12
|
+
walletActions,
|
|
13
|
+
} from 'viem';
|
|
14
|
+
|
|
15
|
+
export function waitUntilBlock<T extends Client>(client: T, blockNumber: number | bigint, logger?: DebugLogger) {
|
|
16
|
+
const publicClient =
|
|
17
|
+
'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
|
|
18
|
+
? (client as unknown as PublicClient)
|
|
19
|
+
: client.extend(publicActions);
|
|
20
|
+
|
|
21
|
+
return retryUntil(
|
|
22
|
+
async () => {
|
|
23
|
+
const currentBlockNumber = await publicClient.getBlockNumber({ cacheTime: 0 });
|
|
24
|
+
logger?.debug(`Block number is ${currentBlockNumber} (waiting until ${blockNumber})`);
|
|
25
|
+
return currentBlockNumber >= BigInt(blockNumber);
|
|
26
|
+
},
|
|
27
|
+
`Wait until L1 block ${blockNumber}`,
|
|
28
|
+
60,
|
|
29
|
+
0.1,
|
|
30
|
+
);
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export function waitUntilL1Timestamp<T extends Client>(client: T, timestamp: number | bigint, logger?: DebugLogger) {
|
|
34
|
+
const publicClient =
|
|
35
|
+
'getBlockNumber' in client && typeof client.getBlockNumber === 'function'
|
|
36
|
+
? (client as unknown as PublicClient)
|
|
37
|
+
: client.extend(publicActions);
|
|
38
|
+
|
|
39
|
+
let lastBlock: bigint | undefined = undefined;
|
|
40
|
+
return retryUntil(
|
|
41
|
+
async () => {
|
|
42
|
+
const currentBlockNumber = await publicClient.getBlockNumber({ cacheTime: 0 });
|
|
43
|
+
if (currentBlockNumber === lastBlock) {
|
|
44
|
+
return false;
|
|
45
|
+
}
|
|
46
|
+
lastBlock = currentBlockNumber;
|
|
47
|
+
const currentBlock = await publicClient.getBlock({ includeTransactions: false, blockNumber: currentBlockNumber });
|
|
48
|
+
const currentTs = currentBlock.timestamp;
|
|
49
|
+
logger?.debug(`Block timstamp is ${currentTs} (waiting until ${timestamp})`);
|
|
50
|
+
return currentTs >= BigInt(timestamp);
|
|
51
|
+
},
|
|
52
|
+
`Wait until L1 timestamp ${timestamp}`,
|
|
53
|
+
60,
|
|
54
|
+
0.1,
|
|
55
|
+
);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export interface Delayer {
|
|
59
|
+
/** Returns the list of all txs (not just the delayed ones) sent through the attached client. */
|
|
60
|
+
getTxs(): Hex[];
|
|
61
|
+
/** Delays the next tx to be sent so it lands on the given L1 block number. */
|
|
62
|
+
pauseNextTxUntilBlock(l1BlockNumber: number | bigint | undefined): void;
|
|
63
|
+
/** Delays the next tx to be sent so it lands on the given timestamp. */
|
|
64
|
+
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint | undefined): void;
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
class DelayerImpl implements Delayer {
|
|
68
|
+
constructor(opts: { ethereumSlotDuration: bigint | number }) {
|
|
69
|
+
this.ethereumSlotDuration = BigInt(opts.ethereumSlotDuration);
|
|
70
|
+
}
|
|
71
|
+
|
|
72
|
+
public ethereumSlotDuration: bigint;
|
|
73
|
+
public nextWait: { l1Timestamp: bigint } | { l1BlockNumber: bigint } | undefined = undefined;
|
|
74
|
+
public txs: Hex[] = [];
|
|
75
|
+
|
|
76
|
+
getTxs() {
|
|
77
|
+
return this.txs;
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
pauseNextTxUntilBlock(l1BlockNumber: number | bigint) {
|
|
81
|
+
this.nextWait = { l1BlockNumber: BigInt(l1BlockNumber) };
|
|
82
|
+
}
|
|
83
|
+
|
|
84
|
+
pauseNextTxUntilTimestamp(l1Timestamp: number | bigint) {
|
|
85
|
+
this.nextWait = { l1Timestamp: BigInt(l1Timestamp) };
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
|
|
89
|
+
/**
|
|
90
|
+
* Returns a new client (without modifying the one passed in) with an injected tx delayer.
|
|
91
|
+
* The delayer can be used to hold off the next tx to be sent until a given block number.
|
|
92
|
+
*/
|
|
93
|
+
export function withDelayer<T extends WalletClient>(
|
|
94
|
+
client: T,
|
|
95
|
+
opts: { ethereumSlotDuration: bigint | number },
|
|
96
|
+
): { client: T; delayer: Delayer } {
|
|
97
|
+
const logger = createDebugLogger('aztec:ethereum:tx_delayer');
|
|
98
|
+
const delayer = new DelayerImpl(opts);
|
|
99
|
+
const extended = client
|
|
100
|
+
// Tweak sendRawTransaction so it uses the delay defined in the delayer.
|
|
101
|
+
// Note that this will only work with local accounts (ie accounts for which we have the private key).
|
|
102
|
+
// Transactions signed by the node will not be delayed since they use sendTransaction directly,
|
|
103
|
+
// but we do not use them in our codebase at all.
|
|
104
|
+
.extend(client => ({
|
|
105
|
+
async sendRawTransaction(...args) {
|
|
106
|
+
if (delayer.nextWait !== undefined) {
|
|
107
|
+
const waitUntil = delayer.nextWait;
|
|
108
|
+
delayer.nextWait = undefined;
|
|
109
|
+
|
|
110
|
+
const publicClient = client as unknown as PublicClient;
|
|
111
|
+
const wait =
|
|
112
|
+
'l1BlockNumber' in waitUntil
|
|
113
|
+
? waitUntilBlock(publicClient, waitUntil.l1BlockNumber - 1n, logger)
|
|
114
|
+
: waitUntilL1Timestamp(publicClient, waitUntil.l1Timestamp - delayer.ethereumSlotDuration, logger);
|
|
115
|
+
|
|
116
|
+
// Compute the tx hash manually so we emulate sendRawTransaction response
|
|
117
|
+
const { serializedTransaction } = args[0];
|
|
118
|
+
const txHash = keccak256(serializedTransaction);
|
|
119
|
+
logger.info(`Delaying tx ${txHash} until ${inspect(waitUntil)}`);
|
|
120
|
+
|
|
121
|
+
// Do not await here so we can return the tx hash immediately as if it had been sent on the spot.
|
|
122
|
+
// Instead, delay it so it lands on the desired block number or timestamp, assuming anvil will
|
|
123
|
+
// mine it immediately.
|
|
124
|
+
void wait
|
|
125
|
+
.then(async () => {
|
|
126
|
+
const txHash = await client.sendRawTransaction(...args);
|
|
127
|
+
logger.info(`Sent previously delayed tx ${txHash} to land on ${inspect(waitUntil)}`);
|
|
128
|
+
delayer.txs.push(txHash);
|
|
129
|
+
})
|
|
130
|
+
.catch(err => logger.error(`Error sending tx after delay`, err));
|
|
131
|
+
|
|
132
|
+
return Promise.resolve(txHash);
|
|
133
|
+
} else {
|
|
134
|
+
const txHash = await client.sendRawTransaction(...args);
|
|
135
|
+
logger.debug(`Sent tx immediately ${txHash}`);
|
|
136
|
+
delayer.txs.push(txHash);
|
|
137
|
+
return txHash;
|
|
138
|
+
}
|
|
139
|
+
},
|
|
140
|
+
}))
|
|
141
|
+
// Re-extend with sendTransaction so it uses the modified sendRawTransaction.
|
|
142
|
+
.extend(client => ({ sendTransaction: walletActions(client).sendTransaction }))
|
|
143
|
+
// And with the actions that depend on the modified sendTransaction
|
|
144
|
+
.extend(client => ({
|
|
145
|
+
writeContract: walletActions(client).writeContract,
|
|
146
|
+
deployContract: walletActions(client).deployContract,
|
|
147
|
+
})) as T;
|
|
148
|
+
|
|
149
|
+
return { client: extended, delayer };
|
|
150
|
+
}
|
package/src/types.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import {
|
|
2
|
+
type Chain,
|
|
3
|
+
type Client,
|
|
4
|
+
type HttpTransport,
|
|
5
|
+
type PrivateKeyAccount,
|
|
6
|
+
type PublicActions,
|
|
7
|
+
type PublicRpcSchema,
|
|
8
|
+
type WalletActions,
|
|
9
|
+
type WalletRpcSchema,
|
|
10
|
+
} from 'viem';
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Type for a viem wallet and public client using a local private key.
|
|
14
|
+
* Created as: `createWalletClient({ account: privateKeyToAccount(key), transport: http(url), chain }).extend(publicActions)`
|
|
15
|
+
*/
|
|
16
|
+
export type ViemClient = Client<
|
|
17
|
+
HttpTransport,
|
|
18
|
+
Chain,
|
|
19
|
+
PrivateKeyAccount,
|
|
20
|
+
[...PublicRpcSchema, ...WalletRpcSchema],
|
|
21
|
+
PublicActions<HttpTransport, Chain> & WalletActions<Chain, PrivateKeyAccount>
|
|
22
|
+
>;
|