@rosen-bridge/abstract-extractor 0.1.5 → 0.2.0

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.
Files changed (37) hide show
  1. package/CHANGELOG.md +6 -0
  2. package/dist/ergo/initializable/AbstractInitializable.d.ts +0 -6
  3. package/dist/ergo/initializable/AbstractInitializable.d.ts.map +1 -1
  4. package/dist/ergo/initializable/AbstractInitializable.js +3 -1
  5. package/dist/ergo/initializable/InitializableByAddress.d.ts +0 -7
  6. package/dist/ergo/initializable/InitializableByAddress.d.ts.map +1 -1
  7. package/dist/ergo/initializable/InitializableByAddress.js +1 -9
  8. package/dist/ergo/initializable/InitializableByToken.d.ts +0 -7
  9. package/dist/ergo/initializable/InitializableByToken.d.ts.map +1 -1
  10. package/dist/ergo/initializable/InitializableByToken.js +1 -9
  11. package/dist/ergo/interfaces.d.ts +4 -0
  12. package/dist/ergo/interfaces.d.ts.map +1 -1
  13. package/dist/ergo/interfaces.js +1 -1
  14. package/dist/ergo/network/AbstractNetwork.d.ts +0 -6
  15. package/dist/ergo/network/AbstractNetwork.d.ts.map +1 -1
  16. package/dist/ergo/network/AbstractNetwork.js +1 -1
  17. package/dist/ergo/network/ExplorerNetwork.d.ts +6 -3
  18. package/dist/ergo/network/ExplorerNetwork.d.ts.map +1 -1
  19. package/dist/ergo/network/ExplorerNetwork.js +16 -8
  20. package/dist/ergo/network/NodeNetwork.d.ts +7 -4
  21. package/dist/ergo/network/NodeNetwork.d.ts.map +1 -1
  22. package/dist/ergo/network/NodeNetwork.js +21 -14
  23. package/dist/tsconfig.tsbuildinfo +1 -1
  24. package/lib/ergo/initializable/AbstractInitializable.ts +2 -7
  25. package/lib/ergo/initializable/InitializableByAddress.ts +0 -10
  26. package/lib/ergo/initializable/InitializableByToken.ts +0 -10
  27. package/lib/ergo/interfaces.ts +4 -0
  28. package/lib/ergo/network/AbstractNetwork.ts +1 -7
  29. package/lib/ergo/network/ExplorerNetwork.ts +23 -7
  30. package/lib/ergo/network/NodeNetwork.ts +25 -13
  31. package/package.json +2 -2
  32. package/tests/initializable/AbstractInitializable.spec.ts +3 -0
  33. package/tests/initializable/testData.ts +1 -0
  34. package/tests/network/ExplorerNetwork.spec.ts +31 -0
  35. package/tests/network/NodeNetwork.spec.ts +37 -0
  36. package/tests/network/testData.ts +1232 -0
  37. package/tsconfig.build.tsbuildinfo +1 -1
@@ -34,13 +34,6 @@ export abstract class AbstractInitializableErgoExtractor<
34
34
  limit: number
35
35
  ) => Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }>;
36
36
 
37
- /**
38
- * return block information of specified tx
39
- * @param txId
40
- * @return block info
41
- */
42
- abstract getTxBlock: (txId: string) => Promise<BlockInfo>;
43
-
44
37
  /**
45
38
  * return all related data below the initial height (including the init height)
46
39
  * @param initialHeight
@@ -75,6 +68,8 @@ export abstract class AbstractInitializableErgoExtractor<
75
68
  ...data,
76
69
  spendBlock: spent ? box.spentBlockId : undefined,
77
70
  spendHeight: spent ? box.spentHeight : undefined,
71
+ spendTxId: spent ? box.spentTransactionId : undefined,
72
+ spendIndex: spent ? box.spentIndex : undefined,
78
73
  } as ExtractedData;
79
74
  this.logger.debug(
80
75
  `Extracted data ${JsonBigInt.stringify(extractedData)} from box ${
@@ -2,7 +2,6 @@ import { AbstractLogger } from '@rosen-bridge/abstract-logger';
2
2
 
3
3
  import { ErgoExtractedData, ErgoNetworkType, ErgoBox } from '../interfaces';
4
4
  import { AbstractInitializableErgoExtractor } from './AbstractInitializable';
5
- import { BlockInfo } from '../../interfaces';
6
5
  import { ExplorerNetwork } from '../network/ExplorerNetwork';
7
6
  import { NodeNetwork } from '../network/NodeNetwork';
8
7
  import { AbstractNetwork } from '../network/AbstractNetwork';
@@ -41,13 +40,4 @@ export abstract class AbstractInitializableByAddressErgoExtractor<
41
40
  ): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
42
41
  return this.network.getBoxesByAddress(this.address, offset, limit);
43
42
  };
44
-
45
- /**
46
- * return block information from the specified network source
47
- * @param txId
48
- * @return block info
49
- */
50
- getTxBlock = (txId: string): Promise<BlockInfo> => {
51
- return this.network.getTxBlock(txId);
52
- };
53
43
  }
@@ -2,7 +2,6 @@ import { AbstractLogger } from '@rosen-bridge/abstract-logger';
2
2
 
3
3
  import { ErgoExtractedData, ErgoNetworkType, ErgoBox } from '../interfaces';
4
4
  import { AbstractInitializableErgoExtractor } from './AbstractInitializable';
5
- import { BlockInfo } from '../../interfaces';
6
5
  import { ExplorerNetwork } from '../network/ExplorerNetwork';
7
6
  import { NodeNetwork } from '../network/NodeNetwork';
8
7
  import { AbstractNetwork } from '../network/AbstractNetwork';
@@ -41,13 +40,4 @@ export abstract class AbstractInitializableByTokenErgoExtractor<
41
40
  ): Promise<{ boxes: ErgoBox[]; hasNextBatch: boolean }> => {
42
41
  return this.network.getBoxesByTokenId(this.tokenId, offset, limit);
43
42
  };
44
-
45
- /**
46
- * return block information from the specified network source
47
- * @param txId
48
- * @return block info
49
- */
50
- getTxBlock = (txId: string): Promise<BlockInfo> => {
51
- return this.network.getTxBlock(txId);
52
- };
53
43
  }
@@ -41,6 +41,8 @@ export interface ErgoBox extends OutputBox {
41
41
  inclusionHeight: number;
42
42
  spentBlockId?: string;
43
43
  spentHeight?: number;
44
+ spentTransactionId?: string;
45
+ spentIndex?: number;
44
46
  }
45
47
 
46
48
  export type Transaction = {
@@ -63,4 +65,6 @@ export interface ErgoExtractedData {
63
65
  blockId: string;
64
66
  spendBlock?: string;
65
67
  spendHeight?: number;
68
+ spendTxId?: string;
69
+ spendIndex?: number;
66
70
  }
@@ -1,12 +1,6 @@
1
- import { BlockInfo } from '../../interfaces';
2
1
  import { ErgoBox } from '../interfaces';
3
- export abstract class AbstractNetwork {
4
- /**
5
- * return block information of specified tx
6
- * @param txId
7
- */
8
- abstract getTxBlock: (txId: string) => Promise<BlockInfo>;
9
2
 
3
+ export abstract class AbstractNetwork {
10
4
  /**
11
5
  * return related boxes by specified address with limit offset
12
6
  * @param address
@@ -4,6 +4,7 @@ import { BlockInfo } from '../../interfaces';
4
4
  import { ErgoBox } from '../interfaces';
5
5
  import { AbstractNetwork } from './AbstractNetwork';
6
6
  import { V1 } from '@rosen-clients/ergo-explorer';
7
+ import { mapValues, pick } from 'lodash-es';
7
8
 
8
9
  export class ExplorerNetwork extends AbstractNetwork {
9
10
  private api;
@@ -14,14 +15,24 @@ export class ExplorerNetwork extends AbstractNetwork {
14
15
  }
15
16
 
16
17
  /**
17
- * return block information of specified tx
18
- * @param txId
18
+ * return spending information of a specified box by having spendTxId
19
+ * @param boxId
20
+ * @param spendTxId
19
21
  */
20
- getTxBlock = async (txId: string): Promise<BlockInfo> => {
21
- const tx = await this.api.v1.getApiV1TransactionsP1(txId);
22
+ getSpendingInfo = async (
23
+ boxId: string,
24
+ spendTxId: string
25
+ ): Promise<BlockInfo & { spendIndex: number }> => {
26
+ const tx = await this.api.v1.getApiV1TransactionsP1(spendTxId);
27
+ const spendIndex = tx.inputs?.findIndex((box) => box.boxId === boxId);
28
+ if (spendIndex == undefined)
29
+ throw Error(
30
+ `Impossible behavior, the box [${boxId}] should have been spent in tx [${spendTxId}]`
31
+ );
22
32
  return {
23
33
  hash: tx.blockId,
24
34
  height: tx.inclusionHeight,
35
+ spendIndex,
25
36
  };
26
37
  };
27
38
 
@@ -32,7 +43,7 @@ export class ExplorerNetwork extends AbstractNetwork {
32
43
  */
33
44
  convertBox = async (box: V1.OutputInfo): Promise<ErgoBox> => {
34
45
  const spendInfo = box.spentTransactionId
35
- ? await this.getTxBlock(box.spentTransactionId)
46
+ ? await this.getSpendingInfo(box.boxId, box.spentTransactionId)
36
47
  : undefined;
37
48
  return {
38
49
  blockId: box.blockId,
@@ -43,10 +54,15 @@ export class ExplorerNetwork extends AbstractNetwork {
43
54
  index: box.index,
44
55
  transactionId: box.transactionId,
45
56
  value: box.value,
46
- additionalRegisters: box.additionalRegisters,
47
- assets: box.assets,
57
+ additionalRegisters: mapValues(
58
+ box.additionalRegisters,
59
+ 'serializedValue'
60
+ ),
61
+ assets: box.assets?.map((asset) => pick(asset, ['tokenId', 'amount'])),
48
62
  spentHeight: spendInfo?.height,
49
63
  spentBlockId: spendInfo?.hash,
64
+ spentTransactionId: box.spentTransactionId,
65
+ spentIndex: spendInfo?.spendIndex,
50
66
  };
51
67
  };
52
68
 
@@ -19,10 +19,10 @@ export class NodeNetwork extends AbstractNetwork {
19
19
  * @param box
20
20
  * @returns ErgoBox
21
21
  */
22
- convertToErgoBox = async (box: IndexedErgoBox): Promise<ErgoBox> => {
23
- const tx = await this.getTxBlock(box.transactionId!);
22
+ convertBox = async (box: IndexedErgoBox): Promise<ErgoBox> => {
23
+ const tx = await await this.api.getTxById(box.transactionId!);
24
24
  const spendInfo = box.spentTransactionId
25
- ? await this.getTxBlock(box.spentTransactionId)
25
+ ? await this.getSpendingInfo(box.boxId!, box.spentTransactionId)
26
26
  : undefined;
27
27
  return {
28
28
  transactionId: box.transactionId || '',
@@ -30,25 +30,37 @@ export class NodeNetwork extends AbstractNetwork {
30
30
  value: box.value || 0n,
31
31
  ergoTree: box.ergoTree || '',
32
32
  creationHeight: box.creationHeight || 0,
33
- inclusionHeight: tx.height,
33
+ inclusionHeight: tx.inclusionHeight,
34
34
  assets: box.assets || [],
35
35
  additionalRegisters: box.additionalRegisters,
36
36
  boxId: box.boxId || '',
37
- blockId: tx.hash,
38
- spentBlockId: spendInfo?.hash ?? undefined,
39
- spentHeight: spendInfo?.height ?? undefined,
37
+ blockId: tx.blockId,
38
+ spentBlockId: spendInfo?.hash,
39
+ spentHeight: spendInfo?.height,
40
+ spentTransactionId: box.spentTransactionId,
41
+ spentIndex: spendInfo?.spendIndex,
40
42
  };
41
43
  };
42
44
 
43
45
  /**
44
- * return block information of specified tx
45
- * @param txId
46
+ * return spending information of a specified box by having spendTxId
47
+ * @param boxId
48
+ * @param spendTxId
46
49
  */
47
- getTxBlock = async (txId: string): Promise<BlockInfo> => {
48
- const tx = await this.api.getTxById(txId);
50
+ getSpendingInfo = async (
51
+ boxId: string,
52
+ spendTxId: string
53
+ ): Promise<BlockInfo & { spendIndex: number }> => {
54
+ const tx = await this.api.getTxById(spendTxId);
55
+ const spendIndex = tx.inputs?.findIndex((box) => box.boxId == boxId);
56
+ if (spendIndex == undefined)
57
+ throw Error(
58
+ `Impossible behavior, the box [${boxId}] should have been spent in tx [${spendTxId}]`
59
+ );
49
60
  return {
50
61
  hash: tx.blockId,
51
62
  height: tx.inclusionHeight,
63
+ spendIndex,
52
64
  };
53
65
  };
54
66
 
@@ -72,7 +84,7 @@ export class NodeNetwork extends AbstractNetwork {
72
84
  if (!boxes)
73
85
  throw new Error('Ergo node BoxesByAddress api expected to have items');
74
86
  const ergoBoxes = await Promise.all(
75
- boxes.map(async (box) => await this.convertToErgoBox(box))
87
+ boxes.map(async (box) => await this.convertBox(box))
76
88
  );
77
89
  return { boxes: ergoBoxes, hasNextBatch: boxes.length > 0 };
78
90
  };
@@ -96,7 +108,7 @@ export class NodeNetwork extends AbstractNetwork {
96
108
  if (!boxes.items)
97
109
  throw new Error('Ergo node BoxesByTokenId api expected to have items');
98
110
  const ergoBoxes = await Promise.all(
99
- boxes.items.map(async (box) => await this.convertToErgoBox(box))
111
+ boxes.items.map(async (box) => await this.convertBox(box))
100
112
  );
101
113
  return { boxes: ergoBoxes, hasNextBatch: boxes.items.length > 0 };
102
114
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@rosen-bridge/abstract-extractor",
3
- "version": "0.1.5",
3
+ "version": "0.2.0",
4
4
  "description": "Rosen Bridge extractor interfaces to work with scanner",
5
5
  "repository": "",
6
6
  "license": "GPL-3.0",
@@ -35,7 +35,7 @@
35
35
  },
36
36
  "dependencies": {
37
37
  "@rosen-bridge/abstract-logger": "^1.0.0",
38
- "@rosen-clients/ergo-explorer": "^1.1.1",
38
+ "@rosen-clients/ergo-explorer": "^1.1.2",
39
39
  "@rosen-clients/ergo-node": "^1.1.1",
40
40
  "lodash-es": "^4.17.21"
41
41
  }
@@ -111,6 +111,9 @@ describe('AbstractInitializableErgoExtractorAction', () => {
111
111
  spendBlock:
112
112
  '9239ebca7b3b10701895e491a2213da4e07a37abc413d05434a5fab04993a19d',
113
113
  spendHeight: 1252680,
114
+ spendTxId:
115
+ 'b08fa920a6907d0e76eb0ad754439f1a26fa112b39f4de489620e4e6241930b7',
116
+ spendIndex: 1,
114
117
  },
115
118
  ],
116
119
  hasNextBatch: true,
@@ -51,6 +51,7 @@ export const ergoBoxes = [
51
51
  '9239ebca7b3b10701895e491a2213da4e07a37abc413d05434a5fab04993a19d',
52
52
  spentTransactionId:
53
53
  'b08fa920a6907d0e76eb0ad754439f1a26fa112b39f4de489620e4e6241930b7',
54
+ spentIndex: 1,
54
55
  },
55
56
  ];
56
57
 
@@ -0,0 +1,31 @@
1
+ import { describe, expect, it, vitest } from 'vitest';
2
+ import ergoExplorerClientFactory from '@rosen-clients/ergo-explorer';
3
+
4
+ import { ExplorerNetwork } from '../../lib';
5
+ import { convertedBox, explorerBox, explorerTxInfo } from './testData';
6
+
7
+ vitest.mock('@rosen-clients/ergo-explorer');
8
+
9
+ describe('ExplorerNetwork', () => {
10
+ describe('convertBox', () => {
11
+ /**
12
+ * @target covertBox should properly convert explorer api box to ergo box
13
+ * @dependencies
14
+ * @scenario
15
+ * - mock getApiV1TransactionsP1 to return spending transaction
16
+ * - run test (call `covertBox`)
17
+ * @expected
18
+ * - to convert box properly
19
+ */
20
+ it('should properly convert explorer api box to ergo box', async () => {
21
+ vitest.mocked(ergoExplorerClientFactory).mockReturnValue({
22
+ v1: {
23
+ getApiV1TransactionsP1: async () => explorerTxInfo,
24
+ },
25
+ } as unknown as ReturnType<typeof ergoExplorerClientFactory>);
26
+ const explorerNetwork = new ExplorerNetwork('explorer_url');
27
+ const ergoBox = await explorerNetwork.convertBox(explorerBox);
28
+ expect(ergoBox).toEqual(convertedBox);
29
+ });
30
+ });
31
+ });
@@ -0,0 +1,37 @@
1
+ import { describe, expect, it, vitest } from 'vitest';
2
+ import ergoNodeClientFactory from '@rosen-clients/ergo-node';
3
+
4
+ import { NodeNetwork } from '../../lib';
5
+ import {
6
+ convertedBox,
7
+ nodeBox,
8
+ nodeSpendingTxInfo,
9
+ nodeCreationTxInfo,
10
+ } from './testData';
11
+
12
+ vitest.mock('@rosen-clients/ergo-node');
13
+
14
+ describe('NodeNetwork', () => {
15
+ describe('convertBox', () => {
16
+ /**
17
+ * @target covertBox should properly convert node api box to ergo box
18
+ * @dependencies
19
+ * @scenario
20
+ * - mock getTxById to return creation and spending transaction
21
+ * - run test (call `covertBox`)
22
+ * @expected
23
+ * - to convert box properly
24
+ */
25
+ it('should properly convert node api box to ergo box', async () => {
26
+ vitest.mocked(ergoNodeClientFactory).mockReturnValue({
27
+ getTxById: async (txId: string) => {
28
+ if (txId == nodeSpendingTxInfo.id) return nodeSpendingTxInfo;
29
+ else return nodeCreationTxInfo;
30
+ },
31
+ } as unknown as ReturnType<typeof ergoNodeClientFactory>);
32
+ const nodeNetwork = new NodeNetwork('node_url');
33
+ const ergoBox = await nodeNetwork.convertBox(nodeBox);
34
+ expect(ergoBox).toEqual(convertedBox);
35
+ });
36
+ });
37
+ });