@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 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 { RosenTokens } from '@rosen-bridge/tokens';
4
- import { AbstractLogger } from '@rosen-bridge/abstract-logger';
5
- import { DataSource } from 'typeorm';
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 Id for current extractor
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
- getId: () => string;
12
+ processTransactions: (txs: Array<TransactionResponse>, block: Block) => Promise<boolean>;
13
13
  /**
14
14
  * gets transaction id from TransactionType
15
15
  */
16
- getTxId: (tx: Transaction) => string;
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,EAAE,WAAW,EAAE,MAAM,QAAQ,CAAC;AACrC,OAAO,EAAE,4BAA4B,EAAE,MAAM,qCAAqC,CAAC;AAEnF,OAAO,EAAE,WAAW,EAAE,MAAM,sBAAsB,CAAC;AACnD,OAAO,EAAE,cAAc,EAAE,MAAM,+BAA+B,CAAC;AAC/D,OAAO,EAAE,UAAU,EAAE,MAAM,SAAS,CAAC;AAErC,qBAAa,+BAAgC,SAAQ,4BAA4B,CAAC,WAAW,CAAC;IAC5F,QAAQ,CAAC,UAAU,cAAc;gBAG/B,WAAW,EAAE,MAAM,EACnB,UAAU,EAAE,UAAU,EACtB,MAAM,EAAE,WAAW,EACnB,MAAM,CAAC,EAAE,cAAc;IAUzB;;OAEG;IACH,KAAK,eAAkC;IAEvC;;OAEG;IACH,OAAO,OAAQ,WAAW,YAOxB;CACH"}
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 { EvmRpcRosenExtractor } from '@rosen-bridge/rosen-extractor';
3
- export class EthereumRpcObservationExtractor extends AbstractObservationExtractor {
4
- FROM_CHAIN = 'ethereum';
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 Id for current extractor
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
- getId = () => 'ethereum-rpc-extractor';
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('ImpossibleBehaviour: Transactions comming from RPC have to be signed.');
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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQ0EsT0FBTyxFQUFFLDRCQUE0QixFQUFFLE1BQU0scUNBQXFDLENBQUM7QUFDbkYsT0FBTyxFQUFFLG9CQUFvQixFQUFFLE1BQU0sK0JBQStCLENBQUM7QUFLckUsTUFBTSxPQUFPLCtCQUFnQyxTQUFRLDRCQUF5QztJQUNuRixVQUFVLEdBQUcsVUFBVSxDQUFDO0lBRWpDLFlBQ0UsV0FBbUIsRUFDbkIsVUFBc0IsRUFDdEIsTUFBbUIsRUFDbkIsTUFBdUI7UUFFdkIsS0FBSyxDQUNILFVBQVUsRUFDVixNQUFNLEVBQ04sSUFBSSxvQkFBb0IsQ0FBQyxXQUFXLEVBQUUsTUFBTSxFQUFFLFVBQVUsRUFBRSxLQUFLLEVBQUUsTUFBTSxDQUFDLEVBQ3hFLE1BQU0sQ0FDUCxDQUFDO0lBQ0osQ0FBQztJQUVEOztPQUVHO0lBQ0gsS0FBSyxHQUFHLEdBQUcsRUFBRSxDQUFDLHdCQUF3QixDQUFDO0lBRXZDOztPQUVHO0lBQ0gsT0FBTyxHQUFHLENBQUMsRUFBZSxFQUFFLEVBQUU7UUFDNUIsSUFBSSxFQUFFLENBQUMsSUFBSSxJQUFJLElBQUksRUFBRSxDQUFDO1lBQ3BCLE1BQU0sS0FBSyxDQUNULHVFQUF1RSxDQUN4RSxDQUFDO1FBQ0osQ0FBQztRQUNELE9BQU8sRUFBRSxDQUFDLElBQUksQ0FBQztJQUNqQixDQUFDLENBQUM7Q0FDSCIsInNvdXJjZXNDb250ZW50IjpbImltcG9ydCB7IFRyYW5zYWN0aW9uIH0gZnJvbSAnZXRoZXJzJztcbmltcG9ydCB7IEFic3RyYWN0T2JzZXJ2YXRpb25FeHRyYWN0b3IgfSBmcm9tICdAcm9zZW4tYnJpZGdlL29ic2VydmF0aW9uLWV4dHJhY3Rvcic7XG5pbXBvcnQgeyBFdm1ScGNSb3NlbkV4dHJhY3RvciB9IGZyb20gJ0Byb3Nlbi1icmlkZ2Uvcm9zZW4tZXh0cmFjdG9yJztcbmltcG9ydCB7IFJvc2VuVG9rZW5zIH0gZnJvbSAnQHJvc2VuLWJyaWRnZS90b2tlbnMnO1xuaW1wb3J0IHsgQWJzdHJhY3RMb2dnZXIgfSBmcm9tICdAcm9zZW4tYnJpZGdlL2Fic3RyYWN0LWxvZ2dlcic7XG5pbXBvcnQgeyBEYXRhU291cmNlIH0gZnJvbSAndHlwZW9ybSc7XG5cbmV4cG9ydCBjbGFzcyBFdGhlcmV1bVJwY09ic2VydmF0aW9uRXh0cmFjdG9yIGV4dGVuZHMgQWJzdHJhY3RPYnNlcnZhdGlvbkV4dHJhY3RvcjxUcmFuc2FjdGlvbj4ge1xuICByZWFkb25seSBGUk9NX0NIQUlOID0gJ2V0aGVyZXVtJztcblxuICBjb25zdHJ1Y3RvcihcbiAgICBsb2NrQWRkcmVzczogc3RyaW5nLFxuICAgIGRhdGFTb3VyY2U6IERhdGFTb3VyY2UsXG4gICAgdG9rZW5zOiBSb3NlblRva2VucyxcbiAgICBsb2dnZXI/OiBBYnN0cmFjdExvZ2dlclxuICApIHtcbiAgICBzdXBlcihcbiAgICAgIGRhdGFTb3VyY2UsXG4gICAgICB0b2tlbnMsXG4gICAgICBuZXcgRXZtUnBjUm9zZW5FeHRyYWN0b3IobG9ja0FkZHJlc3MsIHRva2VucywgJ2V0aGVyZXVtJywgJ2V0aCcsIGxvZ2dlciksXG4gICAgICBsb2dnZXJcbiAgICApO1xuICB9XG5cbiAgLyoqXG4gICAqIGdldHMgSWQgZm9yIGN1cnJlbnQgZXh0cmFjdG9yXG4gICAqL1xuICBnZXRJZCA9ICgpID0+ICdldGhlcmV1bS1ycGMtZXh0cmFjdG9yJztcblxuICAvKipcbiAgICogZ2V0cyB0cmFuc2FjdGlvbiBpZCBmcm9tIFRyYW5zYWN0aW9uVHlwZVxuICAgKi9cbiAgZ2V0VHhJZCA9ICh0eDogVHJhbnNhY3Rpb24pID0+IHtcbiAgICBpZiAodHguaGFzaCA9PSBudWxsKSB7XG4gICAgICB0aHJvdyBFcnJvcihcbiAgICAgICAgJ0ltcG9zc2libGVCZWhhdmlvdXI6IFRyYW5zYWN0aW9ucyBjb21taW5nIGZyb20gUlBDIGhhdmUgdG8gYmUgc2lnbmVkLidcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB0eC5oYXNoO1xuICB9O1xufVxuIl19
59
+ //# sourceMappingURL=data:application/json;base64,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IuanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsT0FBTyxFQUNMLDRCQUE0QixHQUU3QixNQUFNLHFDQUFxQyxDQUFDO0FBQzdDLE9BQU8sRUFBRSxlQUFlLEVBQXVCLE1BQU0sUUFBUSxDQUFDO0FBQzlELE9BQU8sRUFBRSxPQUFPLEVBQUUsTUFBTSxTQUFTLENBQUM7QUFHbEMsTUFBTSxPQUFnQiwwQkFBMkIsU0FBUSw0QkFBaUQ7SUFDeEc7Ozs7OztPQU1HO0lBQ0gsbUJBQW1CLEdBQUcsS0FBSyxFQUN6QixHQUErQixFQUMvQixLQUFZLEVBQ00sRUFBRTtRQUNwQixNQUFNLFlBQVksR0FBZ0MsRUFBRSxDQUFDO1FBQ3JELEtBQUssTUFBTSxXQUFXLElBQUksR0FBRyxFQUFFLENBQUM7WUFDOUIsTUFBTSxJQUFJLEdBQUcsSUFBSSxDQUFDLFNBQVMsQ0FBQyxHQUFHLENBQUMsV0FBVyxDQUFDLENBQUM7WUFDN0MsSUFBSSxJQUFJLEVBQUUsQ0FBQztnQkFDVCxJQUFJLENBQUM7b0JBQ0gsTUFBTSxNQUFNLEdBQUcsTUFBTSxXQUFXLENBQUMsSUFBSSxDQUFDLENBQUMsQ0FBQyxDQUFDO29CQUN6QyxJQUFJLE1BQU0sRUFBRSxDQUFDO3dCQUNYLE1BQU0sU0FBUyxHQUFHLE1BQU0sQ0FBQyxJQUFJLENBQzNCLE9BQU8sQ0FBQyxJQUFJLENBQUMsT0FBTyxDQUFDLFdBQVcsQ0FBQyxFQUFFLFNBQVMsRUFBRSxFQUFFLENBQUMsQ0FDbEQsQ0FBQyxRQUFRLENBQUMsS0FBSyxDQUFDLENBQUM7d0JBQ2xCLFlBQVksQ0FBQyxJQUFJLENBQUM7NEJBQ2hCLFNBQVMsRUFBRSxJQUFJLENBQUMsVUFBVTs0QkFDMUIsT0FBTyxFQUFFLElBQUksQ0FBQyxPQUFPOzRCQUNyQixNQUFNLEVBQUUsSUFBSSxDQUFDLE1BQU07NEJBQ25CLGtCQUFrQixFQUFFLElBQUksQ0FBQyxrQkFBa0I7NEJBQzNDLGtCQUFrQixFQUFFLElBQUksQ0FBQyxrQkFBa0I7NEJBQzNDLFVBQVUsRUFBRSxJQUFJLENBQUMsVUFBVTs0QkFDM0IsU0FBUyxFQUFFLElBQUksQ0FBQyxTQUFTOzRCQUN6QixVQUFVLEVBQUUsSUFBSSxDQUFDLFVBQVU7NEJBQzNCLGFBQWEsRUFBRSxLQUFLLENBQUMsSUFBSTs0QkFDekIsU0FBUyxFQUFFLFNBQVM7NEJBQ3BCLFNBQVMsRUFBRSxJQUFJLENBQUMsU0FBUzs0QkFDekIsV0FBVyxFQUFFLElBQUksQ0FBQyxXQUFXO3lCQUM5QixDQUFDLENBQUM7b0JBQ0wsQ0FBQzs7d0JBQ0MsTUFBTSxLQUFLLENBQ1QsZ0NBQWdDLFdBQVcsQ0FBQyxJQUFJLDJCQUEyQixLQUFLLENBQUMsSUFBSSw2Q0FBNkMsQ0FDbkksQ0FBQztnQkFDTixDQUFDO2dCQUFDLE9BQU8sQ0FBQyxFQUFFLENBQUM7b0JBQ1gsSUFBSSxlQUFlLENBQUMsQ0FBQyxDQUFDO3dCQUNwQixJQUFJLENBQUMsTUFBTSxDQUFDLEtBQUssQ0FDZixpQ0FBaUMsV0FBVyxDQUFDLElBQUksb0JBQW9CLENBQ3RFLENBQUM7O3dCQUNDLE1BQU0sQ0FBQyxDQUFDO2dCQUNmLENBQUM7WUFDSCxDQUFDO1FBQ0gsQ0FBQztRQUNELE9BQU8sSUFBSSxDQUFDLE9BQU8sQ0FBQyxpQkFBaUIsQ0FBQyxZQUFZLEVBQUUsS0FBSyxFQUFFLElBQUksQ0FBQyxLQUFLLEVBQUUsQ0FBQyxDQUFDO0lBQzNFLENBQUMsQ0FBQztJQUVGOztPQUVHO0lBQ0gsT0FBTyxHQUFHLENBQUMsRUFBdUIsRUFBRSxFQUFFO1FBQ3BDLElBQUksRUFBRSxDQUFDLElBQUksSUFBSSxJQUFJLEVBQUUsQ0FBQztZQUNwQixNQUFNLEtBQUssQ0FDVCxxRUFBcUUsQ0FDdEUsQ0FBQztRQUNKLENBQUM7UUFDRCxPQUFPLEVBQUUsQ0FBQyxJQUFJLENBQUM7SUFDakIsQ0FBQyxDQUFDO0NBQ0giLCJzb3VyY2VzQ29udGVudCI6WyJpbXBvcnQge1xuICBBYnN0cmFjdE9ic2VydmF0aW9uRXh0cmFjdG9yLFxuICBFeHRyYWN0ZWRPYnNlcnZhdGlvbixcbn0gZnJvbSAnQHJvc2VuLWJyaWRnZS9vYnNlcnZhdGlvbi1leHRyYWN0b3InO1xuaW1wb3J0IHsgaXNDYWxsRXhjZXB0aW9uLCBUcmFuc2FjdGlvblJlc3BvbnNlIH0gZnJvbSAnZXRoZXJzJztcbmltcG9ydCB7IGJsYWtlMmIgfSBmcm9tICdibGFrZWpzJztcbmltcG9ydCB7IEJsb2NrIH0gZnJvbSAnQHJvc2VuLWJyaWRnZS9hYnN0cmFjdC1leHRyYWN0b3InO1xuXG5leHBvcnQgYWJzdHJhY3QgY2xhc3MgRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3IgZXh0ZW5kcyBBYnN0cmFjdE9ic2VydmF0aW9uRXh0cmFjdG9yPFRyYW5zYWN0aW9uUmVzcG9uc2U+IHtcbiAgLyoqXG4gICAqIGdldHMgYmxvY2sgaWQgYW5kIHRyYW5zYWN0aW9ucyBjb3JyZXNwb25kaW5nIHRvIHRoZSBibG9jayBhbmQgc2F2ZXMgaWYgdGhleSBhcmUgdmFsaWQgcm9zZW5cbiAgICogIHRyYW5zYWN0aW9ucyBhbmQgaW4gY2FzZSBvZiBzdWNjZXNzIHJldHVybiB0cnVlIGFuZCBpbiBjYXNlIG9mIGZhaWx1cmUgcmV0dXJucyBmYWxzZVxuICAgKiBhZGRpdGlvbmFsbHksIGl0IHJldHVybnMgZmFsc2UgaWYgdGhlIHRyYW5zYWN0aW9uIGlzIGZhaWxlZFxuICAgKiBAcGFyYW0gYmxvY2tcbiAgICogQHBhcmFtIHR4c1xuICAgKi9cbiAgcHJvY2Vzc1RyYW5zYWN0aW9ucyA9IGFzeW5jIChcbiAgICB0eHM6IEFycmF5PFRyYW5zYWN0aW9uUmVzcG9uc2U+LFxuICAgIGJsb2NrOiBCbG9ja1xuICApOiBQcm9taXNlPGJvb2xlYW4+ID0+IHtcbiAgICBjb25zdCBvYnNlcnZhdGlvbnM6IEFycmF5PEV4dHJhY3RlZE9ic2VydmF0aW9uPiA9IFtdO1xuICAgIGZvciAoY29uc3QgdHJhbnNhY3Rpb24gb2YgdHhzKSB7XG4gICAgICBjb25zdCBkYXRhID0gdGhpcy5leHRyYWN0b3IuZ2V0KHRyYW5zYWN0aW9uKTtcbiAgICAgIGlmIChkYXRhKSB7XG4gICAgICAgIHRyeSB7XG4gICAgICAgICAgY29uc3QgcmVzdWx0ID0gYXdhaXQgdHJhbnNhY3Rpb24ud2FpdCgwKTtcbiAgICAgICAgICBpZiAocmVzdWx0KSB7XG4gICAgICAgICAgICBjb25zdCByZXF1ZXN0SWQgPSBCdWZmZXIuZnJvbShcbiAgICAgICAgICAgICAgYmxha2UyYih0aGlzLmdldFR4SWQodHJhbnNhY3Rpb24pLCB1bmRlZmluZWQsIDMyKVxuICAgICAgICAgICAgKS50b1N0cmluZygnaGV4Jyk7XG4gICAgICAgICAgICBvYnNlcnZhdGlvbnMucHVzaCh7XG4gICAgICAgICAgICAgIGZyb21DaGFpbjogdGhpcy5GUk9NX0NIQUlOLFxuICAgICAgICAgICAgICB0b0NoYWluOiBkYXRhLnRvQ2hhaW4sXG4gICAgICAgICAgICAgIGFtb3VudDogZGF0YS5hbW91bnQsXG4gICAgICAgICAgICAgIHNvdXJjZUNoYWluVG9rZW5JZDogZGF0YS5zb3VyY2VDaGFpblRva2VuSWQsXG4gICAgICAgICAgICAgIHRhcmdldENoYWluVG9rZW5JZDogZGF0YS50YXJnZXRDaGFpblRva2VuSWQsXG4gICAgICAgICAgICAgIHNvdXJjZVR4SWQ6IGRhdGEuc291cmNlVHhJZCxcbiAgICAgICAgICAgICAgYnJpZGdlRmVlOiBkYXRhLmJyaWRnZUZlZSxcbiAgICAgICAgICAgICAgbmV0d29ya0ZlZTogZGF0YS5uZXR3b3JrRmVlLFxuICAgICAgICAgICAgICBzb3VyY2VCbG9ja0lkOiBibG9jay5oYXNoLFxuICAgICAgICAgICAgICByZXF1ZXN0SWQ6IHJlcXVlc3RJZCxcbiAgICAgICAgICAgICAgdG9BZGRyZXNzOiBkYXRhLnRvQWRkcmVzcyxcbiAgICAgICAgICAgICAgZnJvbUFkZHJlc3M6IGRhdGEuZnJvbUFkZHJlc3MsXG4gICAgICAgICAgICB9KTtcbiAgICAgICAgICB9IGVsc2VcbiAgICAgICAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAgICAgICBgSW1wb3NzaWJsZSBiZWhhdmlvcjogRXZtIFR4IFske3RyYW5zYWN0aW9uLmhhc2h9XSBpcyBpbmNsdWRlZCBpbiBibG9jayBbJHtibG9jay5oYXNofV0gYnV0IHdhaXRpbmcgcmVzdWx0ZWQgaW4gbnVsbCBvciB1bmRlZmluZWRgXG4gICAgICAgICAgICApO1xuICAgICAgICB9IGNhdGNoIChlKSB7XG4gICAgICAgICAgaWYgKGlzQ2FsbEV4Y2VwdGlvbihlKSlcbiAgICAgICAgICAgIHRoaXMubG9nZ2VyLmRlYnVnKFxuICAgICAgICAgICAgICBgZm91bmQgdmFsaWQgbG9jayB0cmFuc2FjdGlvbiBbJHt0cmFuc2FjdGlvbi5oYXNofV0gYnV0IHR4IGlzIGZhaWxlZGBcbiAgICAgICAgICAgICk7XG4gICAgICAgICAgZWxzZSB0aHJvdyBlO1xuICAgICAgICB9XG4gICAgICB9XG4gICAgfVxuICAgIHJldHVybiB0aGlzLmFjdGlvbnMuc3RvcmVPYnNlcnZhdGlvbnMob2JzZXJ2YXRpb25zLCBibG9jaywgdGhpcy5nZXRJZCgpKTtcbiAgfTtcblxuICAvKipcbiAgICogZ2V0cyB0cmFuc2FjdGlvbiBpZCBmcm9tIFRyYW5zYWN0aW9uVHlwZVxuICAgKi9cbiAgZ2V0VHhJZCA9ICh0eDogVHJhbnNhY3Rpb25SZXNwb25zZSkgPT4ge1xuICAgIGlmICh0eC5oYXNoID09IG51bGwpIHtcbiAgICAgIHRocm93IEVycm9yKFxuICAgICAgICAnSW1wb3NzaWJsZUJlaGF2aW9yOiBUcmFuc2FjdGlvbnMgY29taW5nIGZyb20gUlBDIGhhdmUgdG8gYmUgc2lnbmVkLidcbiAgICAgICk7XG4gICAgfVxuICAgIHJldHVybiB0eC5oYXNoO1xuICB9O1xufVxuIl19
package/dist/index.d.ts CHANGED
@@ -1,2 +1,3 @@
1
+ export * from './EthereumRpcObservationExtractor';
1
2
  export * from './EvmRpcObservationExtractor';
2
3
  //# sourceMappingURL=index.d.ts.map
@@ -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,eyJ2ZXJzaW9uIjozLCJmaWxlIjoiaW5kZXguanMiLCJzb3VyY2VSb290IjoiIiwic291cmNlcyI6WyIuLi9saWIvaW5kZXgudHMiXSwibmFtZXMiOltdLCJtYXBwaW5ncyI6IkFBQUEsY0FBYyw4QkFBOEIsQ0FBQyIsInNvdXJjZXNDb250ZW50IjpbImV4cG9ydCAqIGZyb20gJy4vRXZtUnBjT2JzZXJ2YXRpb25FeHRyYWN0b3InO1xuIl19
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 { Transaction } from 'ethers';
2
- import { AbstractObservationExtractor } from '@rosen-bridge/observation-extractor';
3
- import { EvmRpcRosenExtractor } from '@rosen-bridge/rosen-extractor';
4
- import { RosenTokens } from '@rosen-bridge/tokens';
5
- import { AbstractLogger } from '@rosen-bridge/abstract-logger';
6
- import { DataSource } from 'typeorm';
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 Id for current extractor
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
- getId = () => 'ethereum-rpc-extractor';
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: Transaction) => {
64
+ getTxId = (tx: TransactionResponse) => {
34
65
  if (tx.hash == null) {
35
66
  throw Error(
36
- 'ImpossibleBehaviour: Transactions comming from RPC have to be signed.'
67
+ 'ImpossibleBehavior: Transactions coming from RPC have to be signed.'
37
68
  );
38
69
  }
39
70
  return tx.hash;
package/lib/index.ts CHANGED
@@ -1 +1,2 @@
1
+ export * from './EthereumRpcObservationExtractor';
1
2
  export * from './EvmRpcObservationExtractor';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosen-bridge/evm-observation-extractor",
3
- "version": "1.0.9",
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/evm-rpc-scanner": "^0.2.9",
32
- "@rosen-bridge/observation-extractor": "^4.4.9",
33
- "@rosen-bridge/rosen-extractor": "^5.0.0"
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
+ };