@rosen-bridge/evm-observation-extractor 1.0.9 → 2.0.1-08d1d81f
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/CHANGELOG.md +19 -0
- package/dist/EthereumRpcObservationExtractor.d.ts +13 -0
- package/dist/EthereumRpcObservationExtractor.d.ts.map +1 -0
- package/dist/EthereumRpcObservationExtractor.js +13 -0
- package/dist/EvmRpcObservationExtractor.d.ts +10 -10
- package/dist/EvmRpcObservationExtractor.d.ts.map +1 -1
- package/dist/EvmRpcObservationExtractor.js +48 -11
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -1
- package/lib/EthereumRpcObservationExtractor.ts +34 -0
- package/lib/EvmRpcObservationExtractor.ts +58 -27
- package/lib/index.ts +1 -0
- package/package.json +14 -5
- package/tests/EvmRpcObservationExtractor.spec.ts +160 -0
- package/tests/TestObservationExtractor.ts +8 -0
- package/tests/testData.ts +35 -0
- package/tests/utils.mock.ts +560 -0
- package/tsconfig.build.tsbuildinfo +1 -1
- package/vitest.config.ts +18 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,24 @@
|
|
|
1
1
|
# @rosen-bridge/evm-observation-extractor
|
|
2
2
|
|
|
3
|
+
## 2.0.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- update rosen-extractor version
|
|
8
|
+
- Updated dependencies
|
|
9
|
+
- @rosen-bridge/observation-extractor@5.0.1
|
|
10
|
+
|
|
11
|
+
## 2.0.0
|
|
12
|
+
|
|
13
|
+
### Major Changes
|
|
14
|
+
|
|
15
|
+
- consider decimals drop
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- Updated dependencies
|
|
20
|
+
- @rosen-bridge/observation-extractor@5.0.0
|
|
21
|
+
|
|
3
22
|
## 1.0.9
|
|
4
23
|
|
|
5
24
|
### Patch Changes
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { AbstractLogger } from '@rosen-bridge/abstract-logger';
|
|
2
|
+
import { RosenTokens } from '@rosen-bridge/tokens';
|
|
3
|
+
import { DataSource } from 'typeorm';
|
|
4
|
+
import { EvmRpcObservationExtractor } from './EvmRpcObservationExtractor';
|
|
5
|
+
export declare class EthereumRpcObservationExtractor extends EvmRpcObservationExtractor {
|
|
6
|
+
readonly FROM_CHAIN = "ethereum";
|
|
7
|
+
constructor(lockAddress: string, dataSource: DataSource, tokens: RosenTokens, logger?: AbstractLogger);
|
|
8
|
+
/**
|
|
9
|
+
* gets Id for current extractor
|
|
10
|
+
*/
|
|
11
|
+
getId: () => string;
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=EthereumRpcObservationExtractor.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"EthereumRpcObservationExtractor.d.ts","sourceRoot":"","sources":["../lib/EthereumRpcObservationExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAE/D,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AACrC,OAAO,EAAE,0BAA0B,EAAE,MAAM,8BAA8B,CAAC;AAE1E,qBAAa,+BAAgC,SAAQ,0BAA0B;IAC7E,QAAQ,CAAC,UAAU,cAAc;gBAG/B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,cAAc;IAgBzB;;OAEG;IACH,KAAK,eAAkC;CACxC"}
|
|
@@ -0,0 +1,13 @@
|
|
|
1
|
+
import { EvmEthersRosenExtractor } from '@rosen-bridge/rosen-extractor';
|
|
2
|
+
import { EvmRpcObservationExtractor } from './EvmRpcObservationExtractor';
|
|
3
|
+
export class EthereumRpcObservationExtractor extends EvmRpcObservationExtractor {
|
|
4
|
+
FROM_CHAIN = 'ethereum';
|
|
5
|
+
constructor(lockAddress, dataSource, tokens, logger) {
|
|
6
|
+
super(dataSource, tokens, new EvmEthersRosenExtractor(lockAddress, tokens, 'ethereum', 'eth', logger), logger);
|
|
7
|
+
}
|
|
8
|
+
/**
|
|
9
|
+
* gets Id for current extractor
|
|
10
|
+
*/
|
|
11
|
+
getId = () => 'ethereum-rpc-extractor';
|
|
12
|
+
}
|
|
13
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXRoZXJldW1ScGNPYnNlcnZhdGlvbkV4dHJhY3Rvci5qcyIsInNvdXJjZVJvb3QiOiIiLCJzb3VyY2VzIjpbIi4uL2xpYi9FdGhlcmV1bVJwY09ic2VydmF0aW9uRXh0cmFjdG9yLnRzIl0sIm5hbWVzIjpbXSwibWFwcGluZ3MiOiJBQUNBLE9BQU8sRUFBRSx1QkFBdUIsRUFBRSxNQUFNLCtCQUErQixDQUFDO0FBR3hFLE9BQU8sRUFBRSwwQkFBMEIsRUFBRSxNQUFNLDhCQUE4QixDQUFDO0FBRTFFLE1BQU0sT0FBTywrQkFBZ0MsU0FBUSwwQkFBMEI7SUFDcEUsVUFBVSxHQUFHLFVBQVUsQ0FBQztJQUVqQyxZQUNFLFdBQW1CLEVBQ25CLFVBQXNCLEVBQ3RCLE1BQW1CLEVBQ25CLE1BQXVCO1FBRXZCLEtBQUssQ0FDSCxVQUFVLEVBQ1YsTUFBTSxFQUNOLElBQUksdUJBQXVCLENBQ3pCLFdBQVcsRUFDWCxNQUFNLEVBQ04sVUFBVSxFQUNWLEtBQUssRUFDTCxNQUFNLENBQ1AsRUFDRCxNQUFNLENBQ1AsQ0FBQztJQUNKLENBQUM7SUFFRDs7T0FFRztJQUNILEtBQUssR0FBRyxHQUFHLEVBQUUsQ0FBQyx3QkFBd0IsQ0FBQztDQUN4QyIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IEFic3RyYWN0TG9nZ2VyIH0gZnJvbSAnQHJvc2VuLWJyaWRnZS9hYnN0cmFjdC1sb2dnZXInO1xuaW1wb3J0IHsgRXZtRXRoZXJzUm9zZW5FeHRyYWN0b3IgfSBmcm9tICdAcm9zZW4tYnJpZGdlL3Jvc2VuLWV4dHJhY3Rvcic7XG5pbXBvcnQgeyBSb3NlblRva2VucyB9IGZyb20gJ0Byb3Nlbi1icmlkZ2UvdG9rZW5zJztcbmltcG9ydCB7IERhdGFTb3VyY2UgfSBmcm9tICd0eXBlb3JtJztcbmltcG9ydCB7IEV2bVJwY09ic2VydmF0aW9uRXh0cmFjdG9yIH0gZnJvbSAnLi9Fdm1ScGNPYnNlcnZhdGlvbkV4dHJhY3Rvcic7XG5cbmV4cG9ydCBjbGFzcyBFdGhlcmV1bVJwY09ic2VydmF0aW9uRXh0cmFjdG9yIGV4dGVuZHMgRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3Ige1xuICByZWFkb25seSBGUk9NX0NIQUlOID0gJ2V0aGVyZXVtJztcblxuICBjb25zdHJ1Y3RvcihcbiAgICBsb2NrQWRkcmVzczogc3RyaW5nLFxuICAgIGRhdGFTb3VyY2U6IERhdGFTb3VyY2UsXG4gICAgdG9rZW5zOiBSb3NlblRva2VucyxcbiAgICBsb2dnZXI/OiBBYnN0cmFjdExvZ2dlclxuICApIHtcbiAgICBzdXBlcihcbiAgICAgIGRhdGFTb3VyY2UsXG4gICAgICB0b2tlbnMsXG4gICAgICBuZXcgRXZtRXRoZXJzUm9zZW5FeHRyYWN0b3IoXG4gICAgICAgIGxvY2tBZGRyZXNzLFxuICAgICAgICB0b2tlbnMsXG4gICAgICAgICdldGhlcmV1bScsXG4gICAgICAgICdldGgnLFxuICAgICAgICBsb2dnZXJcbiAgICAgICksXG4gICAgICBsb2dnZXJcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldHMgSWQgZm9yIGN1cnJlbnQgZXh0cmFjdG9yXG4gICAqL1xuICBnZXRJZCA9ICgpID0+ICdldGhlcmV1bS1ycGMtZXh0cmFjdG9yJztcbn1cbiJdfQ==
|
|
@@ -1,18 +1,18 @@
|
|
|
1
|
-
import { Transaction } from 'ethers';
|
|
2
1
|
import { AbstractObservationExtractor } from '@rosen-bridge/observation-extractor';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
6
|
-
export declare class EthereumRpcObservationExtractor extends AbstractObservationExtractor<Transaction> {
|
|
7
|
-
readonly FROM_CHAIN = "ethereum";
|
|
8
|
-
constructor(lockAddress: string, dataSource: DataSource, tokens: RosenTokens, logger?: AbstractLogger);
|
|
2
|
+
import { TransactionResponse } from 'ethers';
|
|
3
|
+
import { Block } from '@rosen-bridge/abstract-extractor';
|
|
4
|
+
export declare abstract class EvmRpcObservationExtractor extends AbstractObservationExtractor<TransactionResponse> {
|
|
9
5
|
/**
|
|
10
|
-
* gets
|
|
6
|
+
* gets block id and transactions corresponding to the block and saves if they are valid rosen
|
|
7
|
+
* transactions and in case of success return true and in case of failure returns false
|
|
8
|
+
* additionally, it returns false if the transaction is failed
|
|
9
|
+
* @param block
|
|
10
|
+
* @param txs
|
|
11
11
|
*/
|
|
12
|
-
|
|
12
|
+
processTransactions: (txs: Array<TransactionResponse>, block: Block) => Promise<boolean>;
|
|
13
13
|
/**
|
|
14
14
|
* gets transaction id from TransactionType
|
|
15
15
|
*/
|
|
16
|
-
getTxId: (tx:
|
|
16
|
+
getTxId: (tx: TransactionResponse) => string;
|
|
17
17
|
}
|
|
18
18
|
//# sourceMappingURL=EvmRpcObservationExtractor.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EvmRpcObservationExtractor.d.ts","sourceRoot":"","sources":["../lib/EvmRpcObservationExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,
|
|
1
|
+
{"version":3,"file":"EvmRpcObservationExtractor.d.ts","sourceRoot":"","sources":["../lib/EvmRpcObservationExtractor.ts"],"names":[],"mappings":"AAAA,OAAO,EACL,4BAA4B,EAE7B,MAAM,qCAAqC,CAAC;AAC7C,OAAO,EAAmB,mBAAmB,EAAE,MAAM,QAAQ,CAAC;AAE9D,OAAO,EAAE,KAAK,EAAE,MAAM,kCAAkC,CAAC;AAEzD,8BAAsB,0BAA2B,SAAQ,4BAA4B,CAAC,mBAAmB,CAAC;IACxG;;;;;;OAMG;IACH,mBAAmB,QACZ,MAAM,mBAAmB,CAAC,SACxB,KAAK,KACX,QAAQ,OAAO,CAAC,CAuCjB;IAEF;;OAEG;IACH,OAAO,OAAQ,mBAAmB,YAOhC;CACH"}
|
|
@@ -1,22 +1,59 @@
|
|
|
1
|
-
import { AbstractObservationExtractor } from '@rosen-bridge/observation-extractor';
|
|
2
|
-
import {
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
constructor(lockAddress, dataSource, tokens, logger) {
|
|
6
|
-
super(dataSource, tokens, new EvmRpcRosenExtractor(lockAddress, tokens, 'ethereum', 'eth', logger), logger);
|
|
7
|
-
}
|
|
1
|
+
import { AbstractObservationExtractor, } from '@rosen-bridge/observation-extractor';
|
|
2
|
+
import { isCallException } from 'ethers';
|
|
3
|
+
import { blake2b } from 'blakejs';
|
|
4
|
+
export class EvmRpcObservationExtractor extends AbstractObservationExtractor {
|
|
8
5
|
/**
|
|
9
|
-
* gets
|
|
6
|
+
* gets block id and transactions corresponding to the block and saves if they are valid rosen
|
|
7
|
+
* transactions and in case of success return true and in case of failure returns false
|
|
8
|
+
* additionally, it returns false if the transaction is failed
|
|
9
|
+
* @param block
|
|
10
|
+
* @param txs
|
|
10
11
|
*/
|
|
11
|
-
|
|
12
|
+
processTransactions = async (txs, block) => {
|
|
13
|
+
const observations = [];
|
|
14
|
+
for (const transaction of txs) {
|
|
15
|
+
const data = this.extractor.get(transaction);
|
|
16
|
+
if (data) {
|
|
17
|
+
try {
|
|
18
|
+
const result = await transaction.wait(0);
|
|
19
|
+
if (result) {
|
|
20
|
+
const requestId = Buffer.from(blake2b(this.getTxId(transaction), undefined, 32)).toString('hex');
|
|
21
|
+
observations.push({
|
|
22
|
+
fromChain: this.FROM_CHAIN,
|
|
23
|
+
toChain: data.toChain,
|
|
24
|
+
amount: data.amount,
|
|
25
|
+
sourceChainTokenId: data.sourceChainTokenId,
|
|
26
|
+
targetChainTokenId: data.targetChainTokenId,
|
|
27
|
+
sourceTxId: data.sourceTxId,
|
|
28
|
+
bridgeFee: data.bridgeFee,
|
|
29
|
+
networkFee: data.networkFee,
|
|
30
|
+
sourceBlockId: block.hash,
|
|
31
|
+
requestId: requestId,
|
|
32
|
+
toAddress: data.toAddress,
|
|
33
|
+
fromAddress: data.fromAddress,
|
|
34
|
+
});
|
|
35
|
+
}
|
|
36
|
+
else
|
|
37
|
+
throw Error(`Impossible behavior: Evm Tx [${transaction.hash}] is included in block [${block.hash}] but waiting resulted in null or undefined`);
|
|
38
|
+
}
|
|
39
|
+
catch (e) {
|
|
40
|
+
if (isCallException(e))
|
|
41
|
+
this.logger.debug(`found valid lock transaction [${transaction.hash}] but tx is failed`);
|
|
42
|
+
else
|
|
43
|
+
throw e;
|
|
44
|
+
}
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return this.actions.storeObservations(observations, block, this.getId());
|
|
48
|
+
};
|
|
12
49
|
/**
|
|
13
50
|
* gets transaction id from TransactionType
|
|
14
51
|
*/
|
|
15
52
|
getTxId = (tx) => {
|
|
16
53
|
if (tx.hash == null) {
|
|
17
|
-
throw Error('
|
|
54
|
+
throw Error('ImpossibleBehavior: Transactions coming from RPC have to be signed.');
|
|
18
55
|
}
|
|
19
56
|
return tx.hash;
|
|
20
57
|
};
|
|
21
58
|
}
|
|
22
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
59
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLDRCQUE0QixHQUU3QixNQUFNLHFDQUFxQyxDQUFDO0FBQzdDLE9BQU8sRUFBRSxlQUFlLEVBQXVCLE1BQU0sUUFBUSxDQUFDO0FBQzlELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFHbEMsTUFBTSxPQUFnQiwwQkFBMkIsU0FBUSw0QkFBaUQ7SUFDeEc7Ozs7OztPQU1HO0lBQ0gsbUJBQW1CLEdBQUcsS0FBSyxFQUN6QixHQUErQixFQUMvQixLQUFZLEVBQ00sRUFBRTtRQUNwQixNQUFNLFlBQVksR0FBZ0MsRUFBRSxDQUFDO1FBQ3JELEtBQUssTUFBTSxXQUFXLElBQUksR0FBRyxFQUFFLENBQUM7WUFDOUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDN0MsSUFBSSxJQUFJLEVBQUUsQ0FBQztnQkFDVCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUN6QyxJQUFJLE1BQU0sRUFBRSxDQUFDO3dCQUNYLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQzNCLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FDbEQsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7d0JBQ2xCLFlBQVksQ0FBQyxJQUFJLENBQUM7NEJBQ2hCLFNBQVMsRUFBRSxJQUFJLENBQUMsVUFBVTs0QkFDMUIsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPOzRCQUNyQixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07NEJBQ25CLGtCQUFrQixFQUFFLElBQUksQ0FBQyxrQkFBa0I7NEJBQzNDLGtCQUFrQixFQUFFLElBQUksQ0FBQyxrQkFBa0I7NEJBQzNDLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVTs0QkFDM0IsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTOzRCQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7NEJBQzNCLGFBQWEsRUFBRSxLQUFLLENBQUMsSUFBSTs0QkFDekIsU0FBUyxFQUFFLFNBQVM7NEJBQ3BCLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUzs0QkFDekIsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXO3lCQUM5QixDQUFDLENBQUM7b0JBQ0wsQ0FBQzs7d0JBQ0MsTUFBTSxLQUFLLENBQ1QsZ0NBQWdDLFdBQVcsQ0FBQyxJQUFJLDJCQUEyQixLQUFLLENBQUMsSUFBSSw2Q0FBNkMsQ0FDbkksQ0FBQztnQkFDTixDQUFDO2dCQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ1gsSUFBSSxlQUFlLENBQUMsQ0FBQyxDQUFDO3dCQUNwQixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FDZixpQ0FBaUMsV0FBVyxDQUFDLElBQUksb0JBQW9CLENBQ3RFLENBQUM7O3dCQUNDLE1BQU0sQ0FBQyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQzNFLENBQUMsQ0FBQztJQUVGOztPQUVHO0lBQ0gsT0FBTyxHQUFHLENBQUMsRUFBdUIsRUFBRSxFQUFFO1FBQ3BDLElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUNwQixNQUFNLEtBQUssQ0FDVCxxRUFBcUUsQ0FDdEUsQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUM7SUFDakIsQ0FBQyxDQUFDO0NBQ0giLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBBYnN0cmFjdE9ic2VydmF0aW9uRXh0cmFjdG9yLFxuICBFeHRyYWN0ZWRPYnNlcnZhdGlvbixcbn0gZnJvbSAnQHJvc2VuLWJyaWRnZS9vYnNlcnZhdGlvbi1leHRyYWN0b3InO1xuaW1wb3J0IHsgaXNDYWxsRXhjZXB0aW9uLCBUcmFuc2FjdGlvblJlc3BvbnNlIH0gZnJvbSAnZXRoZXJzJztcbmltcG9ydCB7IGJsYWtlMmIgfSBmcm9tICdibGFrZWpzJztcbmltcG9ydCB7IEJsb2NrIH0gZnJvbSAnQHJvc2VuLWJyaWRnZS9hYnN0cmFjdC1leHRyYWN0b3InO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IgZXh0ZW5kcyBBYnN0cmFjdE9ic2VydmF0aW9uRXh0cmFjdG9yPFRyYW5zYWN0aW9uUmVzcG9uc2U+IHtcbiAgLyoqXG4gICAqIGdldHMgYmxvY2sgaWQgYW5kIHRyYW5zYWN0aW9ucyBjb3JyZXNwb25kaW5nIHRvIHRoZSBibG9jayBhbmQgc2F2ZXMgaWYgdGhleSBhcmUgdmFsaWQgcm9zZW5cbiAgICogIHRyYW5zYWN0aW9ucyBhbmQgaW4gY2FzZSBvZiBzdWNjZXNzIHJldHVybiB0cnVlIGFuZCBpbiBjYXNlIG9mIGZhaWx1cmUgcmV0dXJucyBmYWxzZVxuICAgKiBhZGRpdGlvbmFsbHksIGl0IHJldHVybnMgZmFsc2UgaWYgdGhlIHRyYW5zYWN0aW9uIGlzIGZhaWxlZFxuICAgKiBAcGFyYW0gYmxvY2tcbiAgICogQHBhcmFtIHR4c1xuICAgKi9cbiAgcHJvY2Vzc1RyYW5zYWN0aW9ucyA9IGFzeW5jIChcbiAgICB0eHM6IEFycmF5PFRyYW5zYWN0aW9uUmVzcG9uc2U+LFxuICAgIGJsb2NrOiBCbG9ja1xuICApOiBQcm9taXNlPGJvb2xlYW4+ID0+IHtcbiAgICBjb25zdCBvYnNlcnZhdGlvbnM6IEFycmF5PEV4dHJhY3RlZE9ic2VydmF0aW9uPiA9IFtdO1xuICAgIGZvciAoY29uc3QgdHJhbnNhY3Rpb24gb2YgdHhzKSB7XG4gICAgICBjb25zdCBkYXRhID0gdGhpcy5leHRyYWN0b3IuZ2V0KHRyYW5zYWN0aW9uKTtcbiAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdHJhbnNhY3Rpb24ud2FpdCgwKTtcbiAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0SWQgPSBCdWZmZXIuZnJvbShcbiAgICAgICAgICAgICAgYmxha2UyYih0aGlzLmdldFR4SWQodHJhbnNhY3Rpb24pLCB1bmRlZmluZWQsIDMyKVxuICAgICAgICAgICAgKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICAgICAgICBvYnNlcnZhdGlvbnMucHVzaCh7XG4gICAgICAgICAgICAgIGZyb21DaGFpbjogdGhpcy5GUk9NX0NIQUlOLFxuICAgICAgICAgICAgICB0b0NoYWluOiBkYXRhLnRvQ2hhaW4sXG4gICAgICAgICAgICAgIGFtb3VudDogZGF0YS5hbW91bnQsXG4gICAgICAgICAgICAgIHNvdXJjZUNoYWluVG9rZW5JZDogZGF0YS5zb3VyY2VDaGFpblRva2VuSWQsXG4gICAgICAgICAgICAgIHRhcmdldENoYWluVG9rZW5JZDogZGF0YS50YXJnZXRDaGFpblRva2VuSWQsXG4gICAgICAgICAgICAgIHNvdXJjZVR4SWQ6IGRhdGEuc291cmNlVHhJZCxcbiAgICAgICAgICAgICAgYnJpZGdlRmVlOiBkYXRhLmJyaWRnZUZlZSxcbiAgICAgICAgICAgICAgbmV0d29ya0ZlZTogZGF0YS5uZXR3b3JrRmVlLFxuICAgICAgICAgICAgICBzb3VyY2VCbG9ja0lkOiBibG9jay5oYXNoLFxuICAgICAgICAgICAgICByZXF1ZXN0SWQ6IHJlcXVlc3RJZCxcbiAgICAgICAgICAgICAgdG9BZGRyZXNzOiBkYXRhLnRvQWRkcmVzcyxcbiAgICAgICAgICAgICAgZnJvbUFkZHJlc3M6IGRhdGEuZnJvbUFkZHJlc3MsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGVsc2VcbiAgICAgICAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAgICAgICBgSW1wb3NzaWJsZSBiZWhhdmlvcjogRXZtIFR4IFske3RyYW5zYWN0aW9uLmhhc2h9XSBpcyBpbmNsdWRlZCBpbiBibG9jayBbJHtibG9jay5oYXNofV0gYnV0IHdhaXRpbmcgcmVzdWx0ZWQgaW4gbnVsbCBvciB1bmRlZmluZWRgXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgaWYgKGlzQ2FsbEV4Y2VwdGlvbihlKSlcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKFxuICAgICAgICAgICAgICBgZm91bmQgdmFsaWQgbG9jayB0cmFuc2FjdGlvbiBbJHt0cmFuc2FjdGlvbi5oYXNofV0gYnV0IHR4IGlzIGZhaWxlZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgZWxzZSB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFjdGlvbnMuc3RvcmVPYnNlcnZhdGlvbnMob2JzZXJ2YXRpb25zLCBibG9jaywgdGhpcy5nZXRJZCgpKTtcbiAgfTtcblxuICAvKipcbiAgICogZ2V0cyB0cmFuc2FjdGlvbiBpZCBmcm9tIFRyYW5zYWN0aW9uVHlwZVxuICAgKi9cbiAgZ2V0VHhJZCA9ICh0eDogVHJhbnNhY3Rpb25SZXNwb25zZSkgPT4ge1xuICAgIGlmICh0eC5oYXNoID09IG51bGwpIHtcbiAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAnSW1wb3NzaWJsZUJlaGF2aW9yOiBUcmFuc2FjdGlvbnMgY29taW5nIGZyb20gUlBDIGhhdmUgdG8gYmUgc2lnbmVkLidcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB0eC5oYXNoO1xuICB9O1xufVxuIl19
|
package/dist/index.d.ts
CHANGED
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,8BAA8B,CAAC"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../lib/index.ts"],"names":[],"mappings":"AAAA,cAAc,mCAAmC,CAAC;AAClD,cAAc,8BAA8B,CAAC"}
|
package/dist/index.js
CHANGED
|
@@ -1,2 +1,3 @@
|
|
|
1
|
+
export * from './EthereumRpcObservationExtractor';
|
|
1
2
|
export * from './EvmRpcObservationExtractor';
|
|
2
|
-
//# sourceMappingURL=data:application/json;base64,
|
|
3
|
+
//# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyxtQ0FBbUMsQ0FBQztBQUNsRCxjQUFjLDhCQUE4QixDQUFDIiwic291cmNlc0NvbnRlbnQiOlsiZXhwb3J0ICogZnJvbSAnLi9FdGhlcmV1bVJwY09ic2VydmF0aW9uRXh0cmFjdG9yJztcbmV4cG9ydCAqIGZyb20gJy4vRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3InO1xuIl19
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
import { AbstractLogger } from '@rosen-bridge/abstract-logger';
|
|
2
|
+
import { EvmEthersRosenExtractor } from '@rosen-bridge/rosen-extractor';
|
|
3
|
+
import { RosenTokens } from '@rosen-bridge/tokens';
|
|
4
|
+
import { DataSource } from 'typeorm';
|
|
5
|
+
import { EvmRpcObservationExtractor } from './EvmRpcObservationExtractor';
|
|
6
|
+
|
|
7
|
+
export class EthereumRpcObservationExtractor extends EvmRpcObservationExtractor {
|
|
8
|
+
readonly FROM_CHAIN = 'ethereum';
|
|
9
|
+
|
|
10
|
+
constructor(
|
|
11
|
+
lockAddress: string,
|
|
12
|
+
dataSource: DataSource,
|
|
13
|
+
tokens: RosenTokens,
|
|
14
|
+
logger?: AbstractLogger
|
|
15
|
+
) {
|
|
16
|
+
super(
|
|
17
|
+
dataSource,
|
|
18
|
+
tokens,
|
|
19
|
+
new EvmEthersRosenExtractor(
|
|
20
|
+
lockAddress,
|
|
21
|
+
tokens,
|
|
22
|
+
'ethereum',
|
|
23
|
+
'eth',
|
|
24
|
+
logger
|
|
25
|
+
),
|
|
26
|
+
logger
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
/**
|
|
31
|
+
* gets Id for current extractor
|
|
32
|
+
*/
|
|
33
|
+
getId = () => 'ethereum-rpc-extractor';
|
|
34
|
+
}
|
|
@@ -1,39 +1,70 @@
|
|
|
1
|
-
import {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
import {
|
|
6
|
-
import {
|
|
7
|
-
|
|
8
|
-
export class EthereumRpcObservationExtractor extends AbstractObservationExtractor<Transaction> {
|
|
9
|
-
readonly FROM_CHAIN = 'ethereum';
|
|
10
|
-
|
|
11
|
-
constructor(
|
|
12
|
-
lockAddress: string,
|
|
13
|
-
dataSource: DataSource,
|
|
14
|
-
tokens: RosenTokens,
|
|
15
|
-
logger?: AbstractLogger
|
|
16
|
-
) {
|
|
17
|
-
super(
|
|
18
|
-
dataSource,
|
|
19
|
-
tokens,
|
|
20
|
-
new EvmRpcRosenExtractor(lockAddress, tokens, 'ethereum', 'eth', logger),
|
|
21
|
-
logger
|
|
22
|
-
);
|
|
23
|
-
}
|
|
1
|
+
import {
|
|
2
|
+
AbstractObservationExtractor,
|
|
3
|
+
ExtractedObservation,
|
|
4
|
+
} from '@rosen-bridge/observation-extractor';
|
|
5
|
+
import { isCallException, TransactionResponse } from 'ethers';
|
|
6
|
+
import { blake2b } from 'blakejs';
|
|
7
|
+
import { Block } from '@rosen-bridge/abstract-extractor';
|
|
24
8
|
|
|
9
|
+
export abstract class EvmRpcObservationExtractor extends AbstractObservationExtractor<TransactionResponse> {
|
|
25
10
|
/**
|
|
26
|
-
* gets
|
|
11
|
+
* gets block id and transactions corresponding to the block and saves if they are valid rosen
|
|
12
|
+
* transactions and in case of success return true and in case of failure returns false
|
|
13
|
+
* additionally, it returns false if the transaction is failed
|
|
14
|
+
* @param block
|
|
15
|
+
* @param txs
|
|
27
16
|
*/
|
|
28
|
-
|
|
17
|
+
processTransactions = async (
|
|
18
|
+
txs: Array<TransactionResponse>,
|
|
19
|
+
block: Block
|
|
20
|
+
): Promise<boolean> => {
|
|
21
|
+
const observations: Array<ExtractedObservation> = [];
|
|
22
|
+
for (const transaction of txs) {
|
|
23
|
+
const data = this.extractor.get(transaction);
|
|
24
|
+
if (data) {
|
|
25
|
+
try {
|
|
26
|
+
const result = await transaction.wait(0);
|
|
27
|
+
if (result) {
|
|
28
|
+
const requestId = Buffer.from(
|
|
29
|
+
blake2b(this.getTxId(transaction), undefined, 32)
|
|
30
|
+
).toString('hex');
|
|
31
|
+
observations.push({
|
|
32
|
+
fromChain: this.FROM_CHAIN,
|
|
33
|
+
toChain: data.toChain,
|
|
34
|
+
amount: data.amount,
|
|
35
|
+
sourceChainTokenId: data.sourceChainTokenId,
|
|
36
|
+
targetChainTokenId: data.targetChainTokenId,
|
|
37
|
+
sourceTxId: data.sourceTxId,
|
|
38
|
+
bridgeFee: data.bridgeFee,
|
|
39
|
+
networkFee: data.networkFee,
|
|
40
|
+
sourceBlockId: block.hash,
|
|
41
|
+
requestId: requestId,
|
|
42
|
+
toAddress: data.toAddress,
|
|
43
|
+
fromAddress: data.fromAddress,
|
|
44
|
+
});
|
|
45
|
+
} else
|
|
46
|
+
throw Error(
|
|
47
|
+
`Impossible behavior: Evm Tx [${transaction.hash}] is included in block [${block.hash}] but waiting resulted in null or undefined`
|
|
48
|
+
);
|
|
49
|
+
} catch (e) {
|
|
50
|
+
if (isCallException(e))
|
|
51
|
+
this.logger.debug(
|
|
52
|
+
`found valid lock transaction [${transaction.hash}] but tx is failed`
|
|
53
|
+
);
|
|
54
|
+
else throw e;
|
|
55
|
+
}
|
|
56
|
+
}
|
|
57
|
+
}
|
|
58
|
+
return this.actions.storeObservations(observations, block, this.getId());
|
|
59
|
+
};
|
|
29
60
|
|
|
30
61
|
/**
|
|
31
62
|
* gets transaction id from TransactionType
|
|
32
63
|
*/
|
|
33
|
-
getTxId = (tx:
|
|
64
|
+
getTxId = (tx: TransactionResponse) => {
|
|
34
65
|
if (tx.hash == null) {
|
|
35
66
|
throw Error(
|
|
36
|
-
'
|
|
67
|
+
'ImpossibleBehavior: Transactions coming from RPC have to be signed.'
|
|
37
68
|
);
|
|
38
69
|
}
|
|
39
70
|
return tx.hash;
|
package/lib/index.ts
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@rosen-bridge/evm-observation-extractor",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "2.0.1-08d1d81f",
|
|
4
4
|
"description": "Event observation data extractor for EVM-compatible chains",
|
|
5
5
|
"repository": "https://github.com/rosen-bridge/scanner",
|
|
6
6
|
"license": "GPL-3.0",
|
|
@@ -10,26 +10,35 @@
|
|
|
10
10
|
"types": "dist/index.d.ts",
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "tsc --build tsconfig.build.json",
|
|
13
|
+
"coverage": "npm run test -- --coverage",
|
|
13
14
|
"lint": "eslint --fix . && npm run prettify",
|
|
14
15
|
"prettify": "prettier --write . --ignore-path ./.gitignore",
|
|
15
16
|
"release": "npm run build && npm publish --access public",
|
|
17
|
+
"test": "NODE_OPTIONS=--loader=extensionless vitest",
|
|
16
18
|
"type-check": "tsc --noEmit"
|
|
17
19
|
},
|
|
18
20
|
"devDependencies": {
|
|
19
21
|
"@types/node": "^20.11.9",
|
|
20
22
|
"@typescript-eslint/eslint-plugin": "^6.19.1",
|
|
21
23
|
"@typescript-eslint/parser": "^6.19.1",
|
|
24
|
+
"@vitest/coverage-istanbul": "^1.2.2",
|
|
22
25
|
"eslint": "^8.56.0",
|
|
23
26
|
"eslint-config-prettier": "^9.1.0",
|
|
27
|
+
"extensionless": "^1.9.6",
|
|
24
28
|
"prettier": "^3.2.4",
|
|
25
|
-
"typescript": "^5.3.3"
|
|
29
|
+
"typescript": "^5.3.3",
|
|
30
|
+
"vitest": "^1.2.2"
|
|
26
31
|
},
|
|
27
32
|
"engines": {
|
|
28
33
|
"node": ">=20.11.0"
|
|
29
34
|
},
|
|
30
35
|
"dependencies": {
|
|
31
|
-
"@rosen-bridge/
|
|
32
|
-
"@rosen-bridge/
|
|
33
|
-
"@rosen-bridge/
|
|
36
|
+
"@rosen-bridge/abstract-logger": "^1.0.0",
|
|
37
|
+
"@rosen-bridge/evm-rpc-scanner": "^0.2.9-08d1d81f",
|
|
38
|
+
"@rosen-bridge/observation-extractor": "^5.0.1-08d1d81f",
|
|
39
|
+
"@rosen-bridge/rosen-extractor": "^6.1.0",
|
|
40
|
+
"@rosen-bridge/tokens": "^1.2.0",
|
|
41
|
+
"blakejs": "^1.2.1",
|
|
42
|
+
"ethers": "^6.11.0"
|
|
34
43
|
}
|
|
35
44
|
}
|
|
@@ -0,0 +1,160 @@
|
|
|
1
|
+
import { vi } from 'vitest';
|
|
2
|
+
import { DataSource } from 'typeorm';
|
|
3
|
+
import { blake2b } from 'blakejs';
|
|
4
|
+
import { ObservationEntity } from '@rosen-bridge/observation-extractor';
|
|
5
|
+
import { createDatabase, generateBlockEntity } from './utils.mock';
|
|
6
|
+
import { rosenData, tx, txRes } from './testData';
|
|
7
|
+
import { TestEvmRpcObservationExtractor } from './TestObservationExtractor';
|
|
8
|
+
|
|
9
|
+
vi.mock('ethers', async (importOriginal) => {
|
|
10
|
+
const ref = await importOriginal<typeof import('ethers')>();
|
|
11
|
+
return {
|
|
12
|
+
...ref,
|
|
13
|
+
JsonRpcProvider: vi.fn().mockImplementation((url: string) => {
|
|
14
|
+
return {};
|
|
15
|
+
}),
|
|
16
|
+
};
|
|
17
|
+
});
|
|
18
|
+
|
|
19
|
+
describe('EvmRpcObservationExtractor', () => {
|
|
20
|
+
let dataSource: DataSource;
|
|
21
|
+
let extractor: TestEvmRpcObservationExtractor;
|
|
22
|
+
|
|
23
|
+
beforeEach(async () => {
|
|
24
|
+
dataSource = await createDatabase();
|
|
25
|
+
extractor = new TestEvmRpcObservationExtractor(
|
|
26
|
+
dataSource,
|
|
27
|
+
{
|
|
28
|
+
idKeys: {},
|
|
29
|
+
tokens: [],
|
|
30
|
+
},
|
|
31
|
+
{
|
|
32
|
+
get: vi.fn(),
|
|
33
|
+
} as any
|
|
34
|
+
);
|
|
35
|
+
});
|
|
36
|
+
|
|
37
|
+
describe('processTransactions', () => {
|
|
38
|
+
/**
|
|
39
|
+
* @target EvmRpcObservationExtractor.processTransactions
|
|
40
|
+
* should return true and insert observation into database on valid lock tx
|
|
41
|
+
* @dependencies
|
|
42
|
+
* @scenario
|
|
43
|
+
* - mock rosen-extractor to return rosen data
|
|
44
|
+
* - mock `wait` function of the transaction to return tx object
|
|
45
|
+
* - run test
|
|
46
|
+
* - check returned value
|
|
47
|
+
* - check database
|
|
48
|
+
* @expected
|
|
49
|
+
* - it should return true
|
|
50
|
+
* - observation should be inserted into database
|
|
51
|
+
*/
|
|
52
|
+
it('should return true and insert observation into database on valid lock tx', async () => {
|
|
53
|
+
vi.spyOn(extractor.getRosenExtractor(), 'get').mockReturnValue(rosenData);
|
|
54
|
+
|
|
55
|
+
vi.spyOn(txRes, 'wait').mockReturnValue(tx as any);
|
|
56
|
+
|
|
57
|
+
// run test
|
|
58
|
+
const res = await extractor.processTransactions(
|
|
59
|
+
[txRes],
|
|
60
|
+
generateBlockEntity(dataSource, 'block-id')
|
|
61
|
+
);
|
|
62
|
+
|
|
63
|
+
// check returned valid
|
|
64
|
+
expect(res).toEqual(true);
|
|
65
|
+
|
|
66
|
+
// check database
|
|
67
|
+
const repository = dataSource.getRepository(ObservationEntity);
|
|
68
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
69
|
+
expect(rowsCount).toEqual(1);
|
|
70
|
+
const observation1 = rows[0];
|
|
71
|
+
const txHash = tx.hash!;
|
|
72
|
+
expect(observation1).toEqual({
|
|
73
|
+
id: 1,
|
|
74
|
+
fromChain: extractor.FROM_CHAIN,
|
|
75
|
+
toChain: rosenData.toChain,
|
|
76
|
+
fromAddress: rosenData.fromAddress,
|
|
77
|
+
toAddress: rosenData.toAddress,
|
|
78
|
+
height: 1,
|
|
79
|
+
amount: rosenData.amount,
|
|
80
|
+
networkFee: rosenData.networkFee,
|
|
81
|
+
bridgeFee: rosenData.bridgeFee,
|
|
82
|
+
sourceChainTokenId: rosenData.sourceChainTokenId,
|
|
83
|
+
targetChainTokenId: rosenData.targetChainTokenId,
|
|
84
|
+
sourceBlockId: 'block-id',
|
|
85
|
+
sourceTxId: txHash,
|
|
86
|
+
block: 'block-id',
|
|
87
|
+
requestId: Buffer.from(blake2b(txHash, undefined, 32)).toString('hex'),
|
|
88
|
+
extractor: extractor.getId(),
|
|
89
|
+
});
|
|
90
|
+
}, 100000);
|
|
91
|
+
|
|
92
|
+
/**
|
|
93
|
+
* @target EvmRpcObservationExtractor.processTransactions
|
|
94
|
+
* should return true but insert no tx when transaction is failed
|
|
95
|
+
* @dependencies
|
|
96
|
+
* @scenario
|
|
97
|
+
* - mock rosen-extractor to return rosen data
|
|
98
|
+
* - mock `wait` function of the transaction to throw CallException error
|
|
99
|
+
* - run test
|
|
100
|
+
* - check returned value
|
|
101
|
+
* - check database
|
|
102
|
+
* @expected
|
|
103
|
+
* - it should return true
|
|
104
|
+
* - no observation should be inserted into database
|
|
105
|
+
*/
|
|
106
|
+
it('should return true but insert no tx when transaction is failed', async () => {
|
|
107
|
+
vi.spyOn(extractor.getRosenExtractor(), 'get').mockReturnValue(rosenData);
|
|
108
|
+
|
|
109
|
+
vi.spyOn(txRes, 'wait').mockImplementation((x: number | undefined) => {
|
|
110
|
+
throw {
|
|
111
|
+
code: 'CALL_EXCEPTION',
|
|
112
|
+
};
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
// run test
|
|
116
|
+
const res = await extractor.processTransactions(
|
|
117
|
+
[txRes],
|
|
118
|
+
generateBlockEntity(dataSource, 'block-id')
|
|
119
|
+
);
|
|
120
|
+
|
|
121
|
+
// check returned valid
|
|
122
|
+
expect(res).toEqual(true);
|
|
123
|
+
|
|
124
|
+
// check database
|
|
125
|
+
const repository = dataSource.getRepository(ObservationEntity);
|
|
126
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
127
|
+
expect(rowsCount).toEqual(0);
|
|
128
|
+
}, 100000);
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* @target EvmRpcObservationExtractor.processTransactions
|
|
132
|
+
* should return true with no observation in database on invalid lock tx
|
|
133
|
+
* @dependencies
|
|
134
|
+
* @scenario
|
|
135
|
+
* - mock rosen-extractor to return undefined
|
|
136
|
+
* - run test
|
|
137
|
+
* - check returned value
|
|
138
|
+
* - check database
|
|
139
|
+
* @expected
|
|
140
|
+
* - it should return true
|
|
141
|
+
* - no observation should be into database
|
|
142
|
+
*/
|
|
143
|
+
it('should return true with no observation in database on invalid lock tx', async () => {
|
|
144
|
+
vi.spyOn(extractor.getRosenExtractor(), 'get').mockReturnValue(undefined);
|
|
145
|
+
|
|
146
|
+
const res = await extractor.processTransactions(
|
|
147
|
+
[txRes],
|
|
148
|
+
generateBlockEntity(dataSource, 'block-id')
|
|
149
|
+
);
|
|
150
|
+
|
|
151
|
+
// check returned valid
|
|
152
|
+
expect(res).toEqual(true);
|
|
153
|
+
|
|
154
|
+
// check database
|
|
155
|
+
const repository = dataSource.getRepository(ObservationEntity);
|
|
156
|
+
const [rows, rowsCount] = await repository.findAndCount();
|
|
157
|
+
expect(rowsCount).toEqual(0);
|
|
158
|
+
}, 100000);
|
|
159
|
+
});
|
|
160
|
+
});
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import { EvmRpcObservationExtractor } from '../lib';
|
|
2
|
+
|
|
3
|
+
export class TestEvmRpcObservationExtractor extends EvmRpcObservationExtractor {
|
|
4
|
+
readonly FROM_CHAIN = 'test-chain';
|
|
5
|
+
|
|
6
|
+
getId = () => 'test-observation-extractor';
|
|
7
|
+
getRosenExtractor = () => this.extractor;
|
|
8
|
+
}
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { JsonRpcProvider, Transaction, TransactionResponse } from 'ethers';
|
|
2
|
+
|
|
3
|
+
export const tx = Transaction.from({
|
|
4
|
+
type: 2,
|
|
5
|
+
to: '0xeDee4752e5a2F595151c94762fB38e5730357785',
|
|
6
|
+
data: '0xa9059cbb0000000000000000000000004f0d2dde80b45e24ad4019a5aabd6c23aff2842b00000000000000000000000000000000000000000000000000000000e319aa30bbbbbbbbbbbbbbbbbbbbbbbbbbbbbbbb',
|
|
7
|
+
nonce: 53,
|
|
8
|
+
gasLimit: '21000',
|
|
9
|
+
gasPrice: null,
|
|
10
|
+
maxPriorityFeePerGas: '500000000',
|
|
11
|
+
maxFeePerGas: '48978500000',
|
|
12
|
+
value: '0',
|
|
13
|
+
chainId: '1',
|
|
14
|
+
accessList: [],
|
|
15
|
+
signature: {
|
|
16
|
+
r: '0xc15a4d9e300114ed005a2821f01f4aa74dcfd3daf10749f26d8cc1bd8507673c',
|
|
17
|
+
s: '0x090492307ff4c7d2d514f373fa8fb01212c6af83391b6aed7039a3c9d6ecad11',
|
|
18
|
+
yParity: 0,
|
|
19
|
+
},
|
|
20
|
+
hash: '0x3b194eea7cf9507e745806265738ca19213be209885534161ec0fa9c232c9fea',
|
|
21
|
+
});
|
|
22
|
+
export const txRes = new TransactionResponse(tx as any, new JsonRpcProvider());
|
|
23
|
+
|
|
24
|
+
export const rosenData = {
|
|
25
|
+
toChain: 'to-chain',
|
|
26
|
+
toAddress: 'to-address',
|
|
27
|
+
bridgeFee: '1968503938',
|
|
28
|
+
networkFee: '9842520',
|
|
29
|
+
fromAddress: 'from-address',
|
|
30
|
+
sourceChainTokenId: 'source-token-id',
|
|
31
|
+
amount: '3000000000',
|
|
32
|
+
targetChainTokenId: 'target-token-id',
|
|
33
|
+
sourceTxId:
|
|
34
|
+
'0x3b194eea7cf9507e745806265738ca19213be209885534161ec0fa9c232c9fea',
|
|
35
|
+
};
|