@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.
- package/dist/.tsbuildinfo +1 -1
- package/dist/indexer/blockDispatcher/block-dispatcher.service.js.map +1 -1
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.d.ts +0 -1
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.js +26 -47
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.js.map +1 -1
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.d.ts +1 -0
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.js +189 -0
- package/dist/indexer/dictionary/v1/starknetDictionaryV1.spec.js.map +1 -0
- package/dist/indexer/fetch.service.js +1 -1
- package/dist/indexer/fetch.service.js.map +1 -1
- package/dist/indexer/project.service.js +1 -1
- package/dist/indexer/project.service.js.map +1 -1
- package/dist/indexer/types.js +5 -2
- package/dist/indexer/types.js.map +1 -1
- package/dist/indexer/unfinalizedBlocks.service.d.ts +0 -2
- package/dist/indexer/unfinalizedBlocks.service.js +0 -2
- package/dist/indexer/unfinalizedBlocks.service.js.map +1 -1
- package/dist/starknet/api.service.starknet.test.js +0 -4
- package/dist/starknet/api.service.starknet.test.js.map +1 -1
- package/dist/starknet/api.starknet.d.ts +7 -9
- package/dist/starknet/api.starknet.js +52 -90
- package/dist/starknet/api.starknet.js.map +1 -1
- package/dist/starknet/api.starknet.test.js +12 -5
- package/dist/starknet/api.starknet.test.js.map +1 -1
- package/dist/starknet/block.starknet.js +20 -12
- package/dist/starknet/block.starknet.js.map +1 -1
- package/dist/starknet/block.starknet.test.js +36 -1
- package/dist/starknet/block.starknet.test.js.map +1 -1
- package/dist/starknet/finalized.block.starknet.d.ts +21 -0
- package/dist/starknet/finalized.block.starknet.js +93 -0
- package/dist/starknet/finalized.block.starknet.js.map +1 -0
- package/dist/starknet/finalized.block.starknet.spec.d.ts +1 -0
- package/dist/starknet/finalized.block.starknet.spec.js +94 -0
- package/dist/starknet/finalized.block.starknet.spec.js.map +1 -0
- package/dist/starknet/utils.starknet.d.ts +40 -7
- package/dist/starknet/utils.starknet.js +50 -21
- package/dist/starknet/utils.starknet.js.map +1 -1
- package/dist/utils/project.js.map +1 -1
- 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":"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","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:
|
|
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
|
|
10
|
-
|
|
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
|
|
14
|
-
|
|
15
|
-
|
|
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:
|
|
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 !==
|
|
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;;
|
|
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-
|
|
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-
|
|
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-
|
|
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-
|
|
63
|
+
"stableVersion": "0.0.2-8"
|
|
64
64
|
}
|