@subql/node-starknet 0.0.2-7 → 0.0.2-9

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 (39) hide show
  1. package/dist/.tsbuildinfo +1 -1
  2. package/dist/indexer/blockDispatcher/block-dispatcher.service.js.map +1 -1
  3. package/dist/indexer/dictionary/v1/starknetDictionaryV1.d.ts +0 -1
  4. package/dist/indexer/dictionary/v1/starknetDictionaryV1.js +26 -47
  5. package/dist/indexer/dictionary/v1/starknetDictionaryV1.js.map +1 -1
  6. package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.d.ts +1 -0
  7. package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.js +189 -0
  8. package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.js.map +1 -0
  9. package/dist/indexer/fetch.service.js +1 -1
  10. package/dist/indexer/fetch.service.js.map +1 -1
  11. package/dist/indexer/project.service.js +1 -1
  12. package/dist/indexer/project.service.js.map +1 -1
  13. package/dist/indexer/types.js +5 -2
  14. package/dist/indexer/types.js.map +1 -1
  15. package/dist/indexer/unfinalizedBlocks.service.d.ts +0 -2
  16. package/dist/indexer/unfinalizedBlocks.service.js +0 -2
  17. package/dist/indexer/unfinalizedBlocks.service.js.map +1 -1
  18. package/dist/starknet/api.service.starknet.test.js +0 -4
  19. package/dist/starknet/api.service.starknet.test.js.map +1 -1
  20. package/dist/starknet/api.starknet.d.ts +7 -9
  21. package/dist/starknet/api.starknet.js +52 -90
  22. package/dist/starknet/api.starknet.js.map +1 -1
  23. package/dist/starknet/api.starknet.test.js +12 -5
  24. package/dist/starknet/api.starknet.test.js.map +1 -1
  25. package/dist/starknet/block.starknet.js +20 -12
  26. package/dist/starknet/block.starknet.js.map +1 -1
  27. package/dist/starknet/block.starknet.test.js +36 -1
  28. package/dist/starknet/block.starknet.test.js.map +1 -1
  29. package/dist/starknet/finalized.block.starknet.d.ts +21 -0
  30. package/dist/starknet/finalized.block.starknet.js +93 -0
  31. package/dist/starknet/finalized.block.starknet.js.map +1 -0
  32. package/dist/starknet/finalized.block.starknet.spec.d.ts +1 -0
  33. package/dist/starknet/finalized.block.starknet.spec.js +94 -0
  34. package/dist/starknet/finalized.block.starknet.spec.js.map +1 -0
  35. package/dist/starknet/utils.starknet.d.ts +40 -7
  36. package/dist/starknet/utils.starknet.js +50 -21
  37. package/dist/starknet/utils.starknet.js.map +1 -1
  38. package/dist/utils/project.js.map +1 -1
  39. package/package.json +4 -4
@@ -1 +1 @@
1
- {"version":3,"file":"block.starknet.test.js","sourceRoot":"","sources":["../../src/starknet/block.starknet.test.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { EventEmitter2 } from '@nestjs/event-emitter';\nimport { StarknetBlock } from '@subql/types-starknet';\nimport { StarknetApi } from './api.starknet';\nimport {\n filterLogsProcessor,\n filterTransactionsProcessor,\n} from './block.starknet';\nimport { hexEq } from './utils.starknet';\n\nconst HTTP_ENDPOINT = 'https://free-rpc.nethermind.io/mainnet-juno/v0_7';\n\njest.setTimeout(100000);\n\ndescribe('block filters', () => {\n let strkApi: StarknetApi;\n const eventEmitter = new EventEmitter2();\n let blockData: StarknetBlock;\n\n const fetchBlock = async (height: number) => {\n const block = await strkApi.fetchBlock(height);\n\n return block.block as StarknetBlock;\n };\n\n beforeAll(async () => {\n strkApi = new StarknetApi(HTTP_ENDPOINT, eventEmitter);\n await strkApi.init();\n });\n\n describe('Filter transactions', () => {\n it('filter with invoke tx', async () => {\n // https://starkscan.co/tx/0x00b3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443\n const block = await fetchBlock(986480);\n const tx = block.transactions.find(\n (tx) =>\n tx.hash ===\n '0xb3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443',\n );\n // Filter by address\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n // StarkGate: STRK Token Transfer\n function:\n '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e',\n },\n '0x0xxxxx',\n ),\n ).toBeFalsy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n // Expect filtered match transaction will attach will decodedCalls\n expect(tx!.decodedCalls?.length).toBe(4);\n\n // filter function selector with both text and hex\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function: 'approve',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeFalsy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function:\n '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function: 'transfer',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n //Filter with \"to\"\n expect(\n filterTransactionsProcessor(tx!, {\n to: '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n }),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(tx!, {\n to: '0x047aaaaad',\n }),\n ).toBeFalsy();\n });\n\n it('should filter tx with multiple conditions', async () => {\n const block_997058 = await fetchBlock(997058);\n // https://starkscan.co/tx/0x055588e82f864f830c4d1d1117e6e8d61a917ef18cf79961af001dc321d96cb3\n const txWithdraw = block_997058.transactions.find(\n (tx) =>\n tx.hash ===\n '0x55588e82f864f830c4d1d1117e6e8d61a917ef18cf79961af001dc321d96cb3',\n );\n expect(\n filterTransactionsProcessor(txWithdraw!, {\n to: '0x04c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05',\n function: 'withdraw',\n type: 'INVOKE',\n }),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(txWithdraw!, {\n to: '0x04c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05',\n function:\n '0x015511cc3694f64379908437d6d64458dc76d02482052bfb8a5b33a72c054c77', // with extra 0 padding\n type: 'INVOKE',\n }),\n ).toBeTruthy();\n });\n\n it('decodeCalls only on filtered tx', async () => {\n // https://starkscan.co/tx/0x00b3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443\n const block = await fetchBlock(986480);\n const tx = block.transactions.find(\n (tx) =>\n tx.hash ===\n '0xb3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443',\n );\n // Filter should be failed, and it should not decode calls\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n function: 'mockFn',\n },\n '0x0xxxxx',\n ),\n ).toBeFalsy();\n\n expect(tx!.decodedCalls).toBeUndefined();\n });\n\n it('filter L1 / Invoke v0 transaction', async () => {\n // https://starkscan.co/tx/0x043ce8e6e2ad703a81701f85ce26a8fb32ad54cc2fac7685ed0b1367a4813ade\n const block = await fetchBlock(981920);\n const tx = block.transactions.find((tx) =>\n hexEq(\n tx.hash,\n '0x043ce8e6e2ad703a81701f85ce26a8fb32ad54cc2fac7685ed0b1367a4813ade',\n ),\n );\n\n // Filter by address\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'L1_HANDLER',\n },\n '0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82',\n ),\n ).toBeTruthy();\n\n // Filter by Selector\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function:\n '0x01b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb19',\n },\n '0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82',\n ),\n ).toBeTruthy();\n });\n });\n\n describe('Filter logs', () => {\n it('filter events with plain text also hex', async () => {\n // https://starkscan.co/tx/0x04b546ff4375c16c30c03bd92d2d9082041e5e9397f5bda4f832661d6c029655#events\n const block = await fetchBlock(986480);\n\n const testLog = block.logs[2]!;\n expect(testLog.transaction.hash).toEqual(\n '0x4b546ff4375c16c30c03bd92d2d9082041e5e9397f5bda4f832661d6c029655',\n );\n // Filter with \"address\"\n expect(\n filterLogsProcessor(\n block.logs[2]!,\n {},\n '0x06a9e4c6f0799160ea8ddc43ff982a5f83d7f633e9732ce42701de1288ff705f',\n ),\n ).toBeTruthy();\n\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: ['Transfer'],\n }),\n ).toBeFalsy();\n\n // Event name can be in plain text or hex\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: [\n '0x1a2f334228cee715f1f0f54053bb6b5eac54fa336e0bc1aacf7516decb0471d',\n ],\n }),\n ).toBeTruthy();\n\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: ['StoreSetRecord'],\n }),\n ).toBeTruthy();\n });\n });\n});\n"]}
1
+ {"version":3,"file":"block.starknet.test.js","sourceRoot":"","sources":["../../src/starknet/block.starknet.test.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { EventEmitter2 } from '@nestjs/event-emitter';\nimport { StarknetBlock, StarknetTransaction } from '@subql/types-starknet';\nimport { StarknetApi } from './api.starknet';\nimport {\n filterLogsProcessor,\n filterTransactionsProcessor,\n} from './block.starknet';\nimport { hexEq } from './utils.starknet';\n\nconst HTTP_ENDPOINT = 'https://free-rpc.nethermind.io/mainnet-juno/v0_7';\n\njest.setTimeout(100000);\n\ndescribe('block filters', () => {\n let strkApi: StarknetApi;\n const eventEmitter = new EventEmitter2();\n\n const fetchBlock = async (height: number) => {\n const block = await strkApi.fetchBlock(height);\n\n return block.block as StarknetBlock;\n };\n\n beforeAll(async () => {\n strkApi = new StarknetApi(HTTP_ENDPOINT, eventEmitter);\n await strkApi.init();\n });\n\n describe('Filter transactions', () => {\n it('filter with invoke tx', async () => {\n // https://starkscan.co/tx/0x00b3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443\n const block = await fetchBlock(986480);\n const tx = block.transactions.find(\n (tx) =>\n tx.hash ===\n '0xb3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443',\n );\n // Filter by address\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n // StarkGate: STRK Token Transfer\n function:\n '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e',\n },\n '0x0xxxxx',\n ),\n ).toBeFalsy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n // Expect filtered match transaction will attach will decodedCalls\n expect(tx!.decodedCalls?.length).toBe(4);\n\n // filter function selector with both text and hex\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function: 'approve',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeFalsy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function:\n '0x83afd3f4caedc6eebf44246fe54e38c95e3179a5ec9ea81740eca5b482d12e',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function: 'transfer',\n },\n '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n ),\n ).toBeTruthy();\n\n //Filter with \"to\"\n expect(\n filterTransactionsProcessor(tx!, {\n to: '0x04718f5a0fc34cc1af16a1cdee98ffb20c31f5cd61d6ab07201858f4287c938d',\n }),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(tx!, {\n to: '0x047aaaaad',\n }),\n ).toBeFalsy();\n\n //Filter with function is \"null\"\n expect(\n filterTransactionsProcessor(tx!, {\n function: null,\n }),\n ).toBeFalsy();\n\n //Filter with to is \"null\"\n expect(\n filterTransactionsProcessor(tx!, {\n to: null,\n }),\n ).toBeFalsy();\n });\n\n it('filter with tx function or to is null', () => {\n const mockTx1 = {\n calldata: ['0xmock', null],\n parseCallData: jest.fn(),\n } as unknown as StarknetTransaction;\n //Filter with function is \"null\"\n expect(\n filterTransactionsProcessor(mockTx1, {\n function: null,\n }),\n ).toBeTruthy();\n\n //Filter with to is \"null\"\n expect(\n filterTransactionsProcessor(mockTx1, {\n to: null,\n }),\n ).toBeTruthy();\n\n // test with decode calls\n const mockTx2 = {\n parseCallData: () => {\n return [{ to: null, selector: null, calldata: [] }];\n },\n } as unknown as StarknetTransaction;\n\n //Filter with function is \"null\"\n expect(\n filterTransactionsProcessor(mockTx2, {\n function: null,\n }),\n ).toBeTruthy();\n\n //Filter with to is \"null\"\n expect(\n filterTransactionsProcessor(mockTx2, {\n to: null,\n }),\n ).toBeTruthy();\n });\n\n it('should filter tx with multiple conditions', async () => {\n const block_997058 = await fetchBlock(997058);\n // https://starkscan.co/tx/0x055588e82f864f830c4d1d1117e6e8d61a917ef18cf79961af001dc321d96cb3\n const txWithdraw = block_997058.transactions.find(\n (tx) =>\n tx.hash ===\n '0x55588e82f864f830c4d1d1117e6e8d61a917ef18cf79961af001dc321d96cb3',\n );\n expect(\n filterTransactionsProcessor(txWithdraw!, {\n to: '0x04c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05',\n function: 'withdraw',\n type: 'INVOKE',\n }),\n ).toBeTruthy();\n\n expect(\n filterTransactionsProcessor(txWithdraw!, {\n to: '0x04c0a5193d58f74fbace4b74dcf65481e734ed1714121bdc571da345540efa05',\n function:\n '0x015511cc3694f64379908437d6d64458dc76d02482052bfb8a5b33a72c054c77', // with extra 0 padding\n type: 'INVOKE',\n }),\n ).toBeTruthy();\n });\n\n it('decodeCalls only on filtered tx', async () => {\n // https://starkscan.co/tx/0x00b3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443\n const block = await fetchBlock(986480);\n const tx = block.transactions.find(\n (tx) =>\n tx.hash ===\n '0xb3173b7a65b32fc8669da8f4676a7ef10c6f58ddd3159db7c0cd3de1025443',\n );\n // Filter should be failed, and it should not decode calls\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'INVOKE',\n function: 'mockFn',\n },\n '0x0xxxxx',\n ),\n ).toBeFalsy();\n\n expect(tx!.decodedCalls).toBeUndefined();\n });\n\n it('filter L1 / Invoke v0 transaction', async () => {\n // https://starkscan.co/tx/0x043ce8e6e2ad703a81701f85ce26a8fb32ad54cc2fac7685ed0b1367a4813ade\n const block = await fetchBlock(981920);\n const tx = block.transactions.find((tx) =>\n hexEq(\n tx.hash,\n '0x043ce8e6e2ad703a81701f85ce26a8fb32ad54cc2fac7685ed0b1367a4813ade',\n ),\n );\n\n // Filter by address\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n type: 'L1_HANDLER',\n },\n '0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82',\n ),\n ).toBeTruthy();\n\n // Filter by Selector\n expect(\n filterTransactionsProcessor(\n tx!,\n {\n function:\n '0x01b64b1b3b690b43b9b514fb81377518f4039cd3e4f4914d8a6bdf01d679fb19',\n },\n '0x073314940630fd6dcda0d772d4c972c4e0a9946bef9dabf4ef84eda8ef542b82',\n ),\n ).toBeTruthy();\n });\n });\n\n describe('Filter logs', () => {\n it('filter events with plain text also hex', async () => {\n // https://starkscan.co/tx/0x04b546ff4375c16c30c03bd92d2d9082041e5e9397f5bda4f832661d6c029655#events\n const block = await fetchBlock(986480);\n\n const testLog = block.logs[2]!;\n expect(testLog.transaction.hash).toEqual(\n '0x4b546ff4375c16c30c03bd92d2d9082041e5e9397f5bda4f832661d6c029655',\n );\n // Filter with \"address\"\n expect(\n filterLogsProcessor(\n block.logs[2]!,\n {},\n '0x06a9e4c6f0799160ea8ddc43ff982a5f83d7f633e9732ce42701de1288ff705f',\n ),\n ).toBeTruthy();\n\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: ['Transfer'],\n }),\n ).toBeFalsy();\n\n // Event name can be in plain text or hex\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: [\n '0x1a2f334228cee715f1f0f54053bb6b5eac54fa336e0bc1aacf7516decb0471d',\n ],\n }),\n ).toBeTruthy();\n\n expect(\n filterLogsProcessor(block.logs[2]!, {\n topics: ['StoreSetRecord'],\n }),\n ).toBeTruthy();\n });\n });\n});\n"]}
@@ -0,0 +1,21 @@
1
+ import P from 'pino';
2
+ import { SPEC } from 'starknet-types-07';
3
+ /***
4
+
5
+ Due to the current limitations of Starknet's RPC methods, there is no direct way to retrieve the latest ACCEPTED_ON_L1 block (which we consider as the finalized block).
6
+
7
+ To address this, we use a binary search approach to efficiently find the latest finalized block by searching backward. The number of blocks between the best head and the finalized head is typically around 1,671.
8
+
9
+ Depending on the RPC response time, this implementation can find the finalized block within approximately 15 RPC calls and 4 seconds.
10
+
11
+ Since block statuses do not change frequently, we implement caching to improve performance and further reduce the number of RPC calls.
12
+ ***/
13
+ export declare class FinalizedBlockService {
14
+ private getBlock;
15
+ private logger;
16
+ private latestAcceptedBlock?;
17
+ constructor(getBlock: (heightOrHash: number | string) => Promise<SPEC.BLOCK_WITH_RECEIPTS>, logger: P.Logger);
18
+ private findFirstAcceptedOnL1;
19
+ private binarySearchAcceptedOnL1;
20
+ getFinalizedBlock(): Promise<SPEC.BLOCK_WITH_RECEIPTS>;
21
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ // Copyright 2020-2024 SubQuery Pte Ltd authors & contributors
3
+ // SPDX-License-Identifier: GPL-3.0
4
+ var __decorate = (this && this.__decorate) || function (decorators, target, key, desc) {
5
+ var c = arguments.length, r = c < 3 ? target : desc === null ? desc = Object.getOwnPropertyDescriptor(target, key) : desc, d;
6
+ if (typeof Reflect === "object" && typeof Reflect.decorate === "function") r = Reflect.decorate(decorators, target, key, desc);
7
+ else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
8
+ return c > 3 && r && Object.defineProperty(target, key, r), r;
9
+ };
10
+ var __metadata = (this && this.__metadata) || function (k, v) {
11
+ if (typeof Reflect === "object" && typeof Reflect.metadata === "function") return Reflect.metadata(k, v);
12
+ };
13
+ Object.defineProperty(exports, "__esModule", { value: true });
14
+ exports.FinalizedBlockService = void 0;
15
+ const node_core_1 = require("@subql/node-core");
16
+ const INIT_BINARY_JUMP = 1000;
17
+ /***
18
+
19
+ Due to the current limitations of Starknet's RPC methods, there is no direct way to retrieve the latest ACCEPTED_ON_L1 block (which we consider as the finalized block).
20
+
21
+ To address this, we use a binary search approach to efficiently find the latest finalized block by searching backward. The number of blocks between the best head and the finalized head is typically around 1,671.
22
+
23
+ Depending on the RPC response time, this implementation can find the finalized block within approximately 15 RPC calls and 4 seconds.
24
+
25
+ Since block statuses do not change frequently, we implement caching to improve performance and further reduce the number of RPC calls.
26
+ ***/
27
+ class FinalizedBlockService {
28
+ getBlock;
29
+ logger;
30
+ latestAcceptedBlock; // cache latest ACCEPTED_ON_L1
31
+ constructor(getBlock, logger) {
32
+ this.getBlock = getBlock;
33
+ this.logger = logger;
34
+ }
35
+ // **Jump from latest to left side, from a block with Accepted on layer 1, as a start point**
36
+ async findFirstAcceptedOnL1() {
37
+ const latestBlock = await this.getBlock('latest');
38
+ let currentBlockNumber = latestBlock.block_number;
39
+ while (currentBlockNumber > 0) {
40
+ const blockInfo = await this.getBlock(currentBlockNumber);
41
+ if (blockInfo && blockInfo.status === 'ACCEPTED_ON_L1') {
42
+ return blockInfo;
43
+ }
44
+ // Ensure we never go below zero
45
+ currentBlockNumber = Math.max(1, currentBlockNumber - INIT_BINARY_JUMP);
46
+ }
47
+ return undefined;
48
+ }
49
+ async binarySearchAcceptedOnL1(low, high) {
50
+ let latestAcceptedBlock;
51
+ while (low <= high) {
52
+ const mid = Math.floor((low + high) / 2);
53
+ const blockInfo = await this.getBlock(mid);
54
+ if (blockInfo.status === 'ACCEPTED_ON_L1') {
55
+ latestAcceptedBlock = blockInfo;
56
+ low = mid + 1; // Move to higher blocks
57
+ }
58
+ else {
59
+ high = mid - 1; // Move to lower blocks
60
+ }
61
+ }
62
+ return latestAcceptedBlock;
63
+ }
64
+ async getFinalizedBlock() {
65
+ if (!this.latestAcceptedBlock) {
66
+ this.latestAcceptedBlock = await this.findFirstAcceptedOnL1();
67
+ if (!this.latestAcceptedBlock) {
68
+ throw new Error('No ACCEPTED_ON_L1 blocks found.');
69
+ }
70
+ }
71
+ const nextBlockNumber = this.latestAcceptedBlock.block_number + 1;
72
+ const nextBlockInfo = await this.getBlock(nextBlockNumber);
73
+ // Return cached block if no update is detected
74
+ if (!nextBlockInfo || nextBlockInfo.status === 'ACCEPTED_ON_L2') {
75
+ return this.latestAcceptedBlock;
76
+ }
77
+ // If next block changed to ACCEPTED_ON_L1, perform binary search
78
+ if (nextBlockInfo.status === 'ACCEPTED_ON_L1') {
79
+ const latestBlockNumber = (await this.getBlock('latest')).block_number;
80
+ this.latestAcceptedBlock = await this.binarySearchAcceptedOnL1(nextBlockNumber, latestBlockNumber);
81
+ }
82
+ this.logger.debug(`Finalized Block Found: Block Number: ${this.latestAcceptedBlock.block_number}, Block Hash: ${this.latestAcceptedBlock.block_hash}`);
83
+ return this.latestAcceptedBlock;
84
+ }
85
+ }
86
+ exports.FinalizedBlockService = FinalizedBlockService;
87
+ __decorate([
88
+ (0, node_core_1.profiler)(),
89
+ __metadata("design:type", Function),
90
+ __metadata("design:paramtypes", []),
91
+ __metadata("design:returntype", Promise)
92
+ ], FinalizedBlockService.prototype, "getFinalizedBlock", null);
93
+ //# sourceMappingURL=finalized.block.starknet.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finalized.block.starknet.js","sourceRoot":"","sources":["../../src/starknet/finalized.block.starknet.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;;;;;;;;;;;AAEnC,gDAA4C;AAI5C,MAAM,gBAAgB,GAAG,IAAI,CAAC;AAE9B;;;;;;;;;KASK;AACL,MAAa,qBAAqB;IAItB;IAGA;IANF,mBAAmB,CAA4B,CAAC,8BAA8B;IAEtF,YACU,QAE8B,EAC9B,MAAgB;QAHhB,aAAQ,GAAR,QAAQ,CAEsB;QAC9B,WAAM,GAAN,MAAM,CAAU;IACvB,CAAC;IAEJ,6FAA6F;IACrF,KAAK,CAAC,qBAAqB;QAGjC,MAAM,WAAW,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC;QAClD,IAAI,kBAAkB,GAAG,WAAW,CAAC,YAAY,CAAC;QAElD,OAAO,kBAAkB,GAAG,CAAC,EAAE,CAAC;YAC9B,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,kBAAkB,CAAC,CAAC;YAC1D,IAAI,SAAS,IAAI,SAAS,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBACvD,OAAO,SAAS,CAAC;YACnB,CAAC;YAED,gCAAgC;YAChC,kBAAkB,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,kBAAkB,GAAG,gBAAgB,CAAC,CAAC;QAC1E,CAAC;QACD,OAAO,SAAS,CAAC;IACnB,CAAC;IAEO,KAAK,CAAC,wBAAwB,CACpC,GAAW,EACX,IAAY;QAEZ,IAAI,mBAAmB,CAAC;QAExB,OAAO,GAAG,IAAI,IAAI,EAAE,CAAC;YACnB,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,GAAG,GAAG,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC;YACzC,MAAM,SAAS,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,GAAG,CAAC,CAAC;YAC3C,IAAI,SAAS,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;gBAC1C,mBAAmB,GAAG,SAAS,CAAC;gBAChC,GAAG,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,wBAAwB;YACzC,CAAC;iBAAM,CAAC;gBACN,IAAI,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,uBAAuB;YACzC,CAAC;QACH,CAAC;QAED,OAAO,mBAAoB,CAAC;IAC9B,CAAC;IAEK,AAAN,KAAK,CAAC,iBAAiB;QACrB,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;YAC9B,IAAI,CAAC,mBAAmB,GAAG,MAAM,IAAI,CAAC,qBAAqB,EAAE,CAAC;YAC9D,IAAI,CAAC,IAAI,CAAC,mBAAmB,EAAE,CAAC;gBAC9B,MAAM,IAAI,KAAK,CAAC,iCAAiC,CAAC,CAAC;YACrD,CAAC;QACH,CAAC;QAED,MAAM,eAAe,GAAG,IAAI,CAAC,mBAAmB,CAAC,YAAY,GAAG,CAAC,CAAC;QAClE,MAAM,aAAa,GAAG,MAAM,IAAI,CAAC,QAAQ,CAAC,eAAe,CAAC,CAAC;QAE3D,+CAA+C;QAC/C,IAAI,CAAC,aAAa,IAAI,aAAa,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;YAChE,OAAO,IAAI,CAAC,mBAAmB,CAAC;QAClC,CAAC;QAED,iEAAiE;QACjE,IAAI,aAAa,CAAC,MAAM,KAAK,gBAAgB,EAAE,CAAC;YAC9C,MAAM,iBAAiB,GAAG,CAAC,MAAM,IAAI,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,YAAY,CAAC;YACvE,IAAI,CAAC,mBAAmB,GAAG,MAAM,IAAI,CAAC,wBAAwB,CAC5D,eAAe,EACf,iBAAiB,CAClB,CAAC;QACJ,CAAC;QAED,IAAI,CAAC,MAAM,CAAC,KAAK,CACf,wCAAwC,IAAI,CAAC,mBAAmB,CAAC,YAAY,iBAAiB,IAAI,CAAC,mBAAmB,CAAC,UAAU,EAAE,CACpI,CAAC;QAEF,OAAO,IAAI,CAAC,mBAAmB,CAAC;IAClC,CAAC;CACF;AAhFD,sDAgFC;AA/BO;IADL,IAAA,oBAAQ,GAAE;;;;8DA+BV","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { profiler } from '@subql/node-core';\nimport P from 'pino';\nimport { SPEC } from 'starknet-types-07';\n\nconst INIT_BINARY_JUMP = 1000;\n\n/***\n\n Due to the current limitations of Starknet's RPC methods, there is no direct way to retrieve the latest ACCEPTED_ON_L1 block (which we consider as the finalized block).\n\n To address this, we use a binary search approach to efficiently find the latest finalized block by searching backward. The number of blocks between the best head and the finalized head is typically around 1,671.\n\n Depending on the RPC response time, this implementation can find the finalized block within approximately 15 RPC calls and 4 seconds.\n\n Since block statuses do not change frequently, we implement caching to improve performance and further reduce the number of RPC calls.\n ***/\nexport class FinalizedBlockService {\n private latestAcceptedBlock?: SPEC.BLOCK_WITH_RECEIPTS; // cache latest ACCEPTED_ON_L1\n\n constructor(\n private getBlock: (\n heightOrHash: number | string,\n ) => Promise<SPEC.BLOCK_WITH_RECEIPTS>,\n private logger: P.Logger,\n ) {}\n\n // **Jump from latest to left side, from a block with Accepted on layer 1, as a start point**\n private async findFirstAcceptedOnL1(): Promise<\n SPEC.BLOCK_WITH_RECEIPTS | undefined\n > {\n const latestBlock = await this.getBlock('latest');\n let currentBlockNumber = latestBlock.block_number;\n\n while (currentBlockNumber > 0) {\n const blockInfo = await this.getBlock(currentBlockNumber);\n if (blockInfo && blockInfo.status === 'ACCEPTED_ON_L1') {\n return blockInfo;\n }\n\n // Ensure we never go below zero\n currentBlockNumber = Math.max(1, currentBlockNumber - INIT_BINARY_JUMP);\n }\n return undefined;\n }\n\n private async binarySearchAcceptedOnL1(\n low: number,\n high: number,\n ): Promise<SPEC.BLOCK_WITH_RECEIPTS> {\n let latestAcceptedBlock;\n\n while (low <= high) {\n const mid = Math.floor((low + high) / 2);\n const blockInfo = await this.getBlock(mid);\n if (blockInfo.status === 'ACCEPTED_ON_L1') {\n latestAcceptedBlock = blockInfo;\n low = mid + 1; // Move to higher blocks\n } else {\n high = mid - 1; // Move to lower blocks\n }\n }\n\n return latestAcceptedBlock!;\n }\n @profiler()\n async getFinalizedBlock(): Promise<SPEC.BLOCK_WITH_RECEIPTS> {\n if (!this.latestAcceptedBlock) {\n this.latestAcceptedBlock = await this.findFirstAcceptedOnL1();\n if (!this.latestAcceptedBlock) {\n throw new Error('No ACCEPTED_ON_L1 blocks found.');\n }\n }\n\n const nextBlockNumber = this.latestAcceptedBlock.block_number + 1;\n const nextBlockInfo = await this.getBlock(nextBlockNumber);\n\n // Return cached block if no update is detected\n if (!nextBlockInfo || nextBlockInfo.status === 'ACCEPTED_ON_L2') {\n return this.latestAcceptedBlock;\n }\n\n // If next block changed to ACCEPTED_ON_L1, perform binary search\n if (nextBlockInfo.status === 'ACCEPTED_ON_L1') {\n const latestBlockNumber = (await this.getBlock('latest')).block_number;\n this.latestAcceptedBlock = await this.binarySearchAcceptedOnL1(\n nextBlockNumber,\n latestBlockNumber,\n );\n }\n\n this.logger.debug(\n `Finalized Block Found: Block Number: ${this.latestAcceptedBlock.block_number}, Block Hash: ${this.latestAcceptedBlock.block_hash}`,\n );\n\n return this.latestAcceptedBlock;\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,94 @@
1
+ "use strict";
2
+ // Copyright 2020-2024 SubQuery Pte Ltd authors & contributors
3
+ // SPDX-License-Identifier: GPL-3.0
4
+ var __importDefault = (this && this.__importDefault) || function (mod) {
5
+ return (mod && mod.__esModule) ? mod : { "default": mod };
6
+ };
7
+ Object.defineProperty(exports, "__esModule", { value: true });
8
+ const pino_1 = __importDefault(require("pino"));
9
+ const finalized_block_starknet_1 = require("./finalized.block.starknet");
10
+ const createMockBlock = (block_number, status) => ({
11
+ block_number,
12
+ status,
13
+ block_hash: `0xabc${block_number}`,
14
+ parent_hash: `0xparent${block_number - 1}`,
15
+ new_root: `0xnewroot${block_number}`,
16
+ timestamp: 1700000000 + block_number,
17
+ sequencer_address: `0xsequencer${block_number}`,
18
+ transactions: [],
19
+ starknet_version: '',
20
+ l1_da_mode: 'BLOB',
21
+ l1_gas_price: { price_in_wei: '0', price_in_fri: '0' },
22
+ l1_data_gas_price: { price_in_wei: '0', price_in_fri: '0' },
23
+ });
24
+ // **Mock blocks**
25
+ const mockBlocks = {
26
+ latest: createMockBlock(10, 'ACCEPTED_ON_L2'),
27
+ 10: createMockBlock(10, 'ACCEPTED_ON_L2'),
28
+ 9: createMockBlock(9, 'ACCEPTED_ON_L2'),
29
+ 8: createMockBlock(8, 'ACCEPTED_ON_L2'),
30
+ 7: createMockBlock(7, 'ACCEPTED_ON_L1'), // latest ACCEPTED_ON_L1
31
+ 6: createMockBlock(6, 'ACCEPTED_ON_L1'),
32
+ 5: createMockBlock(5, 'ACCEPTED_ON_L1'),
33
+ 4: createMockBlock(4, 'ACCEPTED_ON_L1'),
34
+ 3: createMockBlock(3, 'ACCEPTED_ON_L1'),
35
+ 2: createMockBlock(2, 'ACCEPTED_ON_L1'),
36
+ 1: createMockBlock(1, 'ACCEPTED_ON_L1'),
37
+ };
38
+ const mockBlockService = {
39
+ async getBlock(heightOrHash) {
40
+ if (mockBlocks[heightOrHash] !== undefined) {
41
+ return Promise.resolve(mockBlocks[heightOrHash]);
42
+ }
43
+ return Promise.reject(new Error(`Block ${heightOrHash} not found`));
44
+ },
45
+ };
46
+ // **initialize logger**
47
+ const mockLogger = (0, pino_1.default)({ level: 'debug' });
48
+ describe('FinalizedBlockService', () => {
49
+ let service;
50
+ beforeEach(() => {
51
+ service = new finalized_block_starknet_1.FinalizedBlockService(mockBlockService.getBlock, mockLogger);
52
+ });
53
+ // **Test 1: `findFirstAcceptedOnL1`**
54
+ it('should find the first ACCEPTED_ON_L1 block', async () => {
55
+ const result = await service.findFirstAcceptedOnL1();
56
+ expect(result).toBeDefined();
57
+ expect(result?.block_number).toBe(1);
58
+ expect(result?.status).toBe('ACCEPTED_ON_L1');
59
+ });
60
+ // **Test 2: `binarySearchAcceptedOnL1`**
61
+ it('should find the latest ACCEPTED_ON_L1 block via binary search', async () => {
62
+ const result = await service.binarySearchAcceptedOnL1(1, 10);
63
+ expect(result).toBeDefined();
64
+ expect(result?.block_number).toBe(7);
65
+ expect(result?.status).toBe('ACCEPTED_ON_L1');
66
+ });
67
+ // **Test 3: `getFinalizedBlock` - First call (initialize cache)**
68
+ it('should initialize latestAcceptedBlock and return the correct block', async () => {
69
+ const result = await service.getFinalizedBlock();
70
+ expect(result).toBeDefined();
71
+ expect(result.block_number).toBe(7);
72
+ expect(result.status).toBe('ACCEPTED_ON_L1');
73
+ });
74
+ // **Test 4: `getFinalizedBlock` - Return cached block and verify `getBlock` is not called**
75
+ it('should return cached block if next block remains ACCEPTED_ON_L2 and not call `getBlock`', async () => {
76
+ const getBlockSpy = jest.spyOn(mockBlockService, 'getBlock');
77
+ let result = await service.getFinalizedBlock(); // First call - initializes cache
78
+ expect(result.block_number).toBe(7); // Should return cached block 3
79
+ jest.clearAllMocks(); // Reset call count for the second invocation
80
+ result = await service.getFinalizedBlock(); // Second call - should use cache
81
+ expect(result).toBeDefined();
82
+ expect(result.block_number).toBe(7); // Should return cached block 3
83
+ expect(getBlockSpy).not.toHaveBeenCalled(); // Ensure `getBlock` was NOT called again
84
+ });
85
+ // **Test 5: `getFinalizedBlock` - Update when next block becomes L1**
86
+ it('should update latestAcceptedBlock if next block changes to ACCEPTED_ON_L1', async () => {
87
+ await service.getFinalizedBlock(); // Initialize cache
88
+ mockBlocks[8].status = 'ACCEPTED_ON_L1'; // Change block 8 to L1
89
+ const result = await service.getFinalizedBlock();
90
+ expect(result).toBeDefined();
91
+ expect(result.block_number).toBe(8); // Now should update to block 8
92
+ });
93
+ });
94
+ //# sourceMappingURL=finalized.block.starknet.spec.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"finalized.block.starknet.spec.js","sourceRoot":"","sources":["../../src/starknet/finalized.block.starknet.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport P from 'pino';\nimport { SPEC } from 'starknet-types-07';\nimport { FinalizedBlockService } from './finalized.block.starknet';\n\nconst createMockBlock = (\n block_number: number,\n status: 'ACCEPTED_ON_L1' | 'ACCEPTED_ON_L2',\n): SPEC.BLOCK_WITH_RECEIPTS => ({\n block_number,\n status,\n block_hash: `0xabc${block_number}`,\n parent_hash: `0xparent${block_number - 1}`,\n new_root: `0xnewroot${block_number}`,\n timestamp: 1700000000 + block_number,\n sequencer_address: `0xsequencer${block_number}`,\n transactions: [],\n starknet_version: '',\n l1_da_mode: 'BLOB',\n l1_gas_price: { price_in_wei: '0', price_in_fri: '0' },\n l1_data_gas_price: { price_in_wei: '0', price_in_fri: '0' },\n});\n\n// **Mock blocks**\nconst mockBlocks: Record<number | string, SPEC.BLOCK_WITH_RECEIPTS> = {\n latest: createMockBlock(10, 'ACCEPTED_ON_L2'),\n 10: createMockBlock(10, 'ACCEPTED_ON_L2'),\n 9: createMockBlock(9, 'ACCEPTED_ON_L2'),\n 8: createMockBlock(8, 'ACCEPTED_ON_L2'),\n 7: createMockBlock(7, 'ACCEPTED_ON_L1'), // latest ACCEPTED_ON_L1\n 6: createMockBlock(6, 'ACCEPTED_ON_L1'),\n 5: createMockBlock(5, 'ACCEPTED_ON_L1'),\n 4: createMockBlock(4, 'ACCEPTED_ON_L1'),\n 3: createMockBlock(3, 'ACCEPTED_ON_L1'),\n 2: createMockBlock(2, 'ACCEPTED_ON_L1'),\n 1: createMockBlock(1, 'ACCEPTED_ON_L1'),\n};\n\nconst mockBlockService = {\n async getBlock(\n heightOrHash: number | string,\n ): Promise<SPEC.BLOCK_WITH_RECEIPTS> {\n if (mockBlocks[heightOrHash] !== undefined) {\n return Promise.resolve(mockBlocks[heightOrHash]);\n }\n return Promise.reject(new Error(`Block ${heightOrHash} not found`));\n },\n};\n\n// **initialize logger**\nconst mockLogger = P({ level: 'debug' });\n\ndescribe('FinalizedBlockService', () => {\n let service: FinalizedBlockService;\n\n beforeEach(() => {\n service = new FinalizedBlockService(mockBlockService.getBlock, mockLogger);\n });\n\n // **Test 1: `findFirstAcceptedOnL1`**\n it('should find the first ACCEPTED_ON_L1 block', async () => {\n const result = await (service as any).findFirstAcceptedOnL1();\n expect(result).toBeDefined();\n expect(result?.block_number).toBe(1);\n expect(result?.status).toBe('ACCEPTED_ON_L1');\n });\n\n // **Test 2: `binarySearchAcceptedOnL1`**\n it('should find the latest ACCEPTED_ON_L1 block via binary search', async () => {\n const result = await (service as any).binarySearchAcceptedOnL1(1, 10);\n expect(result).toBeDefined();\n expect(result?.block_number).toBe(7);\n expect(result?.status).toBe('ACCEPTED_ON_L1');\n });\n\n // **Test 3: `getFinalizedBlock` - First call (initialize cache)**\n it('should initialize latestAcceptedBlock and return the correct block', async () => {\n const result = await service.getFinalizedBlock();\n expect(result).toBeDefined();\n expect(result.block_number).toBe(7);\n expect(result.status).toBe('ACCEPTED_ON_L1');\n });\n\n // **Test 4: `getFinalizedBlock` - Return cached block and verify `getBlock` is not called**\n it('should return cached block if next block remains ACCEPTED_ON_L2 and not call `getBlock`', async () => {\n const getBlockSpy = jest.spyOn(mockBlockService, 'getBlock');\n\n let result = await service.getFinalizedBlock(); // First call - initializes cache\n expect(result.block_number).toBe(7); // Should return cached block 3\n\n jest.clearAllMocks(); // Reset call count for the second invocation\n\n result = await service.getFinalizedBlock(); // Second call - should use cache\n\n expect(result).toBeDefined();\n expect(result.block_number).toBe(7); // Should return cached block 3\n expect(getBlockSpy).not.toHaveBeenCalled(); // Ensure `getBlock` was NOT called again\n });\n\n // **Test 5: `getFinalizedBlock` - Update when next block becomes L1**\n it('should update latestAcceptedBlock if next block changes to ACCEPTED_ON_L1', async () => {\n await service.getFinalizedBlock(); // Initialize cache\n mockBlocks[8].status = 'ACCEPTED_ON_L1'; // Change block 8 to L1\n const result = await service.getFinalizedBlock();\n expect(result).toBeDefined();\n expect(result.block_number).toBe(8); // Now should update to block 8\n });\n});\n"]}
@@ -1,19 +1,52 @@
1
1
  import { SPEC } from '@starknet-io/types-js';
2
2
  import { Header, IBlock } from '@subql/node-core';
3
- import { ApiWrapper, LightStarknetBlock, StarknetBlock, StarknetLog, StarknetLogRaw, StarknetTransaction } from '@subql/types-starknet';
3
+ import { ApiWrapper, LightStarknetBlock, StarknetBlock, StarknetLog, StarknetLogRaw, StarknetTransaction, StarknetTransactionRaw } from '@subql/types-starknet';
4
4
  import { Abi, RpcProvider, TransactionReceipt } from 'starknet';
5
5
  import { BlockContent } from '../indexer/types';
6
6
  export declare function calcInterval(api: ApiWrapper): number;
7
- export declare function formatBlock(block: any): StarknetBlock;
7
+ export declare function formatBlock(block: SPEC.BLOCK_WITH_RECEIPTS | SPEC.BLOCK_WITH_TX_HASHES): Omit<StarknetBlock, 'transactions'> & {
8
+ transactions: StarknetTransactionRaw[];
9
+ };
8
10
  export declare function formatBlockUtil<B extends StarknetBlock | LightStarknetBlock = StarknetBlock>(block: B): IBlock<B>;
9
- export declare function formatLog(log: StarknetLogRaw, block: StarknetBlock): StarknetLog;
10
- export declare function formatTransaction(tx: Record<string, any>, block: StarknetBlock, txIndex: number): Omit<StarknetTransaction, 'receipt'>;
11
+ export declare function formatLog(log: StarknetLogRaw, logIndex: number, tx: StarknetTransaction, block: Omit<StarknetBlock, 'transactions'> & {
12
+ transactions: StarknetTransactionRaw[];
13
+ }): StarknetLog;
14
+ /***
15
+ * @param tx
16
+ * @param block
17
+ * @param txIndex
18
+ * Explanation for from, to, selector, calldata with different tx type
19
+ * When apply filter please refer to the following:
20
+ *
21
+ * 1. L1_HANDLER
22
+ * from is the Contract Address (contract been called)
23
+ * entryPointSelector (method)
24
+ * within decodedCalls, to is same as from, selector is the entryPointSelector
25
+ * 2. DEPLOY_ACCOUNT
26
+ * from is the contract_address, also is the new account address
27
+ * 3. DECLARE
28
+ * from is the sender_address
29
+ * 4. DEPLOY
30
+ * from is the sender_address
31
+ * 5. INVOKE V1 and V3
32
+ * from is the sender_address
33
+ * within decodedCalls, to is the contract been called, selector is the method
34
+ * 6. INVOKE V0
35
+ * from is the Contract Address (contract been called)
36
+ * entryPointSelector is the method been called
37
+ */
38
+ export declare function formatTransaction(tx: Record<string, any>, block: StarknetBlock | (Omit<StarknetBlock, 'transactions'> & {
39
+ transactions: StarknetTransactionRaw[];
40
+ }), txIndex: number): StarknetTransaction;
11
41
  export declare function getTxContractAddress(tx: Record<string, any>): string;
12
42
  export declare function formatReceipt(receipt: Record<string, any>): TransactionReceipt;
13
- export declare function starknetBlockToHeader(block: BlockContent): Header;
14
- export declare function starknetBlockHeaderToHeader(block: SPEC.BLOCK_HEADER): Header;
15
- export declare function reverseToRawLog(log: StarknetLog): StarknetLogRaw;
43
+ export declare function starknetBlockToHeader(block: BlockContent | (Omit<StarknetBlock, 'transactions'> & {
44
+ transactions: StarknetTransactionRaw[];
45
+ })): Header;
46
+ export declare function starknetBlockHeaderToHeader(block: Partial<SPEC.BLOCK_HEADER>): Header;
47
+ export declare function reverseToRawLog(log: StarknetLog): SPEC.EMITTED_EVENT;
16
48
  export declare function fetchAbiFromContractAddress(provider: RpcProvider, contractAddress: string): Promise<Abi>;
17
49
  export declare function encodeSelectorToHex(eventName: string): string;
18
50
  export declare function encodeEventKey(eventName: string): string;
19
51
  export declare function hexEq(a: string, b: string): boolean;
52
+ export declare function isFinalizedBlock(block: SPEC.BLOCK_WITH_RECEIPTS | SPEC.PENDING_BLOCK_WITH_RECEIPTS): block is SPEC.BLOCK_WITH_RECEIPTS;
@@ -16,6 +16,7 @@ exports.fetchAbiFromContractAddress = fetchAbiFromContractAddress;
16
16
  exports.encodeSelectorToHex = encodeSelectorToHex;
17
17
  exports.encodeEventKey = encodeEventKey;
18
18
  exports.hexEq = hexEq;
19
+ exports.isFinalizedBlock = isFinalizedBlock;
19
20
  const lodash_1 = require("lodash");
20
21
  const starknet_1 = require("starknet");
21
22
  const decodeCalldata_1 = require("./decodeCalldata");
@@ -44,51 +45,73 @@ function formatBlockUtil(block) {
44
45
  getHeader: () => starknetBlockToHeader(block),
45
46
  };
46
47
  }
47
- function formatLog(log, block) {
48
+ function formatLog(log, logIndex, tx, block) {
48
49
  const formattedLog = {
49
- block,
50
50
  address: log.from_address,
51
51
  topics: log.keys,
52
52
  blockNumber: block.blockNumber,
53
53
  blockHash: block.blockHash,
54
- transactionHash: log.transaction_hash,
54
+ transactionHash: tx.hash,
55
55
  data: log.data,
56
+ logIndex: logIndex,
57
+ block: block,
58
+ transaction: tx,
59
+ transactionIndex: tx.transactionIndex,
56
60
  toJSON() {
57
61
  return JSON.stringify((0, lodash_1.omit)(this, ['transaction', 'block', 'toJSON']));
58
62
  },
59
63
  };
60
- // Define this afterwards as the spread on `...log` breaks defining a getter
61
- Object.defineProperty(formattedLog, 'transaction', {
62
- get: () => {
63
- const rawTransaction = block.transactions?.find((tx) => tx.hash === log.transaction_hash);
64
- return rawTransaction;
65
- },
66
- });
67
64
  return formattedLog;
68
65
  }
66
+ /***
67
+ * @param tx
68
+ * @param block
69
+ * @param txIndex
70
+ * Explanation for from, to, selector, calldata with different tx type
71
+ * When apply filter please refer to the following:
72
+ *
73
+ * 1. L1_HANDLER
74
+ * from is the Contract Address (contract been called)
75
+ * entryPointSelector (method)
76
+ * within decodedCalls, to is same as from, selector is the entryPointSelector
77
+ * 2. DEPLOY_ACCOUNT
78
+ * from is the contract_address, also is the new account address
79
+ * 3. DECLARE
80
+ * from is the sender_address
81
+ * 4. DEPLOY
82
+ * from is the sender_address
83
+ * 5. INVOKE V1 and V3
84
+ * from is the sender_address
85
+ * within decodedCalls, to is the contract been called, selector is the method
86
+ * 6. INVOKE V0
87
+ * from is the Contract Address (contract been called)
88
+ * entryPointSelector is the method been called
89
+ */
69
90
  function formatTransaction(tx, block, txIndex) {
70
91
  const transaction = {
71
- ...tx,
72
- hash: tx.transaction_hash,
73
- type: tx.type,
74
- version: tx.version,
75
- nonce: tx.nonce,
76
- maxFee: tx.max_fee,
77
- from: getTxContractAddress(tx),
78
- calldata: tx.calldata,
92
+ ...tx.transaction,
93
+ hash: tx.transaction.transaction_hash,
94
+ type: tx.transaction.type,
95
+ version: tx.transaction.version,
96
+ nonce: tx.transaction.nonce,
97
+ maxFee: tx.transaction.max_fee,
98
+ from: getTxContractAddress(tx.transaction),
99
+ calldata: tx.transaction.calldata,
79
100
  blockHash: block.blockHash,
80
101
  blockNumber: block.blockNumber,
81
102
  blockTimestamp: block.timestamp,
82
103
  transactionIndex: txIndex,
83
- entryPointSelector: tx.entry_point_selector,
84
- contractAddress: tx.contract_address,
104
+ entryPointSelector: tx.transaction.entry_point_selector,
105
+ contractAddress: tx.transaction.contract_address,
106
+ receipt: formatReceipt(tx.receipt),
85
107
  parseCallData() {
86
108
  if (this.decodedCalls) {
87
109
  return this.decodedCalls;
88
110
  }
89
111
  // Handle "INVOKE V1 and V3"
90
112
  if (transaction.type === 'INVOKE' &&
91
- transaction.version !== ('0x0' || '0x100000000000000000000000000000000')) {
113
+ transaction.version !== '0x0' &&
114
+ transaction.version !== '0x100000000000000000000000000000000') {
92
115
  return (0, decodeCalldata_1.decodeInvokeCalldata)(transaction.calldata);
93
116
  }
94
117
  // Handle "L1_HANDLER" and "INVOKE V0"
@@ -170,4 +193,10 @@ function hexEq(a, b) {
170
193
  return false;
171
194
  }
172
195
  }
196
+ // check if block is finalized
197
+ function isFinalizedBlock(block) {
198
+ return ('status' in block &&
199
+ block.status !== 'PENDING' &&
200
+ block.status !== 'REJECTED');
201
+ }
173
202
  //# sourceMappingURL=utils.starknet.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"utils.starknet.js","sourceRoot":"","sources":["../../src/starknet/utils.starknet.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { SPEC } from '@starknet-io/types-js';\nimport { Header, IBlock } from '@subql/node-core';\nimport {\n ApiWrapper,\n LightStarknetBlock,\n StarknetBlock,\n StarknetContractCall,\n StarknetLog,\n StarknetLogRaw,\n StarknetResult,\n StarknetTransaction,\n} from '@subql/types-starknet';\nimport { omit } from 'lodash';\nimport {\n Abi,\n hash,\n num,\n RpcProvider,\n TransactionReceipt,\n validateAndParseAddress,\n} from 'starknet';\nimport { BlockContent } from '../indexer/types';\nimport { decodeGenericCalldata, decodeInvokeCalldata } from './decodeCalldata';\n\nexport function calcInterval(api: ApiWrapper): number {\n // TODO find a way to get this from the blockchain\n return 6000;\n}\n\nexport function formatBlock(block: any): StarknetBlock {\n return {\n blockHash: block.block_hash,\n parentHash: block.parent_hash,\n blockNumber: block.block_number,\n newRoot: block.new_root,\n sequencerAddress: block.sequencer_address,\n l1GasPrice: block.l1_gas_price,\n starknetVersion: block.starknet_version,\n timestamp: block.timestamp,\n status: block.status,\n logs: [], // Filled in at starknetBlockWrapped constructor\n transactions: block.transactions, // Transaction still raw here, will format in fetchBlock\n } as StarknetBlock;\n}\n\nexport function formatBlockUtil<\n B extends StarknetBlock | LightStarknetBlock = StarknetBlock,\n>(block: B): IBlock<B> {\n return {\n block,\n getHeader: () => starknetBlockToHeader(block),\n };\n}\n\nexport function formatLog(\n log: StarknetLogRaw,\n block: StarknetBlock,\n): StarknetLog {\n const formattedLog = {\n block,\n address: log.from_address,\n topics: log.keys,\n blockNumber: block.blockNumber,\n blockHash: block.blockHash,\n transactionHash: log.transaction_hash,\n data: log.data,\n toJSON(): string {\n return JSON.stringify(omit(this, ['transaction', 'block', 'toJSON']));\n },\n };\n\n // Define this afterwards as the spread on `...log` breaks defining a getter\n Object.defineProperty(formattedLog, 'transaction', {\n get: () => {\n const rawTransaction = block.transactions?.find(\n (tx) => tx.hash === log.transaction_hash,\n );\n\n return rawTransaction;\n },\n });\n return formattedLog as unknown as StarknetLog;\n}\n\nexport function formatTransaction(\n tx: Record<string, any>,\n block: StarknetBlock,\n txIndex: number,\n): Omit<StarknetTransaction, 'receipt'> {\n const transaction = {\n ...tx,\n hash: tx.transaction_hash,\n type: tx.type,\n version: tx.version,\n nonce: tx.nonce,\n maxFee: tx.max_fee,\n from: getTxContractAddress(tx),\n calldata: tx.calldata,\n blockHash: block.blockHash,\n blockNumber: block.blockNumber,\n blockTimestamp: block.timestamp,\n transactionIndex: txIndex,\n entryPointSelector: tx.entry_point_selector,\n contractAddress: tx.contract_address,\n parseCallData(): StarknetContractCall[] | undefined {\n if (this.decodedCalls) {\n return this.decodedCalls;\n }\n\n // Handle \"INVOKE V1 and V3\"\n if (\n transaction.type === 'INVOKE' &&\n transaction.version !== ('0x0' || '0x100000000000000000000000000000000')\n ) {\n return decodeInvokeCalldata(transaction.calldata);\n }\n // Handle \"L1_HANDLER\" and \"INVOKE V0\"\n else if (\n transaction.contractAddress &&\n transaction.entryPointSelector &&\n transaction.calldata\n ) {\n return [\n decodeGenericCalldata(\n transaction.contractAddress,\n transaction.entryPointSelector,\n transaction.calldata,\n ),\n ];\n }\n return;\n },\n toJSON(): string {\n return JSON.stringify(omit(this, ['receipt', 'toJSON']));\n },\n } as Omit<StarknetTransaction, 'receipt'>;\n return transaction;\n}\n\nexport function getTxContractAddress(tx: Record<string, any>): string {\n if (tx.type === 'DEPLOY' || tx.type === 'DEPLOY_ACCOUNT') {\n const result = hash.calculateContractAddressFromHash(\n tx.contract_address_salt,\n tx.class_hash,\n tx.constructor_calldata,\n 0,\n );\n return result;\n }\n return tx.contract_address ?? tx.sender_address;\n}\n\nexport function formatReceipt(\n receipt: Record<string, any>,\n): TransactionReceipt {\n return {\n ...receipt,\n toJSON(): string {\n return JSON.stringify(omit(this, ['toJSON']));\n },\n } as unknown as TransactionReceipt;\n}\n\nexport function starknetBlockToHeader(block: BlockContent): Header {\n return {\n blockHeight: block.blockNumber,\n blockHash: block.blockHash,\n parentHash: block.parentHash,\n };\n}\n\nexport function starknetBlockHeaderToHeader(block: SPEC.BLOCK_HEADER): Header {\n return {\n blockHeight: block.block_number,\n blockHash: block.block_hash,\n parentHash: block.parent_hash,\n };\n}\n\n//TODO, only used to phrase abi event\nexport function reverseToRawLog(log: StarknetLog): StarknetLogRaw {\n return {\n block_hash: log.blockHash,\n keys: [...log.topics],\n from_address: log.address,\n transaction_hash: log.transactionHash,\n block_number: log.blockNumber,\n ...log,\n };\n}\n\n// This is used when user abi not provided, or decode call in tx\nexport async function fetchAbiFromContractAddress(\n provider: RpcProvider,\n contractAddress: string,\n): Promise<Abi> {\n const { abi: remoteAbi } = await provider.getClassAt(contractAddress);\n if (remoteAbi === undefined) {\n throw new Error('no abi.');\n }\n return remoteAbi;\n}\n\nexport function encodeSelectorToHex(eventName: string): string {\n return hash.getSelector(eventName);\n}\n\nexport function encodeEventKey(eventName: string): string {\n return num.toHex(hash.starknetKeccak(eventName));\n}\n\n// Check address or selector hex string are equal\nexport function hexEq(a: string, b: string): boolean {\n try {\n return validateAndParseAddress(a) === validateAndParseAddress(b);\n } catch (e) {\n return false;\n }\n}\n"]}
1
+ {"version":3,"file":"utils.starknet.js","sourceRoot":"","sources":["../../src/starknet/utils.starknet.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { SPEC } from '@starknet-io/types-js';\nimport { Header, IBlock } from '@subql/node-core';\nimport {\n ApiWrapper,\n LightStarknetBlock,\n StarknetBlock,\n StarknetContractCall,\n StarknetLog,\n StarknetLogRaw,\n StarknetTransaction,\n StarknetTransactionRaw,\n} from '@subql/types-starknet';\nimport { omit } from 'lodash';\nimport {\n Abi,\n hash,\n num,\n RpcProvider,\n TransactionReceipt,\n validateAndParseAddress,\n} from 'starknet';\nimport { BlockContent } from '../indexer/types';\nimport { decodeGenericCalldata, decodeInvokeCalldata } from './decodeCalldata';\n\nexport function calcInterval(api: ApiWrapper): number {\n // TODO find a way to get this from the blockchain\n return 6000;\n}\n\nexport function formatBlock(\n block: SPEC.BLOCK_WITH_RECEIPTS | SPEC.BLOCK_WITH_TX_HASHES,\n): Omit<StarknetBlock, 'transactions'> & {\n transactions: StarknetTransactionRaw[];\n} {\n return {\n blockHash: block.block_hash,\n parentHash: block.parent_hash,\n blockNumber: block.block_number,\n newRoot: block.new_root,\n sequencerAddress: block.sequencer_address,\n l1GasPrice: block.l1_gas_price,\n starknetVersion: block.starknet_version,\n timestamp: block.timestamp,\n status: block.status,\n logs: [], // Filled in at starknetBlockWrapped constructor\n transactions: block.transactions, // Transaction still raw here, will format in fetchBlock\n };\n}\n\nexport function formatBlockUtil<\n B extends StarknetBlock | LightStarknetBlock = StarknetBlock,\n>(block: B): IBlock<B> {\n return {\n block,\n getHeader: () => starknetBlockToHeader(block),\n };\n}\n\nexport function formatLog(\n log: StarknetLogRaw,\n logIndex: number,\n tx: StarknetTransaction,\n block: Omit<StarknetBlock, 'transactions'> & {\n transactions: StarknetTransactionRaw[];\n },\n): StarknetLog {\n const formattedLog = {\n address: log.from_address,\n topics: log.keys,\n blockNumber: block.blockNumber,\n blockHash: block.blockHash,\n transactionHash: tx.hash,\n data: log.data,\n logIndex: logIndex,\n block: block,\n transaction: tx,\n transactionIndex: tx.transactionIndex,\n toJSON(): string {\n return JSON.stringify(omit(this, ['transaction', 'block', 'toJSON']));\n },\n };\n return formattedLog as unknown as StarknetLog;\n}\n\n/***\n * @param tx\n * @param block\n * @param txIndex\n * Explanation for from, to, selector, calldata with different tx type\n * When apply filter please refer to the following:\n *\n * 1. L1_HANDLER\n * from is the Contract Address (contract been called)\n * entryPointSelector (method)\n * within decodedCalls, to is same as from, selector is the entryPointSelector\n * 2. DEPLOY_ACCOUNT\n * from is the contract_address, also is the new account address\n * 3. DECLARE\n * from is the sender_address\n * 4. DEPLOY\n * from is the sender_address\n * 5. INVOKE V1 and V3\n * from is the sender_address\n * within decodedCalls, to is the contract been called, selector is the method\n * 6. INVOKE V0\n * from is the Contract Address (contract been called)\n * entryPointSelector is the method been called\n */\nexport function formatTransaction(\n tx: Record<string, any>,\n block:\n | StarknetBlock\n | (Omit<StarknetBlock, 'transactions'> & {\n transactions: StarknetTransactionRaw[];\n }),\n txIndex: number,\n): StarknetTransaction {\n const transaction = {\n ...tx.transaction,\n hash: tx.transaction.transaction_hash,\n type: tx.transaction.type,\n version: tx.transaction.version,\n nonce: tx.transaction.nonce,\n maxFee: tx.transaction.max_fee,\n from: getTxContractAddress(tx.transaction),\n calldata: tx.transaction.calldata,\n blockHash: block.blockHash,\n blockNumber: block.blockNumber,\n blockTimestamp: block.timestamp,\n transactionIndex: txIndex,\n entryPointSelector: tx.transaction.entry_point_selector,\n contractAddress: tx.transaction.contract_address,\n receipt: formatReceipt(tx.receipt),\n parseCallData(): StarknetContractCall[] | undefined {\n if (this.decodedCalls) {\n return this.decodedCalls;\n }\n\n // Handle \"INVOKE V1 and V3\"\n if (\n transaction.type === 'INVOKE' &&\n transaction.version !== '0x0' &&\n transaction.version !== '0x100000000000000000000000000000000'\n ) {\n return decodeInvokeCalldata(transaction.calldata);\n }\n // Handle \"L1_HANDLER\" and \"INVOKE V0\"\n else if (\n transaction.contractAddress &&\n transaction.entryPointSelector &&\n transaction.calldata\n ) {\n return [\n decodeGenericCalldata(\n transaction.contractAddress,\n transaction.entryPointSelector,\n transaction.calldata,\n ),\n ];\n }\n return;\n },\n toJSON(): string {\n return JSON.stringify(omit(this, ['receipt', 'toJSON']));\n },\n } as StarknetTransaction;\n return transaction;\n}\n\nexport function getTxContractAddress(tx: Record<string, any>): string {\n if (tx.type === 'DEPLOY' || tx.type === 'DEPLOY_ACCOUNT') {\n const result = hash.calculateContractAddressFromHash(\n tx.contract_address_salt,\n tx.class_hash,\n tx.constructor_calldata,\n 0,\n );\n return result;\n }\n return tx.contract_address ?? tx.sender_address;\n}\n\nexport function formatReceipt(\n receipt: Record<string, any>,\n): TransactionReceipt {\n return {\n ...receipt,\n toJSON(): string {\n return JSON.stringify(omit(this, ['toJSON']));\n },\n } as unknown as TransactionReceipt;\n}\n\nexport function starknetBlockToHeader(\n block:\n | BlockContent\n | (Omit<StarknetBlock, 'transactions'> & {\n transactions: StarknetTransactionRaw[];\n }),\n): Header {\n return {\n blockHeight: block.blockNumber,\n blockHash: block.blockHash,\n parentHash: block.parentHash,\n };\n}\n\nexport function starknetBlockHeaderToHeader(\n block: Partial<SPEC.BLOCK_HEADER>,\n): Header {\n return {\n blockHeight: block.block_number!,\n blockHash: block.block_hash!,\n parentHash: block.parent_hash,\n };\n}\n\n//TODO, only used to phrase abi event\nexport function reverseToRawLog(log: StarknetLog): SPEC.EMITTED_EVENT {\n return {\n block_hash: log.blockHash,\n keys: [...log.topics],\n from_address: log.address,\n transaction_hash: log.transactionHash,\n block_number: log.blockNumber,\n ...log,\n };\n}\n\n// This is used when user abi not provided, or decode call in tx\nexport async function fetchAbiFromContractAddress(\n provider: RpcProvider,\n contractAddress: string,\n): Promise<Abi> {\n const { abi: remoteAbi } = await provider.getClassAt(contractAddress);\n if (remoteAbi === undefined) {\n throw new Error('no abi.');\n }\n return remoteAbi;\n}\n\nexport function encodeSelectorToHex(eventName: string): string {\n return hash.getSelector(eventName);\n}\n\nexport function encodeEventKey(eventName: string): string {\n return num.toHex(hash.starknetKeccak(eventName));\n}\n\n// Check address or selector hex string are equal\nexport function hexEq(a: string, b: string): boolean {\n try {\n return validateAndParseAddress(a) === validateAndParseAddress(b);\n } catch (e) {\n return false;\n }\n}\n\n// check if block is finalized\nexport function isFinalizedBlock(\n block: SPEC.BLOCK_WITH_RECEIPTS | SPEC.PENDING_BLOCK_WITH_RECEIPTS,\n): block is SPEC.BLOCK_WITH_RECEIPTS {\n return (\n 'status' in block &&\n block.status !== 'PENDING' &&\n block.status !== 'REJECTED'\n );\n}\n"]}
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/utils/project.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAiBnC,sCAIC;AAED,0CAIC;AAED,sDAYC;AAcD,kDASC;AA9DD,4DAQgC;AAOhC,SAAgB,aAAa,CAC3B,OAAqB;IAErB,OAAO,MAAM,CAAC,MAAM,CAAS,qCAAmB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED,SAAgB,eAAe,CAC7B,OAAqB;IAErB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,SAAgB,qBAAqB,CACnC,WAAgC;IAEhC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC7B,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC1C,IAAI,OAAO,CAAC,IAAI,KAAK,0CAAwB,CAAC,SAAS,EAAE,CAAC;gBACxD,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,0BAA0B,CAAC,EAAqB;IACvD,IAAI,IAAA,6BAAW,EAAC,EAAE,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAC/B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,qCAAmB,CAAC,KAAK,CACxD,CAAC;IACJ,CAAC;SAAM,IAAI,IAAA,4BAAU,EAAC,EAAE,CAAC,EAAE,CAAC;QAC1B,gDAAgD;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAwB;IAC1D,MAAM,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC3D,0BAA0B,CAAC,EAAE,CAAC,CAC/B,CAAC;IACF,MAAM,mBAAmB,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC1D,0BAA0B,CAAC,EAAuB,CAAC,CACpD,CAAC;IAEF,OAAO,CAAC,kBAAkB,IAAI,CAAC,mBAAmB,CAAC;AACrD,CAAC","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport {\n SubqlRuntimeHandler,\n SubqlCustomHandler,\n SubqlHandler,\n StarknetHandlerKind,\n SubqlStarknetHandlerKind,\n isCustomDs,\n isRuntimeDs,\n} from '@subql/common-starknet';\nimport { retryOnFail } from '@subql/node-core';\nimport {\n StarknetProjectDs,\n SubqueryProject,\n} from '../configure/SubqueryProject';\n\nexport function isBaseHandler(\n handler: SubqlHandler,\n): handler is SubqlRuntimeHandler {\n return Object.values<string>(StarknetHandlerKind).includes(handler.kind);\n}\n\nexport function isCustomHandler(\n handler: SubqlHandler,\n): handler is SubqlCustomHandler {\n return !isBaseHandler(handler);\n}\n\nexport function onlyHasLogDataSources(\n dataSources: StarknetProjectDs[],\n): boolean {\n for (const ds of dataSources) {\n for (const handler of ds.mapping.handlers) {\n if (handler.kind !== SubqlStarknetHandlerKind.StrkEvent) {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction dsContainsNonEventHandlers(ds: StarknetProjectDs): boolean {\n if (isRuntimeDs(ds)) {\n return !!ds.mapping.handlers.find(\n (handler) => handler.kind !== StarknetHandlerKind.Event,\n );\n } else if (isCustomDs(ds)) {\n // TODO this can be improved upon in the future.\n return true;\n }\n return true;\n}\n\nexport function isOnlyEventHandlers(project: SubqueryProject): boolean {\n const hasNonEventHandler = !!project.dataSources.find((ds) =>\n dsContainsNonEventHandlers(ds),\n );\n const hasNonEventTemplate = !!project.templates.find((ds) =>\n dsContainsNonEventHandlers(ds as StarknetProjectDs),\n );\n\n return !hasNonEventHandler && !hasNonEventTemplate;\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/utils/project.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAgBnC,sCAIC;AAED,0CAIC;AAED,sDAYC;AAcD,kDASC;AA7DD,4DAQgC;AAMhC,SAAgB,aAAa,CAC3B,OAAqB;IAErB,OAAO,MAAM,CAAC,MAAM,CAAS,qCAAmB,CAAC,CAAC,QAAQ,CAAC,OAAO,CAAC,IAAI,CAAC,CAAC;AAC3E,CAAC;AAED,SAAgB,eAAe,CAC7B,OAAqB;IAErB,OAAO,CAAC,aAAa,CAAC,OAAO,CAAC,CAAC;AACjC,CAAC;AAED,SAAgB,qBAAqB,CACnC,WAAgC;IAEhC,KAAK,MAAM,EAAE,IAAI,WAAW,EAAE,CAAC;QAC7B,KAAK,MAAM,OAAO,IAAI,EAAE,CAAC,OAAO,CAAC,QAAQ,EAAE,CAAC;YAC1C,IAAI,OAAO,CAAC,IAAI,KAAK,0CAAwB,CAAC,SAAS,EAAE,CAAC;gBACxD,OAAO,KAAK,CAAC;YACf,CAAC;QACH,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAS,0BAA0B,CAAC,EAAqB;IACvD,IAAI,IAAA,6BAAW,EAAC,EAAE,CAAC,EAAE,CAAC;QACpB,OAAO,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,QAAQ,CAAC,IAAI,CAC/B,CAAC,OAAO,EAAE,EAAE,CAAC,OAAO,CAAC,IAAI,KAAK,qCAAmB,CAAC,KAAK,CACxD,CAAC;IACJ,CAAC;SAAM,IAAI,IAAA,4BAAU,EAAC,EAAE,CAAC,EAAE,CAAC;QAC1B,gDAAgD;QAChD,OAAO,IAAI,CAAC;IACd,CAAC;IACD,OAAO,IAAI,CAAC;AACd,CAAC;AAED,SAAgB,mBAAmB,CAAC,OAAwB;IAC1D,MAAM,kBAAkB,GAAG,CAAC,CAAC,OAAO,CAAC,WAAW,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC3D,0BAA0B,CAAC,EAAE,CAAC,CAC/B,CAAC;IACF,MAAM,mBAAmB,GAAG,CAAC,CAAC,OAAO,CAAC,SAAS,CAAC,IAAI,CAAC,CAAC,EAAE,EAAE,EAAE,CAC1D,0BAA0B,CAAC,EAAuB,CAAC,CACpD,CAAC;IAEF,OAAO,CAAC,kBAAkB,IAAI,CAAC,mBAAmB,CAAC;AACrD,CAAC","sourcesContent":["// Copyright 2020-2024 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport {\n SubqlRuntimeHandler,\n SubqlCustomHandler,\n SubqlHandler,\n StarknetHandlerKind,\n SubqlStarknetHandlerKind,\n isCustomDs,\n isRuntimeDs,\n} from '@subql/common-starknet';\nimport {\n StarknetProjectDs,\n SubqueryProject,\n} from '../configure/SubqueryProject';\n\nexport function isBaseHandler(\n handler: SubqlHandler,\n): handler is SubqlRuntimeHandler {\n return Object.values<string>(StarknetHandlerKind).includes(handler.kind);\n}\n\nexport function isCustomHandler(\n handler: SubqlHandler,\n): handler is SubqlCustomHandler {\n return !isBaseHandler(handler);\n}\n\nexport function onlyHasLogDataSources(\n dataSources: StarknetProjectDs[],\n): boolean {\n for (const ds of dataSources) {\n for (const handler of ds.mapping.handlers) {\n if (handler.kind !== SubqlStarknetHandlerKind.StrkEvent) {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction dsContainsNonEventHandlers(ds: StarknetProjectDs): boolean {\n if (isRuntimeDs(ds)) {\n return !!ds.mapping.handlers.find(\n (handler) => handler.kind !== StarknetHandlerKind.Event,\n );\n } else if (isCustomDs(ds)) {\n // TODO this can be improved upon in the future.\n return true;\n }\n return true;\n}\n\nexport function isOnlyEventHandlers(project: SubqueryProject): boolean {\n const hasNonEventHandler = !!project.dataSources.find((ds) =>\n dsContainsNonEventHandlers(ds),\n );\n const hasNonEventTemplate = !!project.templates.find((ds) =>\n dsContainsNonEventHandlers(ds as StarknetProjectDs),\n );\n\n return !hasNonEventHandler && !hasNonEventTemplate;\n}\n"]}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@subql/node-starknet",
3
- "version": "0.0.2-7",
3
+ "version": "0.0.2-9",
4
4
  "description": "",
5
5
  "author": "Jay Ji",
6
6
  "license": "GPL-3.0",
@@ -25,10 +25,10 @@
25
25
  "@nestjs/platform-express": "^9.4.0",
26
26
  "@nestjs/schedule": "^3.0.1",
27
27
  "@subql/common": "^5.2.2",
28
- "@subql/common-starknet": "0.0.2-2",
28
+ "@subql/common-starknet": "0.0.2-3",
29
29
  "@subql/node-core": "16.1.1-1",
30
30
  "@subql/testing": "^2.2.2",
31
- "@subql/types-starknet": "0.0.2-3",
31
+ "@subql/types-starknet": "0.0.2-4",
32
32
  "cacheable-lookup": "6",
33
33
  "eventemitter2": "^6.4.5",
34
34
  "json5": "^2.2.3",
@@ -60,5 +60,5 @@
60
60
  "/dist",
61
61
  "/bin"
62
62
  ],
63
- "stableVersion": "0.0.2-6"
63
+ "stableVersion": "0.0.2-8"
64
64
  }