feature-826/enable-evm-arbitrum-chain-
This commit is contained in:
parent
cff86e4469
commit
5cc39fc2a4
|
@ -91,6 +91,13 @@
|
|||
"rpcs": ["https://alfajores-forno.celo-testnet.org"],
|
||||
"timeout": 10000
|
||||
},
|
||||
"arbitrum": {
|
||||
"name": "arbitrum",
|
||||
"network": "goerli",
|
||||
"chainId": 23,
|
||||
"rpcs": ["https://goerli-rollup.arbitrum.io/rpc"],
|
||||
"timeout": 10000
|
||||
},
|
||||
"optimism": {
|
||||
"name": "optimism",
|
||||
"network": "goerli",
|
||||
|
|
|
@ -40,6 +40,10 @@
|
|||
"network": "mainnet",
|
||||
"rpcs": ["https://forno.celo.org"]
|
||||
},
|
||||
"arbitrum": {
|
||||
"network": "mainnet",
|
||||
"rpcs": ["https://rpc.ankr.com/arbitrum"]
|
||||
},
|
||||
"optimism": {
|
||||
"network": "mainnet",
|
||||
"rpcs": ["https://rpc.ankr.com/optimism"]
|
||||
|
|
|
@ -29,6 +29,7 @@ const EVM_CHAINS = new Map([
|
|||
["optimism", "evmRepo"],
|
||||
["base", "evmRepo"],
|
||||
["bsc", "bsc-evmRepo"],
|
||||
["arbitrum", "arbitrum-evmRepo"],
|
||||
]);
|
||||
|
||||
export class RepositoriesBuilder {
|
||||
|
@ -69,6 +70,10 @@ export class RepositoriesBuilder {
|
|||
};
|
||||
this.repositories.set("evmRepo", new EvmJsonRPCBlockRepository(repoCfg, httpClient));
|
||||
this.repositories.set("bsc-evmRepo", new BscEvmJsonRPCBlockRepository(repoCfg, httpClient));
|
||||
this.repositories.set(
|
||||
"arbitrum-evmRepo",
|
||||
new ArbitrumEvmJsonRPCBlockRepository(repoCfg, httpClient, this.getMetadataRepository())
|
||||
);
|
||||
}
|
||||
});
|
||||
|
||||
|
|
|
@ -0,0 +1,153 @@
|
|||
import { FileMetadataRepository } from "../FileMetadataRepository";
|
||||
import { MetadataRepository } from "../../../domain/repositories";
|
||||
import { HttpClientError } from "../../errors/HttpClientError";
|
||||
import { HttpClient } from "../../rpc/http/HttpClient";
|
||||
import { EvmTag } from "../../../domain/entities";
|
||||
import winston from "../../log";
|
||||
import {
|
||||
EvmJsonRPCBlockRepository,
|
||||
EvmJsonRPCBlockRepositoryCfg,
|
||||
} from "./EvmJsonRPCBlockRepository";
|
||||
|
||||
const FINALIZED = "finalized";
|
||||
const ETHEREUM = "ethereum";
|
||||
|
||||
export class ArbitrumEvmJsonRPCBlockRepository extends EvmJsonRPCBlockRepository {
|
||||
override readonly logger = winston.child({ module: "ArbitrumEvmJsonRPCBlockRepository" });
|
||||
private metadataRepo: MetadataRepository<PersistedBlock[]>;
|
||||
private latestL2Finalized = 0;
|
||||
|
||||
constructor(
|
||||
cfg: EvmJsonRPCBlockRepositoryCfg,
|
||||
httpClient: HttpClient,
|
||||
metadataRepo: MetadataRepository<any>
|
||||
) {
|
||||
super(cfg, httpClient);
|
||||
this.metadataRepo = metadataRepo;
|
||||
}
|
||||
|
||||
async getBlockHeight(chain: string, finality: EvmTag): Promise<bigint> {
|
||||
const chainCfg = this.getCurrentChain(chain);
|
||||
let response: { result: BlockByNumberResult };
|
||||
|
||||
try {
|
||||
// This gets the latest L2 block so we can get the associated L1 block number
|
||||
response = await this.httpClient.post<typeof response>(
|
||||
chainCfg.rpc.href,
|
||||
{
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
method: "eth_getBlockByNumber",
|
||||
params: [finality, false],
|
||||
},
|
||||
{ timeout: chainCfg.timeout, retries: chainCfg.retries }
|
||||
);
|
||||
} catch (e: HttpClientError | any) {
|
||||
this.handleError(chain, e, "getBlockHeight", "eth_getBlockByNumber");
|
||||
throw e;
|
||||
}
|
||||
|
||||
const l2Logs = response.result;
|
||||
const l1BlockNumber = l2Logs.l1BlockNumber;
|
||||
const l2Number = l2Logs.number;
|
||||
|
||||
if (!l2Logs || !l1BlockNumber || !l2Number)
|
||||
throw new Error(`[getBlockHeight] Unable to parse result for latest block on ${chain}`);
|
||||
|
||||
const associatedL1Block: number = parseInt(l1BlockNumber, 16);
|
||||
const l2BlockNumber: number = parseInt(l2Number, 16);
|
||||
|
||||
const persistedBlocks: PersistedBlock[] | undefined = await this.metadataRepo.get(
|
||||
`arbitrum-${finality}`
|
||||
);
|
||||
const auxPersistedBlocks = this.removeDuplicates(persistedBlocks);
|
||||
|
||||
// Only update the persisted block list, if the L2 block number is newer
|
||||
this.saveAssociatedL1Block(auxPersistedBlocks, associatedL1Block, l2BlockNumber);
|
||||
|
||||
// Get the latest finalized L1 block number
|
||||
const latestL1BlockNumber: bigint = await super.getBlockHeight(ETHEREUM, FINALIZED);
|
||||
|
||||
// Search in the persisted list looking for finalized L2 block number
|
||||
this.searchFinalizedBlock(auxPersistedBlocks, latestL1BlockNumber);
|
||||
|
||||
await this.metadataRepo.save(`arbitrum-${finality}`, [...auxPersistedBlocks]);
|
||||
|
||||
const latestL2FinalizedToBigInt = this.latestL2Finalized;
|
||||
return BigInt(latestL2FinalizedToBigInt);
|
||||
}
|
||||
|
||||
private removeDuplicates(persistedBlocks: PersistedBlock[] | undefined): PersistedBlock[] {
|
||||
const uniqueObjects = new Set();
|
||||
|
||||
return (
|
||||
persistedBlocks?.filter((obj) => {
|
||||
const key = JSON.stringify(obj);
|
||||
return !uniqueObjects.has(key) && uniqueObjects.add(key);
|
||||
}) ?? []
|
||||
);
|
||||
}
|
||||
|
||||
private saveAssociatedL1Block(
|
||||
auxPersistedBlocks: PersistedBlock[],
|
||||
associatedL1Block: number,
|
||||
l2BlockNumber: number
|
||||
): void {
|
||||
const findAssociatedL1Block = auxPersistedBlocks.find(
|
||||
(block) => block.associatedL1Block == associatedL1Block
|
||||
)?.associatedL1Block;
|
||||
|
||||
if (!findAssociatedL1Block || findAssociatedL1Block < l2BlockNumber) {
|
||||
auxPersistedBlocks.push({ associatedL1Block, l2BlockNumber });
|
||||
}
|
||||
}
|
||||
|
||||
private searchFinalizedBlock(
|
||||
auxPersistedBlocks: PersistedBlock[],
|
||||
latestL1BlockNumber: bigint
|
||||
): void {
|
||||
const latestL1BlockNumberToNumber = Number(latestL1BlockNumber);
|
||||
|
||||
for (let index = auxPersistedBlocks.length - 1; index >= 0; index--) {
|
||||
const associatedL1Block = auxPersistedBlocks[index].associatedL1Block;
|
||||
|
||||
if (associatedL1Block <= latestL1BlockNumberToNumber) {
|
||||
const l2BlockNumber = auxPersistedBlocks[index].l2BlockNumber;
|
||||
this.latestL2Finalized = l2BlockNumber;
|
||||
auxPersistedBlocks.splice(index, 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
type PersistedBlock = {
|
||||
associatedL1Block: number;
|
||||
l2BlockNumber: number;
|
||||
};
|
||||
|
||||
type BlockByNumberResult = {
|
||||
baseFeePerGas: string;
|
||||
difficulty: string;
|
||||
extraData: string;
|
||||
gasLimit: string;
|
||||
gasUsed: string;
|
||||
hash: string;
|
||||
l1BlockNumber: string;
|
||||
logsBloom: string;
|
||||
miner: string;
|
||||
mixHash: string;
|
||||
nonce: string;
|
||||
number: string;
|
||||
parentHash: string;
|
||||
receiptsRoot: string;
|
||||
sendCount: string;
|
||||
sendRoot: string;
|
||||
sha3Uncles: string;
|
||||
size: string;
|
||||
stateRoot: string;
|
||||
timestamp: string;
|
||||
totalDifficulty: string;
|
||||
transactions: string[];
|
||||
transactionsRoot: string;
|
||||
uncles: string[];
|
||||
};
|
|
@ -13,9 +13,9 @@ import { ChainRPCConfig } from "../../config";
|
|||
const HEXADECIMAL_PREFIX = "0x";
|
||||
|
||||
export class EvmJsonRPCBlockRepository implements EvmBlockRepository {
|
||||
private httpClient: HttpClient;
|
||||
protected httpClient: HttpClient;
|
||||
private cfg: EvmJsonRPCBlockRepositoryCfg;
|
||||
private readonly logger;
|
||||
protected readonly logger;
|
||||
|
||||
constructor(cfg: EvmJsonRPCBlockRepositoryCfg, httpClient: HttpClient) {
|
||||
this.httpClient = httpClient;
|
||||
|
@ -59,7 +59,7 @@ export class EvmJsonRPCBlockRepository implements EvmBlockRepository {
|
|||
retries: chainCfg.retries,
|
||||
});
|
||||
} catch (e: HttpClientError | any) {
|
||||
this.handleError(chain, e, "eth_getBlockByNumber");
|
||||
this.handleError(chain, e, "getBlocks", "eth_getBlockByNumber");
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
@ -145,7 +145,7 @@ export class EvmJsonRPCBlockRepository implements EvmBlockRepository {
|
|||
{ timeout: chainCfg.timeout, retries: chainCfg.retries }
|
||||
);
|
||||
} catch (e: HttpClientError | any) {
|
||||
this.handleError(chain, e, "eth_getLogs");
|
||||
this.handleError(chain, e, "getFilteredLogs", "eth_getLogs");
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
@ -188,7 +188,7 @@ export class EvmJsonRPCBlockRepository implements EvmBlockRepository {
|
|||
{ timeout: chainCfg.timeout, retries: chainCfg.retries }
|
||||
);
|
||||
} catch (e: HttpClientError | any) {
|
||||
this.handleError(chain, e, "eth_getBlockByNumber");
|
||||
this.handleError(chain, e, "getBlock", "eth_getBlockByNumber");
|
||||
throw e;
|
||||
}
|
||||
|
||||
|
@ -207,22 +207,22 @@ export class EvmJsonRPCBlockRepository implements EvmBlockRepository {
|
|||
);
|
||||
}
|
||||
|
||||
private handleError(chain: string, e: any, method: string) {
|
||||
protected handleError(chain: string, e: any, method: string, apiMethod: string) {
|
||||
const chainCfg = this.getCurrentChain(chain);
|
||||
if (e instanceof HttpClientError) {
|
||||
this.logger.error(
|
||||
`[${chain}][getBlock] Got ${e.status} from ${chainCfg.rpc.hostname}/${method}. ${
|
||||
`[${chain}][${method}] Got ${e.status} from ${chainCfg.rpc.hostname}/${apiMethod}. ${
|
||||
e?.message ?? `${e?.message}`
|
||||
}`
|
||||
);
|
||||
} else {
|
||||
this.logger.error(
|
||||
`[${chain}][getBlock] Got error ${e} from ${chainCfg.rpc.hostname}/${method}`
|
||||
`[${chain}][${method}] Got error ${e} from ${chainCfg.rpc.hostname}/${apiMethod}`
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
private getCurrentChain(chain: string) {
|
||||
protected getCurrentChain(chain: string) {
|
||||
const cfg = this.cfg.chains[chain];
|
||||
return {
|
||||
chainId: cfg.chainId,
|
||||
|
|
|
@ -11,6 +11,7 @@ export * from "./FileMetadataRepository";
|
|||
export * from "./SnsEventRepository";
|
||||
export * from "./evm/EvmJsonRPCBlockRepository";
|
||||
export * from "./evm/BscEvmJsonRPCBlockRepository";
|
||||
export * from "./evm/ArbitrumEvmJsonRPCBlockRepository";
|
||||
export * from "./PromStatRepository";
|
||||
export * from "./StaticJobRepository";
|
||||
export * from "./solana/Web3SolanaSlotRepository";
|
||||
|
|
|
@ -0,0 +1,176 @@
|
|||
import { describe, it, expect, afterEach, afterAll, jest } from "@jest/globals";
|
||||
import { ArbitrumEvmJsonRPCBlockRepository } from "../../../src/infrastructure/repositories";
|
||||
import { MetadataRepository } from "../../../src/domain/repositories";
|
||||
import { HttpClient } from "../../../src/infrastructure/rpc/http/HttpClient";
|
||||
import { EvmTag } from "../../../src/domain/entities/evm";
|
||||
import axios from "axios";
|
||||
import nock from "nock";
|
||||
import fs from "fs";
|
||||
|
||||
const dirPath = "./metadata-repo";
|
||||
axios.defaults.adapter = "http"; // needed by nock
|
||||
const ethereum = "ethereum";
|
||||
const arbitrum = "arbitrum";
|
||||
const rpc = "http://localhost";
|
||||
|
||||
let repo: ArbitrumEvmJsonRPCBlockRepository;
|
||||
let metadataSaveSpy: jest.SpiedFunction<MetadataRepository<PersistedBlock[]>["save"]>;
|
||||
let metadataGetSpy: jest.SpiedFunction<MetadataRepository<PersistedBlock[]>["get"]>;
|
||||
let metadataRepo: MetadataRepository<PersistedBlock[]>;
|
||||
|
||||
describe("ArbitrumEvmJsonRPCBlockRepository", () => {
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
if (!fs.existsSync(dirPath)) {
|
||||
fs.mkdirSync(dirPath);
|
||||
}
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nock.cleanAll();
|
||||
fs.rm(dirPath, () => {});
|
||||
});
|
||||
|
||||
it("should be able to get block height with arbitrum latest commitment and eth finalized commitment", async () => {
|
||||
// Given
|
||||
const originalBlock = 19808090n;
|
||||
const expectedBlock = 157542621n;
|
||||
|
||||
givenARepo();
|
||||
givenL2Block("latest");
|
||||
givenBlockHeightIs(originalBlock, "finalized");
|
||||
|
||||
// When
|
||||
const result = await repo.getBlockHeight(arbitrum, "latest");
|
||||
|
||||
// Then
|
||||
expect(result).toBe(expectedBlock);
|
||||
|
||||
const fileExists = fs.existsSync(`${dirPath}/arbitrum-latest.json`);
|
||||
expect(fileExists).toBe(true);
|
||||
});
|
||||
|
||||
it("should be throw error because unable to parse empty result for latest block", async () => {
|
||||
// Given
|
||||
givenARepo();
|
||||
nock(rpc)
|
||||
.post("/", {
|
||||
jsonrpc: "2.0",
|
||||
method: "eth_getBlockByNumber",
|
||||
params: ["latest", false],
|
||||
id: 1,
|
||||
})
|
||||
.reply(200, {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
result: {},
|
||||
});
|
||||
|
||||
try {
|
||||
// When
|
||||
await repo.getBlockHeight(arbitrum, "latest");
|
||||
} catch (e: Error | any) {
|
||||
// Then
|
||||
expect(e).toBeInstanceOf(Error);
|
||||
}
|
||||
});
|
||||
});
|
||||
|
||||
const givenARepo = () => {
|
||||
repo = new ArbitrumEvmJsonRPCBlockRepository(
|
||||
{
|
||||
chains: {
|
||||
ethereum: { rpcs: [rpc], timeout: 100, name: ethereum, network: "mainnet", chainId: 2 },
|
||||
arbitrum: {
|
||||
rpcs: [rpc],
|
||||
timeout: 100,
|
||||
name: arbitrum,
|
||||
network: "mainnet",
|
||||
chainId: 23,
|
||||
},
|
||||
},
|
||||
},
|
||||
new HttpClient(),
|
||||
givenMetadataRepository([{ associatedL1Block: 18764852, l2BlockNumber: 157542621 }])
|
||||
);
|
||||
};
|
||||
|
||||
const givenL2Block = (commitment: EvmTag) => {
|
||||
nock(rpc)
|
||||
.post("/", {
|
||||
jsonrpc: "2.0",
|
||||
method: "eth_getBlockByNumber",
|
||||
params: [commitment, false],
|
||||
id: 1,
|
||||
})
|
||||
.reply(200, {
|
||||
jsonrpc: "2.0",
|
||||
id: 1,
|
||||
result: {
|
||||
baseFeePerGas: "0x5f5e100",
|
||||
difficulty: "0x1",
|
||||
extraData: "0xe24ff00c699874b42c1eb6a325ae6c672c502c529de9dbb24ed9ff51563ab5ec",
|
||||
gasLimit: "0x4000000000000",
|
||||
gasUsed: "0x44a679",
|
||||
hash: "0xff1461564bad17aa8b047fc819f2d167b48e172f9143725815b2ec57b5aef429",
|
||||
l1BlockNumber: "0x11dcb25",
|
||||
logsBloom:
|
||||
"0x00000002000000100000000000020000800000000002000040000000004100000000000000000000000000000000000000005000020020000000000080200000000000010000004980404008000000000202000000000000000000000000004000000020000400000000100400000000040000100000000240000010000800000000000000080000000000000000000400000000000000080000000000000110030000000202000000000400800000000000000000800001000200000080400000000002001000000080001020040000000000000000000080000000000000008010000000000000000000440000000000004000000000000000000000000000",
|
||||
miner: "0xa4b000000000000000000073657175656e636572",
|
||||
mixHash: "0x00000000000184c900000000011dcb25000000000000000a0000000000000000",
|
||||
nonce: "0x000000000012b586",
|
||||
number: "0x963e8dd",
|
||||
parentHash: "0x648eff1f5f81b60f3bd9031114d54af71a1787f3f73e62fdbcf90cc228e976e9",
|
||||
receiptsRoot: "0x25a3340b2150c6db9bab6f82b8885d7f08a0ddbf06b4a5ab765c882a869fc594",
|
||||
sendCount: "0x184c9",
|
||||
sendRoot: "0xe24ff00c699874b42c1eb6a325ae6c672c502c529de9dbb24ed9ff51563ab5ec",
|
||||
sha3Uncles: "0x1dcc4de8dec75d7aab85b567b6ccd41ad312451b948a7413f0a142fd40d49347",
|
||||
size: "0x51f",
|
||||
stateRoot: "0x3ab24e412a3954b82927e92294e42ad73051ecf95e561a57c5bf6fcbb8114cc2",
|
||||
timestamp: "0x6570deb5",
|
||||
totalDifficulty: "0x8110b95",
|
||||
transactions: [
|
||||
"0xbfd339af3216f5a029e3cb96b75f0a09ef81ad49458af64c6f621d94476de2da",
|
||||
"0x3fb701d004eae54917c73e2008ca69e96faa46e8bc9631923170a3c88b19150f",
|
||||
"0x538622dae61bdfd20ddad66a3c06e49c94cba6bdca848a9320b9486b5dc1c8ad",
|
||||
],
|
||||
transactionsRoot: "0xc9f0d07e2b5d4f0d33e28917fc9e8af0ad5e0558c7d885fd6f48034bccafff06",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
const givenMetadataRepository = (data: PersistedBlock[]) => {
|
||||
metadataRepo = {
|
||||
get: () => Promise.resolve(data),
|
||||
save: () => Promise.resolve(),
|
||||
};
|
||||
metadataGetSpy = jest.spyOn(metadataRepo, "get");
|
||||
metadataSaveSpy = jest.spyOn(metadataRepo, "save");
|
||||
return metadataRepo;
|
||||
};
|
||||
|
||||
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",
|
||||
},
|
||||
});
|
||||
};
|
||||
|
||||
type PersistedBlock = {
|
||||
associatedL1Block: number;
|
||||
l2BlockNumber: number;
|
||||
};
|
||||
|
||||
const blockHash = (blockNumber: bigint) => `0x${blockNumber.toString(16)}`;
|
|
@ -0,0 +1,69 @@
|
|||
import { describe, it, expect, afterEach, afterAll } from "@jest/globals";
|
||||
import { BscEvmJsonRPCBlockRepository } from "../../../src/infrastructure/repositories";
|
||||
import { HttpClient } from "../../../src/infrastructure/rpc/http/HttpClient";
|
||||
import { EvmTag } from "../../../src/domain/entities/evm";
|
||||
import axios from "axios";
|
||||
import nock from "nock";
|
||||
|
||||
axios.defaults.adapter = "http"; // needed by nock
|
||||
const rpc = "http://localhost";
|
||||
const bsc = "bsc";
|
||||
|
||||
let repo: BscEvmJsonRPCBlockRepository;
|
||||
|
||||
describe("BscEvmJsonRPCBlockRepository", () => {
|
||||
afterAll(() => {
|
||||
nock.restore();
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
nock.cleanAll();
|
||||
});
|
||||
|
||||
it("should be able to get block height with the latest commitment", async () => {
|
||||
// Given
|
||||
const originalBlock = 1980809n;
|
||||
const expectedBlock = 1980794n;
|
||||
|
||||
givenARepo();
|
||||
givenBlockHeightIs(originalBlock, "latest");
|
||||
|
||||
// When
|
||||
const result = await repo.getBlockHeight(bsc, "latest");
|
||||
|
||||
// Then
|
||||
expect(result).toBe(expectedBlock);
|
||||
});
|
||||
});
|
||||
|
||||
const givenARepo = () => {
|
||||
repo = new BscEvmJsonRPCBlockRepository(
|
||||
{
|
||||
chains: {
|
||||
bsc: { rpcs: [rpc], timeout: 100, name: bsc, network: "mainnet", chainId: 4 },
|
||||
},
|
||||
},
|
||||
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 blockHash = (blockNumber: bigint) => `0x${blockNumber.toString(16)}`;
|
|
@ -175,6 +175,34 @@ data:
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poll-log-message-published-arbitrum",
|
||||
"chain": "arbitrum",
|
||||
"source": {
|
||||
"action": "PollEvmLogs",
|
||||
"config": {
|
||||
"blockBatchSize": 100,
|
||||
"commitment": "latest",
|
||||
"interval": 30000,
|
||||
"addresses": ["0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"],
|
||||
"chain": "arbitrum"
|
||||
}
|
||||
},
|
||||
"handlers": [
|
||||
{
|
||||
"action": "HandleEvmLogs",
|
||||
"target": "sns",
|
||||
"mapper": "evmLogMessagePublishedMapper",
|
||||
"config": {
|
||||
"abi": "event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel)",
|
||||
"filter": {
|
||||
"addresses": ["0xC7A204bDBFe983FCD8d8E61D02b475D4073fF97e"],
|
||||
"topics": ["0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
mainnet-jobs.json: |-
|
||||
|
@ -318,6 +346,34 @@ data:
|
|||
}
|
||||
}
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "poll-log-message-published-arbitrum",
|
||||
"chain": "arbitrum",
|
||||
"source": {
|
||||
"action": "PollEvmLogs",
|
||||
"config": {
|
||||
"blockBatchSize": 100,
|
||||
"commitment": "latest",
|
||||
"interval": 30000,
|
||||
"addresses": ["0xa5f208e072434bC67592E4C49C1B991BA79BCA46"],
|
||||
"chain": "arbitrum"
|
||||
}
|
||||
},
|
||||
"handlers": [
|
||||
{
|
||||
"action": "HandleEvmLogs",
|
||||
"target": "sns",
|
||||
"mapper": "evmLogMessagePublishedMapper",
|
||||
"config": {
|
||||
"abi": "event LogMessagePublished(address indexed sender, uint64 sequence, uint32 nonce, bytes payload, uint8 consistencyLevel)",
|
||||
"filter": {
|
||||
"addresses": ["0xa5f208e072434bC67592E4C49C1B991BA79BCA46"],
|
||||
"topics": ["0x6eb224fb001ed210e379b335e35efe88672a8ce935d981a6896b27ffdf52a3b2"]
|
||||
}
|
||||
}
|
||||
}
|
||||
]
|
||||
}
|
||||
]
|
||||
---
|
||||
|
|
Loading…
Reference in New Issue