@rosen-bridge/evm-scanner 0.1.0-52fc0239

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/.eslintignore ADDED
@@ -0,0 +1 @@
1
+ dist
package/CHANGELOG.md ADDED
@@ -0,0 +1,8 @@
1
+ # @rosen-bridge/evm-scanner
2
+
3
+ ---
4
+
5
+ ## 0.1.0
6
+
7
+ - This package was **renamed** from `@rosen-bridge/evm-rpc-scanner`.
8
+ You can follow the previous history in the old package’s changelog. The last changelog update is available [here](https://github.com/rosen-bridge/scanner/blob/71f759d7e7ca6b6a7a60425f1848741104e1b65b/packages/scanners/evm-rpc-scanner/CHANGELOG.md).
package/README.md ADDED
@@ -0,0 +1,24 @@
1
+ # @rosen-bridge/evm-scanner
2
+
3
+ ## Table of contents
4
+
5
+ - [Introduction](#introduction)
6
+ - [Installation](#installation)
7
+
8
+ ## Introduction
9
+
10
+ An EVM chain scanner based on RPC.
11
+
12
+ ## Installation
13
+
14
+ npm:
15
+
16
+ ```sh
17
+ npm i @rosen-bridge/evm-scanner
18
+ ```
19
+
20
+ yarn:
21
+
22
+ ```sh
23
+ yarn add @rosen-bridge/evm-scanner
24
+ ```
@@ -0,0 +1,24 @@
1
+ import { AbstractNetworkConnector, Block } from '@rosen-bridge/scanner-interfaces';
2
+ import { JsonRpcProvider, TransactionResponse } from 'ethers';
3
+ export declare class EvmRpcNetwork extends AbstractNetworkConnector<TransactionResponse> {
4
+ protected readonly provider: JsonRpcProvider;
5
+ constructor(url: string, timeout?: number, authToken?: string);
6
+ /**
7
+ * Returns block at height
8
+ * @param height
9
+ * @returns Block
10
+ */
11
+ getBlockAtHeight: (height: number) => Promise<Block>;
12
+ /**
13
+ * Returns current network height
14
+ * @returns current height
15
+ */
16
+ getCurrentHeight: () => Promise<number>;
17
+ /**
18
+ * Return transactions in a block with specified hash
19
+ * @param blockHash
20
+ * @returns array of RpcTransaction
21
+ */
22
+ getBlockTxs: (blockHash: string) => Promise<Array<TransactionResponse>>;
23
+ }
24
+ //# sourceMappingURL=EvmRpcNetwork.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvmRpcNetwork.d.ts","sourceRoot":"","sources":["../lib/EvmRpcNetwork.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,wBAAwB,EACxB,KAAK,EACN,MAAM,kCAAkC,CAAC;AAE1C,OAAO,EAAE,eAAe,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE9D,qBAAa,aAAc,SAAQ,wBAAwB,CAAC,mBAAmB,CAAC;IAC9E,SAAS,CAAC,QAAQ,CAAC,QAAQ,EAAE,eAAe,CAAC;gBAEjC,GAAG,EAAE,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,EAAE,SAAS,CAAC,EAAE,MAAM;IAU7D;;;;OAIG;IACH,gBAAgB,WAAY,MAAM,KAAG,QAAQ,KAAK,CAAC,CAiBjD;IAEF;;;OAGG;IACH,gBAAgB,QAAO,QAAQ,MAAM,CAAC,CAEpC;IAEF;;;;OAIG;IACH,WAAW,cACE,MAAM,KAChB,QAAQ,MAAM,mBAAmB,CAAC,CAAC,CAMpC;CACH"}
@@ -0,0 +1,57 @@
1
+ import { AbstractNetworkConnector, } from '@rosen-bridge/scanner-interfaces';
2
+ import { BlockNotFound } from './types';
3
+ import { JsonRpcProvider } from 'ethers';
4
+ export class EvmRpcNetwork extends AbstractNetworkConnector {
5
+ provider;
6
+ constructor(url, timeout, authToken) {
7
+ super();
8
+ this.provider = authToken
9
+ ? new JsonRpcProvider(`${url}/${authToken}`)
10
+ : new JsonRpcProvider(`${url}`);
11
+ if (timeout) {
12
+ this.provider._getConnection().timeout = timeout;
13
+ }
14
+ }
15
+ /**
16
+ * Returns block at height
17
+ * @param height
18
+ * @returns Block
19
+ */
20
+ getBlockAtHeight = (height) => {
21
+ return this.provider.getBlock(height).then((block) => {
22
+ if (block == undefined) {
23
+ throw new BlockNotFound(`Block with height ${height} is not found.`);
24
+ }
25
+ if (block.hash == undefined) {
26
+ throw new Error('no block hash!');
27
+ }
28
+ return {
29
+ hash: block.hash,
30
+ height: block.number,
31
+ parentHash: block.parentHash,
32
+ timestamp: block.timestamp,
33
+ txCount: block.length,
34
+ };
35
+ });
36
+ };
37
+ /**
38
+ * Returns current network height
39
+ * @returns current height
40
+ */
41
+ getCurrentHeight = () => {
42
+ return this.provider.getBlockNumber();
43
+ };
44
+ /**
45
+ * Return transactions in a block with specified hash
46
+ * @param blockHash
47
+ * @returns array of RpcTransaction
48
+ */
49
+ getBlockTxs = async (blockHash) => {
50
+ const block = await this.provider.getBlock(blockHash, true);
51
+ if (block == undefined) {
52
+ throw new BlockNotFound(`Block with hash ${blockHash} is not found.`);
53
+ }
54
+ return block.prefetchedTransactions;
55
+ };
56
+ }
57
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZtUnBjTmV0d29yay5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL2xpYi9Fdm1ScGNOZXR3b3JrLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFDTCx3QkFBd0IsR0FFekIsTUFBTSxrQ0FBa0MsQ0FBQztBQUMxQyxPQUFPLEVBQUUsYUFBYSxFQUFFLE1BQU0sU0FBUyxDQUFDO0FBQ3hDLE9BQU8sRUFBRSxlQUFlLEVBQXVCLE1BQU0sUUFBUSxDQUFDO0FBRTlELE1BQU0sT0FBTyxhQUFjLFNBQVEsd0JBQTZDO0lBQzNELFFBQVEsQ0FBa0I7SUFFN0MsWUFBWSxHQUFXLEVBQUUsT0FBZ0IsRUFBRSxTQUFrQjtRQUMzRCxLQUFLLEVBQUUsQ0FBQztRQUNSLElBQUksQ0FBQyxRQUFRLEdBQUcsU0FBUztZQUN2QixDQUFDLENBQUMsSUFBSSxlQUFlLENBQUMsR0FBRyxHQUFHLElBQUksU0FBUyxFQUFFLENBQUM7WUFDNUMsQ0FBQyxDQUFDLElBQUksZUFBZSxDQUFDLEdBQUcsR0FBRyxFQUFFLENBQUMsQ0FBQztRQUNsQyxJQUFJLE9BQU8sRUFBRSxDQUFDO1lBQ1osSUFBSSxDQUFDLFFBQVEsQ0FBQyxjQUFjLEVBQUUsQ0FBQyxPQUFPLEdBQUcsT0FBTyxDQUFDO1FBQ25ELENBQUM7SUFDSCxDQUFDO0lBRUQ7Ozs7T0FJRztJQUNILGdCQUFnQixHQUFHLENBQUMsTUFBYyxFQUFrQixFQUFFO1FBQ3BELE9BQU8sSUFBSSxDQUFDLFFBQVEsQ0FBQyxRQUFRLENBQUMsTUFBTSxDQUFDLENBQUMsSUFBSSxDQUFDLENBQUMsS0FBSyxFQUFFLEVBQUU7WUFDbkQsSUFBSSxLQUFLLElBQUksU0FBUyxFQUFFLENBQUM7Z0JBQ3ZCLE1BQU0sSUFBSSxhQUFhLENBQUMscUJBQXFCLE1BQU0sZ0JBQWdCLENBQUMsQ0FBQztZQUN2RSxDQUFDO1lBQ0QsSUFBSSxLQUFLLENBQUMsSUFBSSxJQUFJLFNBQVMsRUFBRSxDQUFDO2dCQUM1QixNQUFNLElBQUksS0FBSyxDQUFDLGdCQUFnQixDQUFDLENBQUM7WUFDcEMsQ0FBQztZQUVELE9BQU87Z0JBQ0wsSUFBSSxFQUFFLEtBQUssQ0FBQyxJQUFJO2dCQUNoQixNQUFNLEVBQUUsS0FBSyxDQUFDLE1BQU07Z0JBQ3BCLFVBQVUsRUFBRSxLQUFLLENBQUMsVUFBVTtnQkFDNUIsU0FBUyxFQUFFLEtBQUssQ0FBQyxTQUFTO2dCQUMxQixPQUFPLEVBQUUsS0FBSyxDQUFDLE1BQU07YUFDdEIsQ0FBQztRQUNKLENBQUMsQ0FBQyxDQUFDO0lBQ0wsQ0FBQyxDQUFDO0lBRUY7OztPQUdHO0lBQ0gsZ0JBQWdCLEdBQUcsR0FBb0IsRUFBRTtRQUN2QyxPQUFPLElBQUksQ0FBQyxRQUFRLENBQUMsY0FBYyxFQUFFLENBQUM7SUFDeEMsQ0FBQyxDQUFDO0lBRUY7Ozs7T0FJRztJQUNILFdBQVcsR0FBRyxLQUFLLEVBQ2pCLFNBQWlCLEVBQ29CLEVBQUU7UUFDdkMsTUFBTSxLQUFLLEdBQUcsTUFBTSxJQUFJLENBQUMsUUFBUSxDQUFDLFFBQVEsQ0FBQyxTQUFTLEVBQUUsSUFBSSxDQUFDLENBQUM7UUFDNUQsSUFBSSxLQUFLLElBQUksU0FBUyxFQUFFLENBQUM7WUFDdkIsTUFBTSxJQUFJLGFBQWEsQ0FBQyxtQkFBbUIsU0FBUyxnQkFBZ0IsQ0FBQyxDQUFDO1FBQ3hFLENBQUM7UUFDRCxPQUFPLEtBQUssQ0FBQyxzQkFBc0IsQ0FBQztJQUN0QyxDQUFDLENBQUM7Q0FDSCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7XG4gIEFic3RyYWN0TmV0d29ya0Nvbm5lY3RvcixcbiAgQmxvY2ssXG59IGZyb20gJ0Byb3Nlbi1icmlkZ2Uvc2Nhbm5lci1pbnRlcmZhY2VzJztcbmltcG9ydCB7IEJsb2NrTm90Rm91bmQgfSBmcm9tICcuL3R5cGVzJztcbmltcG9ydCB7IEpzb25ScGNQcm92aWRlciwgVHJhbnNhY3Rpb25SZXNwb25zZSB9IGZyb20gJ2V0aGVycyc7XG5cbmV4cG9ydCBjbGFzcyBFdm1ScGNOZXR3b3JrIGV4dGVuZHMgQWJzdHJhY3ROZXR3b3JrQ29ubmVjdG9yPFRyYW5zYWN0aW9uUmVzcG9uc2U+IHtcbiAgcHJvdGVjdGVkIHJlYWRvbmx5IHByb3ZpZGVyOiBKc29uUnBjUHJvdmlkZXI7XG5cbiAgY29uc3RydWN0b3IodXJsOiBzdHJpbmcsIHRpbWVvdXQ/OiBudW1iZXIsIGF1dGhUb2tlbj86IHN0cmluZykge1xuICAgIHN1cGVyKCk7XG4gICAgdGhpcy5wcm92aWRlciA9IGF1dGhUb2tlblxuICAgICAgPyBuZXcgSnNvblJwY1Byb3ZpZGVyKGAke3VybH0vJHthdXRoVG9rZW59YClcbiAgICAgIDogbmV3IEpzb25ScGNQcm92aWRlcihgJHt1cmx9YCk7XG4gICAgaWYgKHRpbWVvdXQpIHtcbiAgICAgIHRoaXMucHJvdmlkZXIuX2dldENvbm5lY3Rpb24oKS50aW1lb3V0ID0gdGltZW91dDtcbiAgICB9XG4gIH1cblxuICAvKipcbiAgICogUmV0dXJucyBibG9jayBhdCBoZWlnaHRcbiAgICogQHBhcmFtIGhlaWdodFxuICAgKiBAcmV0dXJucyBCbG9ja1xuICAgKi9cbiAgZ2V0QmxvY2tBdEhlaWdodCA9IChoZWlnaHQ6IG51bWJlcik6IFByb21pc2U8QmxvY2s+ID0+IHtcbiAgICByZXR1cm4gdGhpcy5wcm92aWRlci5nZXRCbG9jayhoZWlnaHQpLnRoZW4oKGJsb2NrKSA9PiB7XG4gICAgICBpZiAoYmxvY2sgPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBCbG9ja05vdEZvdW5kKGBCbG9jayB3aXRoIGhlaWdodCAke2hlaWdodH0gaXMgbm90IGZvdW5kLmApO1xuICAgICAgfVxuICAgICAgaWYgKGJsb2NrLmhhc2ggPT0gdW5kZWZpbmVkKSB7XG4gICAgICAgIHRocm93IG5ldyBFcnJvcignbm8gYmxvY2sgaGFzaCEnKTtcbiAgICAgIH1cblxuICAgICAgcmV0dXJuIHtcbiAgICAgICAgaGFzaDogYmxvY2suaGFzaCxcbiAgICAgICAgaGVpZ2h0OiBibG9jay5udW1iZXIsXG4gICAgICAgIHBhcmVudEhhc2g6IGJsb2NrLnBhcmVudEhhc2gsXG4gICAgICAgIHRpbWVzdGFtcDogYmxvY2sudGltZXN0YW1wLFxuICAgICAgICB0eENvdW50OiBibG9jay5sZW5ndGgsXG4gICAgICB9O1xuICAgIH0pO1xuICB9O1xuXG4gIC8qKlxuICAgKiBSZXR1cm5zIGN1cnJlbnQgbmV0d29yayBoZWlnaHRcbiAgICogQHJldHVybnMgY3VycmVudCBoZWlnaHRcbiAgICovXG4gIGdldEN1cnJlbnRIZWlnaHQgPSAoKTogUHJvbWlzZTxudW1iZXI+ID0+IHtcbiAgICByZXR1cm4gdGhpcy5wcm92aWRlci5nZXRCbG9ja051bWJlcigpO1xuICB9O1xuXG4gIC8qKlxuICAgKiBSZXR1cm4gdHJhbnNhY3Rpb25zIGluIGEgYmxvY2sgd2l0aCBzcGVjaWZpZWQgaGFzaFxuICAgKiBAcGFyYW0gYmxvY2tIYXNoXG4gICAqIEByZXR1cm5zIGFycmF5IG9mIFJwY1RyYW5zYWN0aW9uXG4gICAqL1xuICBnZXRCbG9ja1R4cyA9IGFzeW5jIChcbiAgICBibG9ja0hhc2g6IHN0cmluZ1xuICApOiBQcm9taXNlPEFycmF5PFRyYW5zYWN0aW9uUmVzcG9uc2U+PiA9PiB7XG4gICAgY29uc3QgYmxvY2sgPSBhd2FpdCB0aGlzLnByb3ZpZGVyLmdldEJsb2NrKGJsb2NrSGFzaCwgdHJ1ZSk7XG4gICAgaWYgKGJsb2NrID09IHVuZGVmaW5lZCkge1xuICAgICAgdGhyb3cgbmV3IEJsb2NrTm90Rm91bmQoYEJsb2NrIHdpdGggaGFzaCAke2Jsb2NrSGFzaH0gaXMgbm90IGZvdW5kLmApO1xuICAgIH1cbiAgICByZXR1cm4gYmxvY2sucHJlZmV0Y2hlZFRyYW5zYWN0aW9ucztcbiAgfTtcbn1cbiJdfQ==
@@ -0,0 +1,7 @@
1
+ import { GeneralScanner, ScannerConfig } from '@rosen-bridge/abstract-scanner';
2
+ import { TransactionResponse } from 'ethers';
3
+ export declare class EvmRpcScanner extends GeneralScanner<TransactionResponse> {
4
+ readonly chain: string;
5
+ constructor(chain: string, config: ScannerConfig<TransactionResponse>);
6
+ }
7
+ //# sourceMappingURL=EvmRpcScanner.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"EvmRpcScanner.d.ts","sourceRoot":"","sources":["../lib/EvmRpcScanner.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,aAAa,EAAE,MAAM,gCAAgC,CAAC;AAC/E,OAAO,EAAE,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE7C,qBAAa,aAAc,SAAQ,cAAc,CAAC,mBAAmB,CAAC;IACpE,QAAQ,CAAC,KAAK,EAAE,MAAM,CAAC;gBAEX,KAAK,EAAE,MAAM,EAAE,MAAM,EAAE,aAAa,CAAC,mBAAmB,CAAC;CAWtE"}
@@ -0,0 +1,8 @@
1
+ import { GeneralScanner } from '@rosen-bridge/abstract-scanner';
2
+ export class EvmRpcScanner extends GeneralScanner {
3
+ chain;
4
+ constructor(chain, config) {
5
+ super(`${chain}`, config.dataSource, config.initialHeight, config.network, config.blockRetrieveGap, config.logger, config.suffix);
6
+ }
7
+ }
8
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZtUnBjU2Nhbm5lci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL2xpYi9Fdm1ScGNTY2FubmVyLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUFBLE9BQU8sRUFBRSxjQUFjLEVBQWlCLE1BQU0sZ0NBQWdDLENBQUM7QUFHL0UsTUFBTSxPQUFPLGFBQWMsU0FBUSxjQUFtQztJQUMzRCxLQUFLLENBQVM7SUFFdkIsWUFBWSxLQUFhLEVBQUUsTUFBMEM7UUFDbkUsS0FBSyxDQUNILEdBQUcsS0FBSyxFQUFFLEVBQ1YsTUFBTSxDQUFDLFVBQVUsRUFDakIsTUFBTSxDQUFDLGFBQWEsRUFDcEIsTUFBTSxDQUFDLE9BQU8sRUFDZCxNQUFNLENBQUMsZ0JBQWdCLEVBQ3ZCLE1BQU0sQ0FBQyxNQUFNLEVBQ2IsTUFBTSxDQUFDLE1BQU0sQ0FDZCxDQUFDO0lBQ0osQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiaW1wb3J0IHsgR2VuZXJhbFNjYW5uZXIsIFNjYW5uZXJDb25maWcgfSBmcm9tICdAcm9zZW4tYnJpZGdlL2Fic3RyYWN0LXNjYW5uZXInO1xuaW1wb3J0IHsgVHJhbnNhY3Rpb25SZXNwb25zZSB9IGZyb20gJ2V0aGVycyc7XG5cbmV4cG9ydCBjbGFzcyBFdm1ScGNTY2FubmVyIGV4dGVuZHMgR2VuZXJhbFNjYW5uZXI8VHJhbnNhY3Rpb25SZXNwb25zZT4ge1xuICByZWFkb25seSBjaGFpbjogc3RyaW5nO1xuXG4gIGNvbnN0cnVjdG9yKGNoYWluOiBzdHJpbmcsIGNvbmZpZzogU2Nhbm5lckNvbmZpZzxUcmFuc2FjdGlvblJlc3BvbnNlPikge1xuICAgIHN1cGVyKFxuICAgICAgYCR7Y2hhaW59YCxcbiAgICAgIGNvbmZpZy5kYXRhU291cmNlLFxuICAgICAgY29uZmlnLmluaXRpYWxIZWlnaHQsXG4gICAgICBjb25maWcubmV0d29yayxcbiAgICAgIGNvbmZpZy5ibG9ja1JldHJpZXZlR2FwLFxuICAgICAgY29uZmlnLmxvZ2dlcixcbiAgICAgIGNvbmZpZy5zdWZmaXhcbiAgICApO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,3 @@
1
+ export { EvmRpcScanner } from './EvmRpcScanner';
2
+ export { EvmRpcNetwork } from './EvmRpcNetwork';
3
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC;AAChD,OAAO,EAAE,aAAa,EAAE,MAAM,iBAAiB,CAAC"}
package/dist/index.js ADDED
@@ -0,0 +1,3 @@
1
+ export { EvmRpcScanner } from './EvmRpcScanner';
2
+ export { EvmRpcNetwork } from './EvmRpcNetwork';
3
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUFFLGFBQWEsRUFBRSxNQUFNLGlCQUFpQixDQUFDO0FBQ2hELE9BQU8sRUFBRSxhQUFhLEVBQUUsTUFBTSxpQkFBaUIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCB7IEV2bVJwY1NjYW5uZXIgfSBmcm9tICcuL0V2bVJwY1NjYW5uZXInO1xuZXhwb3J0IHsgRXZtUnBjTmV0d29yayB9IGZyb20gJy4vRXZtUnBjTmV0d29yayc7XG4iXX0=
@@ -0,0 +1,4 @@
1
+ export declare class BlockNotFound extends Error {
2
+ constructor(msg: string);
3
+ }
4
+ //# sourceMappingURL=types.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../lib/types.ts"],"names":[],"mappings":"AAAA,qBAAa,aAAc,SAAQ,KAAK;gBAC1B,GAAG,EAAE,MAAM;CAGxB"}
package/dist/types.js ADDED
@@ -0,0 +1,6 @@
1
+ export class BlockNotFound extends Error {
2
+ constructor(msg) {
3
+ super('BlockNotFound: ' + msg);
4
+ }
5
+ }
6
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoidHlwZXMuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvdHlwZXMudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsTUFBTSxPQUFPLGFBQWMsU0FBUSxLQUFLO0lBQ3RDLFlBQVksR0FBVztRQUNyQixLQUFLLENBQUMsaUJBQWlCLEdBQUcsR0FBRyxDQUFDLENBQUM7SUFDakMsQ0FBQztDQUNGIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0IGNsYXNzIEJsb2NrTm90Rm91bmQgZXh0ZW5kcyBFcnJvciB7XG4gIGNvbnN0cnVjdG9yKG1zZzogc3RyaW5nKSB7XG4gICAgc3VwZXIoJ0Jsb2NrTm90Rm91bmQ6ICcgKyBtc2cpO1xuICB9XG59XG4iXX0=
@@ -0,0 +1,67 @@
1
+ import {
2
+ AbstractNetworkConnector,
3
+ Block,
4
+ } from '@rosen-bridge/scanner-interfaces';
5
+ import { BlockNotFound } from './types';
6
+ import { JsonRpcProvider, TransactionResponse } from 'ethers';
7
+
8
+ export class EvmRpcNetwork extends AbstractNetworkConnector<TransactionResponse> {
9
+ protected readonly provider: JsonRpcProvider;
10
+
11
+ constructor(url: string, timeout?: number, authToken?: string) {
12
+ super();
13
+ this.provider = authToken
14
+ ? new JsonRpcProvider(`${url}/${authToken}`)
15
+ : new JsonRpcProvider(`${url}`);
16
+ if (timeout) {
17
+ this.provider._getConnection().timeout = timeout;
18
+ }
19
+ }
20
+
21
+ /**
22
+ * Returns block at height
23
+ * @param height
24
+ * @returns Block
25
+ */
26
+ getBlockAtHeight = (height: number): Promise<Block> => {
27
+ return this.provider.getBlock(height).then((block) => {
28
+ if (block == undefined) {
29
+ throw new BlockNotFound(`Block with height ${height} is not found.`);
30
+ }
31
+ if (block.hash == undefined) {
32
+ throw new Error('no block hash!');
33
+ }
34
+
35
+ return {
36
+ hash: block.hash,
37
+ height: block.number,
38
+ parentHash: block.parentHash,
39
+ timestamp: block.timestamp,
40
+ txCount: block.length,
41
+ };
42
+ });
43
+ };
44
+
45
+ /**
46
+ * Returns current network height
47
+ * @returns current height
48
+ */
49
+ getCurrentHeight = (): Promise<number> => {
50
+ return this.provider.getBlockNumber();
51
+ };
52
+
53
+ /**
54
+ * Return transactions in a block with specified hash
55
+ * @param blockHash
56
+ * @returns array of RpcTransaction
57
+ */
58
+ getBlockTxs = async (
59
+ blockHash: string
60
+ ): Promise<Array<TransactionResponse>> => {
61
+ const block = await this.provider.getBlock(blockHash, true);
62
+ if (block == undefined) {
63
+ throw new BlockNotFound(`Block with hash ${blockHash} is not found.`);
64
+ }
65
+ return block.prefetchedTransactions;
66
+ };
67
+ }
@@ -0,0 +1,18 @@
1
+ import { GeneralScanner, ScannerConfig } from '@rosen-bridge/abstract-scanner';
2
+ import { TransactionResponse } from 'ethers';
3
+
4
+ export class EvmRpcScanner extends GeneralScanner<TransactionResponse> {
5
+ readonly chain: string;
6
+
7
+ constructor(chain: string, config: ScannerConfig<TransactionResponse>) {
8
+ super(
9
+ `${chain}`,
10
+ config.dataSource,
11
+ config.initialHeight,
12
+ config.network,
13
+ config.blockRetrieveGap,
14
+ config.logger,
15
+ config.suffix
16
+ );
17
+ }
18
+ }
package/lib/index.ts ADDED
@@ -0,0 +1,2 @@
1
+ export { EvmRpcScanner } from './EvmRpcScanner';
2
+ export { EvmRpcNetwork } from './EvmRpcNetwork';
package/lib/types.ts ADDED
@@ -0,0 +1,5 @@
1
+ export class BlockNotFound extends Error {
2
+ constructor(msg: string) {
3
+ super('BlockNotFound: ' + msg);
4
+ }
5
+ }
package/package.json ADDED
@@ -0,0 +1,35 @@
1
+ {
2
+ "name": "@rosen-bridge/evm-scanner",
3
+ "version": "0.1.0-52fc0239",
4
+ "description": "An EVM chain scanner based on RPC.",
5
+ "repository": "https://github.com/rosen-bridge/scanner",
6
+ "license": "MIT",
7
+ "author": "Rosen Team",
8
+ "type": "module",
9
+ "main": "dist/index.js",
10
+ "types": "dist/index.d.ts",
11
+ "scripts": {
12
+ "build": "tsc --build tsconfig.build.json",
13
+ "coverage": "npm run test -- --coverage",
14
+ "lint": "eslint --fix . && npm run prettify",
15
+ "prettify": "prettier --write . --ignore-path ./.gitignore",
16
+ "release": "npm run build && npm publish --access public",
17
+ "test": "NODE_OPTIONS=--loader=extensionless vitest",
18
+ "type-check": "tsc --noEmit"
19
+ },
20
+ "devDependencies": {
21
+ "@types/node": "^22.18.0",
22
+ "@vitest/coverage-istanbul": "^1.2.2",
23
+ "extensionless": "^1.9.6",
24
+ "typescript": "^5.3.3",
25
+ "vitest": "^1.2.2"
26
+ },
27
+ "dependencies": {
28
+ "@rosen-bridge/abstract-scanner": "^0.1.0-52fc0239",
29
+ "@rosen-bridge/scanner-interfaces": "^0.1.1-52fc0239",
30
+ "ethers": "6.13.2"
31
+ },
32
+ "engines": {
33
+ "node": ">=22.18.0"
34
+ }
35
+ }
@@ -0,0 +1,141 @@
1
+ import { Transaction } from 'ethers';
2
+ import * as testData from './testData';
3
+ import { TestEvmRpcNetwork } from './TestRpcNetwork';
4
+ import {
5
+ mockGetBlockNumber,
6
+ mockGetBlock,
7
+ resetRpcMock,
8
+ } from './mocked/JsonRpcProvider.mock';
9
+ import { BlockNotFound } from '../lib/types';
10
+
11
+ describe('EvmRpcNetwork', () => {
12
+ let network: TestEvmRpcNetwork;
13
+
14
+ beforeEach(() => {
15
+ resetRpcMock();
16
+ network = new TestEvmRpcNetwork('', 1);
17
+ });
18
+
19
+ describe('getCurrentHeight', () => {
20
+ /**
21
+ * @target `EvmRpcNetwork.getHeight` should return block height successfully
22
+ * @dependencies
23
+ * @scenario
24
+ * - mock `RPC.getBlockNumber`
25
+ * - run test
26
+ * - check returned value
27
+ * @expected
28
+ * - it should be mocked block height
29
+ */
30
+ it('should return block height successfully', async () => {
31
+ // mock client response
32
+ mockGetBlockNumber(network.getProvider());
33
+
34
+ // run test
35
+ const result = await network.getCurrentHeight();
36
+
37
+ // check returned value
38
+ expect(result).toEqual(testData.blockHeight);
39
+ });
40
+ });
41
+
42
+ describe('getBlockTxs', () => {
43
+ /**
44
+ * @target `EvmRpcNetwork.getBlockTxs` should return
45
+ * transactions of the block
46
+ * @dependencies
47
+ * @scenario
48
+ * - mock `RPC.getBlock` with prefetchTxs `true`
49
+ * - run test
50
+ * - check returned value
51
+ * @expected
52
+ * - it should return transactions of the block
53
+ */
54
+ it('should return transactions of the block', async () => {
55
+ // mock client response
56
+ mockGetBlock(network.getProvider(), testData.blockInfo);
57
+
58
+ // run test
59
+ const result = await network.getBlockTxs(testData.blockHash);
60
+
61
+ // check returned value
62
+ for (let i = 0; i < result.length; i++) {
63
+ const trx = Transaction.from(result[i]);
64
+ expect(trx.toJSON()).toEqual(testData.convertedTxList[i]);
65
+ }
66
+ });
67
+
68
+ /**
69
+ * @target `EvmRpcNetwork.getBlockTxs` should throw
70
+ * error if block can not be found.
71
+ * @dependencies
72
+ * @scenario
73
+ * - mock `RPC.getBlock` with prefetchTxs `true`
74
+ * - run test
75
+ * - call the function and expect error
76
+ * @expected
77
+ * - getBlockTxs should throw BlockNotFound
78
+ */
79
+ it('should throw BlockNotFound', async () => {
80
+ // mock client response
81
+ mockGetBlock(network.getProvider(), null);
82
+
83
+ // run test
84
+ const result = network.getBlockTxs(testData.blockHash);
85
+
86
+ // check returned value
87
+ expect(result).rejects.toThrowError(BlockNotFound);
88
+ });
89
+ });
90
+
91
+ describe('getBlockAtHeight', () => {
92
+ /**
93
+ * @target `EvmRpcNetwork.getBlockInfo` should return block info
94
+ * @dependencies
95
+ * @scenario
96
+ * - mock `RPC.getBlock`
97
+ * - run test
98
+ * - check returned value
99
+ * @expected
100
+ * - it should return block info successfully
101
+ */
102
+ it('should return block info successfully', async () => {
103
+ // mock client response
104
+ mockGetBlock(network.getProvider(), testData.blockInfo);
105
+
106
+ // run test
107
+ const result = await network.getBlockAtHeight(testData.blockHeight);
108
+
109
+ // check returned value
110
+ expect(result).toEqual({
111
+ hash: testData.blockInfo.hash,
112
+ height: testData.blockInfo.number,
113
+ parentHash: testData.blockInfo.parentHash,
114
+ timestamp: testData.blockInfo.timestamp,
115
+ txCount: testData.blockInfo.length,
116
+ });
117
+ });
118
+
119
+ /**
120
+ * @target `EvmRpcNetwork.getBlockAtHeight` should throw
121
+ * error when block height is wrong
122
+ * @dependencies
123
+ * @scenario
124
+ * - mock `RPC.getBlock`
125
+ * - run test
126
+ * - call the function and expect error
127
+ * @expected
128
+ * - getBlockAtHeight should throw BlockNotFound
129
+ */
130
+ it('should throw error when block height is wrong', async () => {
131
+ // mock client response
132
+ mockGetBlock(network.getProvider(), null);
133
+
134
+ // run test
135
+ const result = network.getBlockAtHeight(testData.wrongBlockHeight);
136
+
137
+ // check returned value
138
+ expect(result).rejects.toThrow(BlockNotFound);
139
+ });
140
+ });
141
+ });
@@ -0,0 +1,5 @@
1
+ import { EvmRpcNetwork } from '../lib/EvmRpcNetwork';
2
+
3
+ export class TestEvmRpcNetwork extends EvmRpcNetwork {
4
+ getProvider = () => this.provider;
5
+ }
@@ -0,0 +1,47 @@
1
+ import { vi } from 'vitest';
2
+ import { Block, JsonRpcProvider } from 'ethers';
3
+ import * as testData from '../testData';
4
+
5
+ vi.mock('ethers', () => {
6
+ return {
7
+ JsonRpcProvider: vi.fn().mockImplementation(() => {
8
+ return rpcInstance;
9
+ }),
10
+ };
11
+ });
12
+
13
+ export const rpcInstance = {
14
+ getBlock: vi.fn(),
15
+ getBlockNumber: vi.fn(),
16
+ _getConnection: () => {
17
+ return {
18
+ timeout: 0,
19
+ };
20
+ },
21
+ };
22
+
23
+ /**
24
+ * resets rpc functions mocks and call counts
25
+ */
26
+ export const resetRpcMock = () => {
27
+ rpcInstance.getBlock.mockReset();
28
+ rpcInstance.getBlockNumber.mockReset();
29
+ };
30
+
31
+ /**
32
+ * mocks `getBlock` function of the provider to return value
33
+ * @param provider
34
+ */
35
+ export const mockGetBlock = (provider: JsonRpcProvider, data: Block | null) => {
36
+ vi.spyOn(provider, 'getBlock').mockResolvedValue(data);
37
+ };
38
+
39
+ /**
40
+ * mocks `getBlockNumber` function of the provider to return value
41
+ * @param provider
42
+ */
43
+ export const mockGetBlockNumber = (provider: JsonRpcProvider) => {
44
+ vi.spyOn(provider, 'getBlockNumber').mockResolvedValue(
45
+ testData.blockInfo.number
46
+ );
47
+ };