wormhole-explorer/blockchain-watcher/test/infrastructure/repositories/EvmJsonRPCBlockRepository.i...

185 lines
6.3 KiB
TypeScript
Raw Normal View History

import { describe, it, expect, afterEach, afterAll } from "@jest/globals";
import { EvmJsonRPCBlockRepository } from "../../../src/infrastructure/repositories";
import axios from "axios";
import nock from "nock";
import { EvmLogFilter, EvmTag } from "../../../src/domain/entities";
import { HttpClient } from "../../../src/infrastructure/repositories/HttpClient";
axios.defaults.adapter = "http"; // needed by nock
const rpc = "http://localhost";
const address = "0x98f3c9e6e3face36baad05fe09d375ef1464288b";
const topic = "0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2";
const txHash = "0xcbdefc83080a8f60cbde7785eb2978548fd5c1f7d0ea2c024cce537845d339c7";
let repo: EvmJsonRPCBlockRepository;
describe("EvmJsonRPCBlockRepository", () => {
afterAll(() => {
nock.restore();
});
afterEach(() => {
nock.cleanAll();
});
it("should be able to get block height", async () => {
const expectedHeight = 1980809n;
givenARepo();
givenBlockHeightIs(expectedHeight, "latest");
const result = await repo.getBlockHeight("latest");
expect(result).toBe(expectedHeight);
});
it("should be able to get several blocks", async () => {
const blockNumbers = [2n, 3n, 4n];
givenARepo();
givenBlocksArePresent(blockNumbers);
const result = await repo.getBlocks(new Set(blockNumbers));
expect(Object.keys(result)).toHaveLength(blockNumbers.length);
blockNumbers.forEach((blockNumber) => {
expect(result[blockHash(blockNumber)].number).toBe(blockNumber);
});
});
it("should be able to get logs", async () => {
const filter: EvmLogFilter = {
fromBlock: "safe",
toBlock: "latest",
addresses: [address],
topics: [],
};
givenLogsPresent(filter);
const logs = await repo.getFilteredLogs(filter);
expect(logs).toHaveLength(1);
expect(logs[0].blockNumber).toBe(1n);
expect(logs[0].blockHash).toBe(blockHash(1n));
expect(logs[0].address).toBe(address);
});
});
const givenARepo = () => {
repo = new EvmJsonRPCBlockRepository({ rpc, timeout: 100, chain: "ethereum" }, new HttpClient());
};
const givenBlockHeightIs = (height: bigint, commitment: EvmTag) => {
nock(rpc)
.post("/", {
jsonrpc: "2.0",
method: "eth_getBlockByNumber",
params: [commitment, false],
id: 1,
})
.reply(200, {
jsonrpc: "2.0",
id: 1,
result: {
number: `0x${height.toString(16)}`,
hash: blockHash(height),
timestamp: "0x654a892f",
},
});
};
const givenBlocksArePresent = (blockNumbers: bigint[]) => {
const requests = blockNumbers.map((blockNumber) => ({
jsonrpc: "2.0",
method: "eth_getBlockByNumber",
params: [blockNumber.toString(), false],
id: blockNumber.toString(),
}));
const response = blockNumbers.map((blockNumber) => ({
jsonrpc: "2.0",
id: blockNumber.toString(),
result: {
number: `0x${blockNumber.toString(16)}`,
hash: blockHash(blockNumber),
timestamp: "0x654a892f",
},
}));
nock(rpc).post("/", requests).reply(200, response);
};
const givenLogsPresent = (filter: EvmLogFilter) => {
const response = {
jsonrpc: "2.0",
id: 1,
result: [
{
address: filter.addresses[0],
topics: [topic],
data: "0x",
blockNumber: "0x1",
blockHash: blockHash(1n),
transactionHash: txHash,
transactionIndex: "0x0",
logIndex: 0,
removed: false,
},
],
};
nock(rpc).post("/").reply(200, response);
};
const blockHash = (blockNumber: bigint) => `0x${blockNumber.toString(16)}`;
/* Examples:
- blockByNumber:
{
"jsonrpc":"2.0",
"method":"eth_getBlockByNumber",
"params":[
"latest",
true
],
"id":1
}
->
{
"jsonrpc": "2.0",
"id": 1,
"result": {
"baseFeePerGas": "0x78d108bbb",
"difficulty": "0x0",
"extraData": "0x406275696c64657230783639",
"gasLimit": "0x1c9c380",
"gasUsed": "0xfca882",
"hash": "0xb29ad2fa313f50d293fcf5679c6862ee7f4a3d641f09b227ad0ee3fba10d1cbb",
"logsBloom": "0x312390b5e798baf83b514972932b522118d98b9888f5880461db7e26f4bece2e8141717140d492028f887928435127083671a3488c3b6c240130a6eeb3692d908417d91c65fbfd396d0ade2b57a263a080c64f59fb4d3c4033415503e833306057524072daeae803a45cc020c1a32f436f30037b49003cd257c965d9214b441922012654b681e5202053a7d58500a64aa040cec9d90a0c9e5e3321d821503d90cfb84961594a72f02e92c7c2559d95c86504c54260c708ea63e5e4a2538f1143096c2422250a0a20b321a8814678d26e6a6d6a872e232a500a402a3a6445b85b3cf92b481e9020c20a969eac4c50ca08667cda68812f8141108908b3d175f649",
"miner": "0x690b9a9e9aa1c9db991c7721a92d351db4fac990",
"mixHash": "0xbdeea2aa4f2a026b27bc720d28c73680a35ad3e5017568cddcb066b5c12b1f60",
"nonce": "0x0000000000000000",
"number": "0x11a9fa9",
"parentHash": "0x7f8c4ecd8772eab825ee3c8e713c5088c6a32b41d61dbd1c0833e7d4df337713",
"receiptsRoot": "0x06e3cd06761468089708b204a092545576c508739f0eff936c96914da2e277e9",
"sha3Uncles": "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
"size": "0x25301",
"stateRoot": "0x3e1333543583ec7e5fb4e337261c43ef94aeb4f77c4e7f657a538fc3c2e5b6de",
"timestamp": "0x654a892f",
"totalDifficulty": "0xc70d815d562d3cfa955",
"transactions": [
"0x5e59c0bb917e7a5a64f098cd6a370bac4f40ecdf6ca79deaccf25736fe117ef7"
],
"transactionsRoot": "0xbf23b57ac6f6aede4d886a556b7bbee868721542b9a1d912ffa5f4ead0b8ec72",
"uncles": [],
"withdrawals": [
{
"index": "0x16b0d86",
"validatorIndex": "0x2a57b",
"address": "0xdaac5ce35ad7892d5f2dd364954066f4323c9a57",
"amount": "0x105fdec"
}
]
}
}
- blockByHash:
*/