@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 +1 -0
- package/CHANGELOG.md +8 -0
- package/README.md +24 -0
- package/dist/EvmRpcNetwork.d.ts +24 -0
- package/dist/EvmRpcNetwork.d.ts.map +1 -0
- package/dist/EvmRpcNetwork.js +57 -0
- package/dist/EvmRpcScanner.d.ts +7 -0
- package/dist/EvmRpcScanner.d.ts.map +1 -0
- package/dist/EvmRpcScanner.js +8 -0
- package/dist/index.d.ts +3 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +3 -0
- package/dist/types.d.ts +4 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +6 -0
- package/lib/EvmRpcNetwork.ts +67 -0
- package/lib/EvmRpcScanner.ts +18 -0
- package/lib/index.ts +2 -0
- package/lib/types.ts +5 -0
- package/package.json +35 -0
- package/tests/EvmRpcNetwork.spec.ts +141 -0
- package/tests/TestRpcNetwork.ts +5 -0
- package/tests/mocked/JsonRpcProvider.mock.ts +47 -0
- package/tests/testData.ts +226 -0
- package/tsconfig.build.json +11 -0
- package/tsconfig.build.tsbuildinfo +1 -0
- package/tsconfig.json +11 -0
- package/vitest.config.ts +18 -0
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=
|
package/dist/index.d.ts
ADDED
|
@@ -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=
|
package/dist/types.d.ts
ADDED
|
@@ -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
package/lib/types.ts
ADDED
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,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
|
+
};
|