@subql/node-ethereum 6.2.2-0 → 6.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +6 -1
- package/dist/.tsbuildinfo +1 -1
- package/dist/utils/string.js +2 -1
- package/dist/utils/string.js.map +1 -1
- package/package.json +3 -4
- package/dist/blockchain.service.test.d.ts +0 -1
- package/dist/blockchain.service.test.js +0 -27
- package/dist/blockchain.service.test.js.map +0 -1
- package/dist/ethereum/api.ethereum.test.d.ts +0 -1
- package/dist/ethereum/api.ethereum.test.js +0 -266
- package/dist/ethereum/api.ethereum.test.js.map +0 -1
- package/dist/ethereum/api.service.ethereum.test.d.ts +0 -1
- package/dist/ethereum/api.service.ethereum.test.js +0 -91
- package/dist/ethereum/api.service.ethereum.test.js.map +0 -1
- package/dist/ethereum/ethers/celo/celo-ws-provider.spec.d.ts +0 -1
- package/dist/ethereum/ethers/celo/celo-ws-provider.spec.js +0 -33
- package/dist/ethereum/ethers/celo/celo-ws-provider.spec.js.map +0 -1
- package/dist/ethereum/ethers/json-rpc-batch-provider.spec.d.ts +0 -1
- package/dist/ethereum/ethers/json-rpc-batch-provider.spec.js +0 -63
- package/dist/ethereum/ethers/json-rpc-batch-provider.spec.js.map +0 -1
- package/dist/ethereum/ethers/op/op-provider.spec.d.ts +0 -1
- package/dist/ethereum/ethers/op/op-provider.spec.js +0 -32
- package/dist/ethereum/ethers/op/op-provider.spec.js.map +0 -1
- package/dist/indexer/dictionary/utils.spec.d.ts +0 -1
- package/dist/indexer/dictionary/utils.spec.js +0 -101
- package/dist/indexer/dictionary/utils.spec.js.map +0 -1
- package/dist/indexer/dictionary/v1/ethDictionaryV1.spec.d.ts +0 -1
- package/dist/indexer/dictionary/v1/ethDictionaryV1.spec.js +0 -519
- package/dist/indexer/dictionary/v1/ethDictionaryV1.spec.js.map +0 -1
- package/dist/indexer/dictionary/v2/ethDictionaryV2.spec.d.ts +0 -1
- package/dist/indexer/dictionary/v2/ethDictionaryV2.spec.js +0 -585
- package/dist/indexer/dictionary/v2/ethDictionaryV2.spec.js.map +0 -1
- package/dist/indexer/dictionary/v2/utils.spec.d.ts +0 -1
- package/dist/indexer/dictionary/v2/utils.spec.js +0 -92
- package/dist/indexer/dictionary/v2/utils.spec.js.map +0 -1
- package/dist/indexer/unfinalizedBlocks.service.spec.d.ts +0 -1
- package/dist/indexer/unfinalizedBlocks.service.spec.js +0 -169
- package/dist/indexer/unfinalizedBlocks.service.spec.js.map +0 -1
- package/dist/utils/project.spec.d.ts +0 -1
- package/dist/utils/project.spec.js +0 -99
- package/dist/utils/project.spec.js.map +0 -1
- package/dist/utils/string.spec.d.ts +0 -1
- package/dist/utils/string.spec.js +0 -103
- package/dist/utils/string.spec.js.map +0 -1
|
@@ -1,92 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright 2020-2025 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 eventemitter2_1 = __importDefault(require("eventemitter2"));
|
|
9
|
-
const node_fetch_1 = __importDefault(require("node-fetch"));
|
|
10
|
-
const ethereum_1 = require("../../../ethereum");
|
|
11
|
-
const utils_1 = require("./utils");
|
|
12
|
-
const DICTIONARY_URL = 'https://ethereum.node.subquery.network/public';
|
|
13
|
-
const RPC_URL = 'https://ethereum.rpc.subquery.network/public';
|
|
14
|
-
async function fetchDictionaryBlock() {
|
|
15
|
-
const res = await (0, node_fetch_1.default)(DICTIONARY_URL, {
|
|
16
|
-
method: 'POST',
|
|
17
|
-
headers: {
|
|
18
|
-
'Content-Type': 'application/json',
|
|
19
|
-
},
|
|
20
|
-
body: JSON.stringify({
|
|
21
|
-
jsonrpc: '2.0',
|
|
22
|
-
id: 1,
|
|
23
|
-
method: 'subql_filterBlocks',
|
|
24
|
-
params: [
|
|
25
|
-
{
|
|
26
|
-
fromBlock: '0x3e579e',
|
|
27
|
-
toBlock: '0x3e579e',
|
|
28
|
-
limit: '0x1',
|
|
29
|
-
blockFilter: {
|
|
30
|
-
logs: [
|
|
31
|
-
{
|
|
32
|
-
address: ['0x6f28B146804dBa2D6f944C03528A8FDbc673df2C'],
|
|
33
|
-
topics0: [
|
|
34
|
-
'0xb76d0edd90c6a07aa3ff7a222d7f5933e29c6acc660c059c97837f05c4ca1a84',
|
|
35
|
-
],
|
|
36
|
-
},
|
|
37
|
-
],
|
|
38
|
-
},
|
|
39
|
-
fieldSelector: {
|
|
40
|
-
blockHeader: true,
|
|
41
|
-
logs: {
|
|
42
|
-
transaction: true,
|
|
43
|
-
},
|
|
44
|
-
transactions: {
|
|
45
|
-
log: true,
|
|
46
|
-
},
|
|
47
|
-
},
|
|
48
|
-
},
|
|
49
|
-
],
|
|
50
|
-
}),
|
|
51
|
-
});
|
|
52
|
-
return (await res.json()).result.blocks[0];
|
|
53
|
-
}
|
|
54
|
-
describe('rawBlockToEthBlock', () => {
|
|
55
|
-
const api = new ethereum_1.EthereumApi(RPC_URL, 1, new eventemitter2_1.default());
|
|
56
|
-
let raw;
|
|
57
|
-
let rpcBlock;
|
|
58
|
-
beforeAll(async () => {
|
|
59
|
-
[raw, rpcBlock] = await Promise.all([
|
|
60
|
-
fetchDictionaryBlock(),
|
|
61
|
-
api.fetchBlock(4085662),
|
|
62
|
-
]);
|
|
63
|
-
});
|
|
64
|
-
it('successfully converts a block', () => {
|
|
65
|
-
const block = (0, utils_1.rawBlockToEthBlock)(raw, api);
|
|
66
|
-
expect(block.getHeader()).toEqual({
|
|
67
|
-
blockHash: '0xde8e614cc05b483fe092fd0aff435011138c15c9ede862579074218d4aff5132',
|
|
68
|
-
blockHeight: 4085662,
|
|
69
|
-
parentHash: '0x669c7a2c66e38c144ad1e2845ec345b6ce7e2107edbdb331ab88d355982126a7',
|
|
70
|
-
});
|
|
71
|
-
expect(block.block.transactions.length).toBeGreaterThan(0);
|
|
72
|
-
expect(block.block.logs.length).toBeGreaterThan(0);
|
|
73
|
-
// Dictionry is going to return a subset of events/transactions so we base off dictionary block
|
|
74
|
-
for (const log of block.block.logs) {
|
|
75
|
-
// Remove linked types
|
|
76
|
-
const { block: _1, transaction: _2, ...logRest } = log;
|
|
77
|
-
const { block: _3, transaction: _4, ...rpcLogRest } = rpcBlock.block.logs[log.logIndex];
|
|
78
|
-
expect(JSON.stringify(logRest)).toEqual(JSON.stringify(rpcLogRest));
|
|
79
|
-
}
|
|
80
|
-
for (const tx of block.block.transactions) {
|
|
81
|
-
// Cast to any to remove toJSON which results in different key order
|
|
82
|
-
const { logs: _1, receipt: _3, toJSON: _5, ...txRest } = tx;
|
|
83
|
-
const { logs: _2, receipt: _4, toJSON: _6, ...rpcTxRest } = rpcBlock.block.transactions[Number(tx.transactionIndex)];
|
|
84
|
-
expect(txRest).toEqual(rpcTxRest);
|
|
85
|
-
}
|
|
86
|
-
}, 15000);
|
|
87
|
-
it('can fetch receipts', async () => {
|
|
88
|
-
const block = (0, utils_1.rawBlockToEthBlock)(raw, api);
|
|
89
|
-
await expect(block.block.transactions[0].receipt()).resolves.not.toThrow();
|
|
90
|
-
});
|
|
91
|
-
});
|
|
92
|
-
//# sourceMappingURL=utils.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"utils.spec.js","sourceRoot":"","sources":["../../../../src/indexer/dictionary/v2/utils.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { IBlock } from '@subql/node-core';\nimport { EthereumBlock } from '@subql/types-ethereum';\nimport EventEmitter2 from 'eventemitter2';\nimport fetch from 'node-fetch';\nimport { EthereumApi } from '../../../ethereum';\nimport { RawEthBlock } from './types';\nimport { rawBlockToEthBlock } from './utils';\n\nconst DICTIONARY_URL = 'https://ethereum.node.subquery.network/public';\nconst RPC_URL = 'https://ethereum.rpc.subquery.network/public';\n\nasync function fetchDictionaryBlock(): Promise<RawEthBlock> {\n const res = await fetch(DICTIONARY_URL, {\n method: 'POST',\n headers: {\n 'Content-Type': 'application/json',\n },\n body: JSON.stringify({\n jsonrpc: '2.0',\n id: 1,\n method: 'subql_filterBlocks',\n params: [\n {\n fromBlock: '0x3e579e',\n toBlock: '0x3e579e',\n limit: '0x1',\n blockFilter: {\n logs: [\n {\n address: ['0x6f28B146804dBa2D6f944C03528A8FDbc673df2C'],\n topics0: [\n '0xb76d0edd90c6a07aa3ff7a222d7f5933e29c6acc660c059c97837f05c4ca1a84',\n ],\n },\n ],\n },\n fieldSelector: {\n blockHeader: true,\n logs: {\n transaction: true,\n },\n transactions: {\n log: true,\n },\n },\n },\n ],\n }),\n });\n\n return (await res.json()).result.blocks[0];\n}\n\ndescribe('rawBlockToEthBlock', () => {\n const api = new EthereumApi(RPC_URL, 1, new EventEmitter2());\n let raw: RawEthBlock;\n let rpcBlock: IBlock<EthereumBlock>;\n\n beforeAll(async () => {\n [raw, rpcBlock] = await Promise.all([\n fetchDictionaryBlock(),\n api.fetchBlock(4085662),\n ]);\n });\n\n it('successfully converts a block', () => {\n const block = rawBlockToEthBlock(raw, api);\n\n expect(block.getHeader()).toEqual({\n blockHash:\n '0xde8e614cc05b483fe092fd0aff435011138c15c9ede862579074218d4aff5132',\n blockHeight: 4085662,\n parentHash:\n '0x669c7a2c66e38c144ad1e2845ec345b6ce7e2107edbdb331ab88d355982126a7',\n });\n\n expect(block.block.transactions.length).toBeGreaterThan(0);\n expect(block.block.logs.length).toBeGreaterThan(0);\n\n // Dictionry is going to return a subset of events/transactions so we base off dictionary block\n for (const log of block.block.logs) {\n // Remove linked types\n const { block: _1, transaction: _2, ...logRest } = log;\n const {\n block: _3,\n transaction: _4,\n ...rpcLogRest\n } = rpcBlock.block.logs[log.logIndex];\n expect(JSON.stringify(logRest)).toEqual(JSON.stringify(rpcLogRest));\n }\n\n for (const tx of block.block.transactions) {\n // Cast to any to remove toJSON which results in different key order\n const { logs: _1, receipt: _3, toJSON: _5, ...txRest } = tx as any;\n const {\n logs: _2,\n receipt: _4,\n toJSON: _6,\n ...rpcTxRest\n } = rpcBlock.block.transactions[Number(tx.transactionIndex)] as any;\n\n expect(txRest).toEqual(rpcTxRest);\n }\n }, 15000);\n\n it('can fetch receipts', async () => {\n const block = rawBlockToEthBlock(raw, api);\n\n await expect(block.block.transactions[0].receipt()).resolves.not.toThrow();\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,169 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
const bytes_1 = require("@ethersproject/bytes");
|
|
6
|
-
const node_core_1 = require("@subql/node-core");
|
|
7
|
-
const blockchain_service_1 = require("../blockchain.service");
|
|
8
|
-
const unfinalizedBlocks_service_1 = require("./unfinalizedBlocks.service");
|
|
9
|
-
// Adds 0 padding so we can convert to POI block
|
|
10
|
-
const hexify = (input) => (0, bytes_1.hexZeroPad)(input, 4);
|
|
11
|
-
// Mock 1740100000 is the timestamp of the genesis block
|
|
12
|
-
const genBlockTimestamp = (height) => (1740100000 + height) * 1000;
|
|
13
|
-
const genBlockDate = (height) => new Date(genBlockTimestamp(height));
|
|
14
|
-
const makeHeader = (height, finalized) => ({
|
|
15
|
-
blockHeight: height,
|
|
16
|
-
blockHash: hexify(`0xABC${height}${finalized ? 'f' : ''}`),
|
|
17
|
-
parentHash: hexify(`0xABC${height - 1}${finalized ? 'f' : ''}`),
|
|
18
|
-
timestamp: genBlockDate(height),
|
|
19
|
-
});
|
|
20
|
-
const getMockApi = () => {
|
|
21
|
-
return {
|
|
22
|
-
api: {
|
|
23
|
-
getBlockByHeightOrHash: (hash) => {
|
|
24
|
-
const num = typeof hash === 'number'
|
|
25
|
-
? hash
|
|
26
|
-
: Number(hash
|
|
27
|
-
.toString()
|
|
28
|
-
.replace('0x', '')
|
|
29
|
-
.replace('ABC', '')
|
|
30
|
-
.replace('f', ''));
|
|
31
|
-
return Promise.resolve({
|
|
32
|
-
number: num,
|
|
33
|
-
hash: typeof hash === 'number' ? hexify(`0xABC${hash}f`) : hash,
|
|
34
|
-
parentHash: hexify(`0xABC${num - 1}f`),
|
|
35
|
-
timestamp: genBlockTimestamp(num) / 1000,
|
|
36
|
-
});
|
|
37
|
-
},
|
|
38
|
-
getFinalizedBlock: jest.fn(() => ({
|
|
39
|
-
number: 110,
|
|
40
|
-
hash: '0xABC110f',
|
|
41
|
-
parentHash: '0xABC109f',
|
|
42
|
-
timestamp: genBlockTimestamp(110) / 1000,
|
|
43
|
-
})),
|
|
44
|
-
},
|
|
45
|
-
};
|
|
46
|
-
};
|
|
47
|
-
function getMockMetadata() {
|
|
48
|
-
const data = {};
|
|
49
|
-
return {
|
|
50
|
-
upsert: ({ key, value }) => (data[key] = value),
|
|
51
|
-
findOne: ({ where: { key } }) => ({ value: data[key] }),
|
|
52
|
-
findByPk: (key) => data[key],
|
|
53
|
-
find: (key) => data[key],
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
function mockStoreCache() {
|
|
57
|
-
return {
|
|
58
|
-
metadata: new node_core_1.CacheMetadataModel(getMockMetadata()),
|
|
59
|
-
poi: {
|
|
60
|
-
getPoiBlocksBefore: jest.fn(() => [
|
|
61
|
-
node_core_1.PoiBlock.create(99, hexify('0xABC99f'), new Uint8Array(), ''),
|
|
62
|
-
]),
|
|
63
|
-
},
|
|
64
|
-
};
|
|
65
|
-
}
|
|
66
|
-
describe('UnfinalizedBlockService', () => {
|
|
67
|
-
let unfinalizedBlocks;
|
|
68
|
-
let storeCache;
|
|
69
|
-
beforeEach(() => {
|
|
70
|
-
storeCache = mockStoreCache();
|
|
71
|
-
const blockchainService = new blockchain_service_1.BlockchainService(getMockApi());
|
|
72
|
-
unfinalizedBlocks = new unfinalizedBlocks_service_1.UnfinalizedBlocksService(new node_core_1.NodeConfig({
|
|
73
|
-
unfinalizedBlocks: true,
|
|
74
|
-
blockForkReindex: 1000,
|
|
75
|
-
}), storeCache, blockchainService);
|
|
76
|
-
});
|
|
77
|
-
it('handles a block fork', async () => {
|
|
78
|
-
await unfinalizedBlocks.init(jest.fn());
|
|
79
|
-
unfinalizedBlocks._unfinalizedBlocks = [
|
|
80
|
-
makeHeader(100),
|
|
81
|
-
makeHeader(101),
|
|
82
|
-
makeHeader(102),
|
|
83
|
-
makeHeader(103, true), // Where the fork started
|
|
84
|
-
makeHeader(104),
|
|
85
|
-
makeHeader(105),
|
|
86
|
-
makeHeader(106),
|
|
87
|
-
makeHeader(107),
|
|
88
|
-
makeHeader(108),
|
|
89
|
-
makeHeader(109),
|
|
90
|
-
makeHeader(110),
|
|
91
|
-
];
|
|
92
|
-
const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(makeHeader(111, true));
|
|
93
|
-
expect(rewind?.blockHeight).toEqual(103);
|
|
94
|
-
});
|
|
95
|
-
it('uses POI blocks if there are not enough cached unfinalized blocks', async () => {
|
|
96
|
-
await unfinalizedBlocks.init(jest.fn());
|
|
97
|
-
unfinalizedBlocks._unfinalizedBlocks = [
|
|
98
|
-
makeHeader(100),
|
|
99
|
-
makeHeader(101),
|
|
100
|
-
makeHeader(102),
|
|
101
|
-
makeHeader(103),
|
|
102
|
-
makeHeader(104),
|
|
103
|
-
makeHeader(105),
|
|
104
|
-
makeHeader(106),
|
|
105
|
-
makeHeader(107),
|
|
106
|
-
makeHeader(108),
|
|
107
|
-
makeHeader(109),
|
|
108
|
-
makeHeader(110),
|
|
109
|
-
];
|
|
110
|
-
const spy = jest.spyOn(storeCache.poi, 'getPoiBlocksBefore');
|
|
111
|
-
const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(makeHeader(111, true));
|
|
112
|
-
expect(rewind?.blockHeight).toEqual(99);
|
|
113
|
-
expect(spy).toHaveBeenCalled();
|
|
114
|
-
});
|
|
115
|
-
// The finalized block is after the cached unfinalized blocks, they should be rechecked
|
|
116
|
-
it('startup, correctly checks for forks after cached unfinalized blocks', async () => {
|
|
117
|
-
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
118
|
-
makeHeader(99, true),
|
|
119
|
-
makeHeader(100),
|
|
120
|
-
makeHeader(101),
|
|
121
|
-
]));
|
|
122
|
-
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);
|
|
123
|
-
const rewind = jest.fn();
|
|
124
|
-
await unfinalizedBlocks.init(rewind);
|
|
125
|
-
// It should fall back to poi in this case
|
|
126
|
-
expect(rewind).toHaveBeenCalledWith({
|
|
127
|
-
blockHash: '0x00ABC99f',
|
|
128
|
-
blockHeight: 99,
|
|
129
|
-
parentHash: '0x00ABC98f',
|
|
130
|
-
timestamp: genBlockDate(99),
|
|
131
|
-
});
|
|
132
|
-
});
|
|
133
|
-
it('startup, correctly checks for forks within cached unfinalized blocks', async () => {
|
|
134
|
-
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
135
|
-
makeHeader(110),
|
|
136
|
-
makeHeader(111),
|
|
137
|
-
makeHeader(112),
|
|
138
|
-
]));
|
|
139
|
-
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
140
|
-
const rewind = jest.fn();
|
|
141
|
-
await unfinalizedBlocks.init(rewind);
|
|
142
|
-
// It should fall back to poi in this case
|
|
143
|
-
expect(rewind).toHaveBeenCalledWith({
|
|
144
|
-
blockHash: '0x00ABC99f',
|
|
145
|
-
blockHeight: 99,
|
|
146
|
-
parentHash: '0x00ABC98f',
|
|
147
|
-
timestamp: genBlockDate(99),
|
|
148
|
-
});
|
|
149
|
-
});
|
|
150
|
-
it('doesnt throw if there are no unfinalized blocks on startup', async () => {
|
|
151
|
-
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
152
|
-
await expect(unfinalizedBlocks.init(jest.fn())).resolves.not.toThrow();
|
|
153
|
-
});
|
|
154
|
-
it('rewinds using blockForkReindex value if poi is not enabled', async () => {
|
|
155
|
-
// Do this to "disable" poi
|
|
156
|
-
storeCache.poi = null;
|
|
157
|
-
await storeCache.metadata.set(node_core_1.METADATA_UNFINALIZED_BLOCKS_KEY, JSON.stringify([
|
|
158
|
-
makeHeader(110),
|
|
159
|
-
makeHeader(111),
|
|
160
|
-
makeHeader(112),
|
|
161
|
-
]));
|
|
162
|
-
await storeCache.metadata.set(node_core_1.METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);
|
|
163
|
-
const rewind = jest.fn();
|
|
164
|
-
await unfinalizedBlocks.init(rewind);
|
|
165
|
-
// It should fall back to poi in this case
|
|
166
|
-
expect(rewind).toHaveBeenCalledWith({ blockHeight: 0 });
|
|
167
|
-
});
|
|
168
|
-
});
|
|
169
|
-
//# sourceMappingURL=unfinalizedBlocks.service.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"unfinalizedBlocks.service.spec.js","sourceRoot":"","sources":["../../src/indexer/unfinalizedBlocks.service.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { hexZeroPad } from '@ethersproject/bytes';\nimport {\n CacheMetadataModel,\n Header,\n NodeConfig,\n PoiBlock,\n StoreCacheService,\n METADATA_UNFINALIZED_BLOCKS_KEY,\n METADATA_LAST_FINALIZED_PROCESSED_KEY,\n} from '@subql/node-core';\nimport { BlockchainService } from '../blockchain.service';\nimport { EthereumNodeConfig } from '../configure/NodeConfig';\nimport { EthereumApiService } from '../ethereum';\nimport { UnfinalizedBlocksService } from './unfinalizedBlocks.service';\n\n// Adds 0 padding so we can convert to POI block\nconst hexify = (input: string) => hexZeroPad(input, 4);\n\n// Mock 1740100000 is the timestamp of the genesis block\nconst genBlockTimestamp = (height: number) => (1740100000 + height) * 1000;\nconst genBlockDate = (height: number) => new Date(genBlockTimestamp(height));\n\nconst makeHeader = (height: number, finalized?: boolean): Header => ({\n blockHeight: height,\n blockHash: hexify(`0xABC${height}${finalized ? 'f' : ''}`),\n parentHash: hexify(`0xABC${height - 1}${finalized ? 'f' : ''}`),\n timestamp: genBlockDate(height),\n});\n\nconst getMockApi = (): EthereumApiService => {\n return {\n api: {\n getBlockByHeightOrHash: (hash: string | number) => {\n const num =\n typeof hash === 'number'\n ? hash\n : Number(\n hash\n .toString()\n .replace('0x', '')\n .replace('ABC', '')\n .replace('f', ''),\n );\n return Promise.resolve({\n number: num,\n hash: typeof hash === 'number' ? hexify(`0xABC${hash}f`) : hash,\n parentHash: hexify(`0xABC${num - 1}f`),\n timestamp: genBlockTimestamp(num) / 1000,\n });\n },\n getFinalizedBlock: jest.fn(() => ({\n number: 110,\n hash: '0xABC110f',\n parentHash: '0xABC109f',\n timestamp: genBlockTimestamp(110) / 1000,\n })),\n },\n } as any;\n};\n\nfunction getMockMetadata(): any {\n const data: Record<string, any> = {};\n return {\n upsert: ({ key, value }: any) => (data[key] = value),\n findOne: ({ where: { key } }: any) => ({ value: data[key] }),\n findByPk: (key: string) => data[key],\n find: (key: string) => data[key],\n } as any;\n}\n\nfunction mockStoreCache(): StoreCacheService {\n return {\n metadata: new CacheMetadataModel(getMockMetadata()),\n poi: {\n getPoiBlocksBefore: jest.fn(() => [\n PoiBlock.create(99, hexify('0xABC99f'), new Uint8Array(), ''),\n ]),\n },\n } as any as StoreCacheService;\n}\n\ndescribe('UnfinalizedBlockService', () => {\n let unfinalizedBlocks: UnfinalizedBlocksService;\n let storeCache: StoreCacheService;\n\n beforeEach(() => {\n storeCache = mockStoreCache();\n\n const blockchainService = new BlockchainService(getMockApi());\n\n unfinalizedBlocks = new UnfinalizedBlocksService(\n new NodeConfig({\n unfinalizedBlocks: true,\n blockForkReindex: 1000,\n } as any) as EthereumNodeConfig,\n storeCache,\n blockchainService,\n );\n });\n\n it('handles a block fork', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103, true), // Where the fork started\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(103);\n });\n\n it('uses POI blocks if there are not enough cached unfinalized blocks', async () => {\n await unfinalizedBlocks.init(jest.fn());\n\n (unfinalizedBlocks as any)._unfinalizedBlocks = [\n makeHeader(100),\n makeHeader(101),\n makeHeader(102),\n makeHeader(103),\n makeHeader(104),\n makeHeader(105),\n makeHeader(106),\n makeHeader(107),\n makeHeader(108),\n makeHeader(109),\n makeHeader(110),\n ];\n\n const spy = jest.spyOn(storeCache.poi as any, 'getPoiBlocksBefore');\n\n const rewind = await unfinalizedBlocks.processUnfinalizedBlockHeader(\n makeHeader(111, true),\n );\n\n expect(rewind?.blockHeight).toEqual(99);\n expect(spy).toHaveBeenCalled();\n });\n\n // The finalized block is after the cached unfinalized blocks, they should be rechecked\n it('startup, correctly checks for forks after cached unfinalized blocks', async () => {\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(99, true),\n makeHeader(100),\n makeHeader(101),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 99);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('startup, correctly checks for forks within cached unfinalized blocks', async () => {\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({\n blockHash: '0x00ABC99f',\n blockHeight: 99,\n parentHash: '0x00ABC98f',\n timestamp: genBlockDate(99),\n });\n });\n\n it('doesnt throw if there are no unfinalized blocks on startup', async () => {\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n await expect(unfinalizedBlocks.init(jest.fn())).resolves.not.toThrow();\n });\n\n it('rewinds using blockForkReindex value if poi is not enabled', async () => {\n // Do this to \"disable\" poi\n (storeCache as any).poi = null;\n\n await storeCache.metadata.set(\n METADATA_UNFINALIZED_BLOCKS_KEY,\n JSON.stringify(<Header[]>[\n makeHeader(110),\n makeHeader(111),\n makeHeader(112),\n ]),\n );\n\n await storeCache.metadata.set(METADATA_LAST_FINALIZED_PROCESSED_KEY, 109);\n\n const rewind = jest.fn();\n\n await unfinalizedBlocks.init(rewind);\n\n // It should fall back to poi in this case\n expect(rewind).toHaveBeenCalledWith({ blockHeight: 0 });\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,99 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
4
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
5
|
-
const string_1 = require("./string");
|
|
6
|
-
describe('Project utilities - Custom Type Resolution', () => {
|
|
7
|
-
// Create mock custom types map (as would be extracted from ABI)
|
|
8
|
-
const mockCustomTypes = new Map([
|
|
9
|
-
[
|
|
10
|
-
'DisputeType',
|
|
11
|
-
{ name: 'DisputeType', type: 'enum', resolvedType: 'uint8' },
|
|
12
|
-
],
|
|
13
|
-
[
|
|
14
|
-
'MoreData',
|
|
15
|
-
{ name: 'MoreData', type: 'struct', resolvedType: '(bytes32,bytes32)' },
|
|
16
|
-
],
|
|
17
|
-
['Status', { name: 'Status', type: 'enum', resolvedType: 'uint8' }],
|
|
18
|
-
[
|
|
19
|
-
'NestedData',
|
|
20
|
-
{ name: 'NestedData', type: 'struct', resolvedType: '(uint256,bool)' },
|
|
21
|
-
],
|
|
22
|
-
]);
|
|
23
|
-
describe('resolveCustomTypesInSignature', () => {
|
|
24
|
-
const customTypes = mockCustomTypes;
|
|
25
|
-
it('should resolve enum types to uint8 in signature', () => {
|
|
26
|
-
const signature = 'DisputeOpen(uint256,address,address,DisputeType)';
|
|
27
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
28
|
-
expect(resolved).toBe('DisputeOpen(uint256,address,address,uint8)');
|
|
29
|
-
});
|
|
30
|
-
it('should resolve struct types to tuple format in signature', () => {
|
|
31
|
-
const signature = 'DataUpdated(address,MoreData)';
|
|
32
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
33
|
-
expect(resolved).toBe('DataUpdated(address,(bytes32,bytes32))');
|
|
34
|
-
});
|
|
35
|
-
it('should handle dotted namespace custom types in signature', () => {
|
|
36
|
-
// When custom types are defined with namespaces in the ABI (e.g., "enum MyContract.Status")
|
|
37
|
-
// they are extracted with just the type name ("Status")
|
|
38
|
-
// But signatures may reference them with full namespace
|
|
39
|
-
const signature = 'ComplexEvent(Status,NestedData)';
|
|
40
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
41
|
-
expect(resolved).toBe('ComplexEvent(uint8,(uint256,bool))');
|
|
42
|
-
});
|
|
43
|
-
it('should handle fully qualified names when custom types are extracted with namespace', () => {
|
|
44
|
-
// Create custom types map with namespace-aware entries
|
|
45
|
-
const namespacedTypes = new Map([
|
|
46
|
-
[
|
|
47
|
-
'MyContract.Status',
|
|
48
|
-
{ name: 'MyContract.Status', type: 'enum', resolvedType: 'uint8' },
|
|
49
|
-
],
|
|
50
|
-
[
|
|
51
|
-
'MyContract.NestedData',
|
|
52
|
-
{
|
|
53
|
-
name: 'MyContract.NestedData',
|
|
54
|
-
type: 'struct',
|
|
55
|
-
resolvedType: '(uint256,bool)',
|
|
56
|
-
},
|
|
57
|
-
],
|
|
58
|
-
]);
|
|
59
|
-
const signature = 'ComplexEvent(MyContract.Status,MyContract.NestedData)';
|
|
60
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, namespacedTypes);
|
|
61
|
-
expect(resolved).toBe('ComplexEvent(uint8,(uint256,bool))');
|
|
62
|
-
});
|
|
63
|
-
it('should handle realistic manifest signatures with indexed parameters', () => {
|
|
64
|
-
const signature = 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, DisputeType _type)';
|
|
65
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
66
|
-
// Should resolve DisputeType to uint8, preserving the rest of the signature
|
|
67
|
-
expect(resolved).toBe('DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, uint8 _type)');
|
|
68
|
-
});
|
|
69
|
-
it('should not modify signatures without custom types', () => {
|
|
70
|
-
const signature = 'Transfer(address,address,uint256)';
|
|
71
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
72
|
-
expect(resolved).toBe(signature);
|
|
73
|
-
});
|
|
74
|
-
it('should handle multiple custom types in one signature', () => {
|
|
75
|
-
const signature = 'MultiEvent(DisputeType,MoreData,Status)';
|
|
76
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
77
|
-
expect(resolved).toBe('MultiEvent(uint8,(bytes32,bytes32),uint8)');
|
|
78
|
-
});
|
|
79
|
-
it('should be case-sensitive for custom type names', () => {
|
|
80
|
-
const signature = 'Event(disputetype,moredata)'; // lowercase
|
|
81
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
82
|
-
expect(resolved).toBe(signature);
|
|
83
|
-
});
|
|
84
|
-
it('should only replace whole word matches', () => {
|
|
85
|
-
const signature = 'Event(DisputeTypeExtended,MoreDataPlus)';
|
|
86
|
-
const resolved = (0, string_1.resolveCustomTypesInSignature)(signature, customTypes);
|
|
87
|
-
expect(resolved).toBe(signature);
|
|
88
|
-
});
|
|
89
|
-
});
|
|
90
|
-
describe('Integration: Project load-time resolution flow', () => {
|
|
91
|
-
it('should demonstrate the full resolution flow', () => {
|
|
92
|
-
const customTypes = mockCustomTypes;
|
|
93
|
-
const originalFilter = 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, DisputeType _type)';
|
|
94
|
-
const resolvedFilter = (0, string_1.resolveCustomTypesInSignature)(originalFilter, customTypes);
|
|
95
|
-
expect(resolvedFilter).toBe('DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, uint8 _type)');
|
|
96
|
-
});
|
|
97
|
-
});
|
|
98
|
-
});
|
|
99
|
-
//# sourceMappingURL=project.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"project.spec.js","sourceRoot":"","sources":["../../src/utils/project.spec.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAEnC,qCAAwE;AAExE,QAAQ,CAAC,4CAA4C,EAAE,GAAG,EAAE;IAC1D,gEAAgE;IAChE,MAAM,eAAe,GAAG,IAAI,GAAG,CAAwB;QACrD;YACE,aAAa;YACb,EAAE,IAAI,EAAE,aAAa,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE;SAC7D;QACD;YACE,UAAU;YACV,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,mBAAmB,EAAE;SACxE;QACD,CAAC,QAAQ,EAAE,EAAE,IAAI,EAAE,QAAQ,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE,CAAC;QACnE;YACE,YAAY;YACZ,EAAE,IAAI,EAAE,YAAY,EAAE,IAAI,EAAE,QAAQ,EAAE,YAAY,EAAE,gBAAgB,EAAE;SACvE;KACF,CAAC,CAAC;IAEH,QAAQ,CAAC,+BAA+B,EAAE,GAAG,EAAE;QAC7C,MAAM,WAAW,GAAG,eAAe,CAAC;QAEpC,EAAE,CAAC,iDAAiD,EAAE,GAAG,EAAE;YACzD,MAAM,SAAS,GAAG,kDAAkD,CAAC;YACrE,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,4CAA4C,CAAC,CAAC;QACtE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,MAAM,SAAS,GAAG,+BAA+B,CAAC;YAClD,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,wCAAwC,CAAC,CAAC;QAClE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,0DAA0D,EAAE,GAAG,EAAE;YAClE,4FAA4F;YAC5F,wDAAwD;YACxD,wDAAwD;YACxD,MAAM,SAAS,GAAG,iCAAiC,CAAC;YACpD,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,oFAAoF,EAAE,GAAG,EAAE;YAC5F,uDAAuD;YACvD,MAAM,eAAe,GAAG,IAAI,GAAG,CAAwB;gBACrD;oBACE,mBAAmB;oBACnB,EAAE,IAAI,EAAE,mBAAmB,EAAE,IAAI,EAAE,MAAM,EAAE,YAAY,EAAE,OAAO,EAAE;iBACnE;gBACD;oBACE,uBAAuB;oBACvB;wBACE,IAAI,EAAE,uBAAuB;wBAC7B,IAAI,EAAE,QAAQ;wBACd,YAAY,EAAE,gBAAgB;qBAC/B;iBACF;aACF,CAAC,CAAC;YAEH,MAAM,SAAS,GAAG,uDAAuD,CAAC;YAC1E,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAC5C,SAAS,EACT,eAAe,CAChB,CAAC;YAEF,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,oCAAoC,CAAC,CAAC;QAC9D,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,qEAAqE,EAAE,GAAG,EAAE;YAC7E,MAAM,SAAS,GACb,8FAA8F,CAAC;YACjG,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,4EAA4E;YAC5E,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CACnB,wFAAwF,CACzF,CAAC;QACJ,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,mDAAmD,EAAE,GAAG,EAAE;YAC3D,MAAM,SAAS,GAAG,mCAAmC,CAAC;YACtD,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,sDAAsD,EAAE,GAAG,EAAE;YAC9D,MAAM,SAAS,GAAG,yCAAyC,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,2CAA2C,CAAC,CAAC;QACrE,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,gDAAgD,EAAE,GAAG,EAAE;YACxD,MAAM,SAAS,GAAG,6BAA6B,CAAC,CAAC,YAAY;YAC7D,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;QAEH,EAAE,CAAC,wCAAwC,EAAE,GAAG,EAAE;YAChD,MAAM,SAAS,GAAG,yCAAyC,CAAC;YAC5D,MAAM,QAAQ,GAAG,IAAA,sCAA6B,EAAC,SAAS,EAAE,WAAW,CAAC,CAAC;YAEvE,MAAM,CAAC,QAAQ,CAAC,CAAC,IAAI,CAAC,SAAS,CAAC,CAAC;QACnC,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;IAEH,QAAQ,CAAC,gDAAgD,EAAE,GAAG,EAAE;QAC9D,EAAE,CAAC,6CAA6C,EAAE,GAAG,EAAE;YACrD,MAAM,WAAW,GAAG,eAAe,CAAC;YAEpC,MAAM,cAAc,GAClB,8FAA8F,CAAC;YAEjG,MAAM,cAAc,GAAG,IAAA,sCAA6B,EAClD,cAAc,EACd,WAAW,CACZ,CAAC;YAEF,MAAM,CAAC,cAAc,CAAC,CAAC,IAAI,CACzB,wFAAwF,CACzF,CAAC;QACJ,CAAC,CAAC,CAAC;IACL,CAAC,CAAC,CAAC;AACL,CAAC,CAAC,CAAC","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { resolveCustomTypesInSignature, AbiCustomType } from './string';\n\ndescribe('Project utilities - Custom Type Resolution', () => {\n // Create mock custom types map (as would be extracted from ABI)\n const mockCustomTypes = new Map<string, AbiCustomType>([\n [\n 'DisputeType',\n { name: 'DisputeType', type: 'enum', resolvedType: 'uint8' },\n ],\n [\n 'MoreData',\n { name: 'MoreData', type: 'struct', resolvedType: '(bytes32,bytes32)' },\n ],\n ['Status', { name: 'Status', type: 'enum', resolvedType: 'uint8' }],\n [\n 'NestedData',\n { name: 'NestedData', type: 'struct', resolvedType: '(uint256,bool)' },\n ],\n ]);\n\n describe('resolveCustomTypesInSignature', () => {\n const customTypes = mockCustomTypes;\n\n it('should resolve enum types to uint8 in signature', () => {\n const signature = 'DisputeOpen(uint256,address,address,DisputeType)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe('DisputeOpen(uint256,address,address,uint8)');\n });\n\n it('should resolve struct types to tuple format in signature', () => {\n const signature = 'DataUpdated(address,MoreData)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe('DataUpdated(address,(bytes32,bytes32))');\n });\n\n it('should handle dotted namespace custom types in signature', () => {\n // When custom types are defined with namespaces in the ABI (e.g., \"enum MyContract.Status\")\n // they are extracted with just the type name (\"Status\")\n // But signatures may reference them with full namespace\n const signature = 'ComplexEvent(Status,NestedData)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe('ComplexEvent(uint8,(uint256,bool))');\n });\n\n it('should handle fully qualified names when custom types are extracted with namespace', () => {\n // Create custom types map with namespace-aware entries\n const namespacedTypes = new Map<string, AbiCustomType>([\n [\n 'MyContract.Status',\n { name: 'MyContract.Status', type: 'enum', resolvedType: 'uint8' },\n ],\n [\n 'MyContract.NestedData',\n {\n name: 'MyContract.NestedData',\n type: 'struct',\n resolvedType: '(uint256,bool)',\n },\n ],\n ]);\n\n const signature = 'ComplexEvent(MyContract.Status,MyContract.NestedData)';\n const resolved = resolveCustomTypesInSignature(\n signature,\n namespacedTypes,\n );\n\n expect(resolved).toBe('ComplexEvent(uint8,(uint256,bool))');\n });\n\n it('should handle realistic manifest signatures with indexed parameters', () => {\n const signature =\n 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, DisputeType _type)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n // Should resolve DisputeType to uint8, preserving the rest of the signature\n expect(resolved).toBe(\n 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, uint8 _type)',\n );\n });\n\n it('should not modify signatures without custom types', () => {\n const signature = 'Transfer(address,address,uint256)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe(signature);\n });\n\n it('should handle multiple custom types in one signature', () => {\n const signature = 'MultiEvent(DisputeType,MoreData,Status)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe('MultiEvent(uint8,(bytes32,bytes32),uint8)');\n });\n\n it('should be case-sensitive for custom type names', () => {\n const signature = 'Event(disputetype,moredata)'; // lowercase\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe(signature);\n });\n\n it('should only replace whole word matches', () => {\n const signature = 'Event(DisputeTypeExtended,MoreDataPlus)';\n const resolved = resolveCustomTypesInSignature(signature, customTypes);\n\n expect(resolved).toBe(signature);\n });\n });\n\n describe('Integration: Project load-time resolution flow', () => {\n it('should demonstrate the full resolution flow', () => {\n const customTypes = mockCustomTypes;\n\n const originalFilter =\n 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, DisputeType _type)';\n\n const resolvedFilter = resolveCustomTypesInSignature(\n originalFilter,\n customTypes,\n );\n\n expect(resolvedFilter).toBe(\n 'DisputeOpen(uint256 indexed disputeId, address fisherman, address runner, uint8 _type)',\n );\n });\n });\n});\n"]}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
export {};
|
|
@@ -1,103 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors
|
|
3
|
-
// SPDX-License-Identifier: GPL-3.0
|
|
4
|
-
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
5
|
-
if (k2 === undefined) k2 = k;
|
|
6
|
-
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
7
|
-
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
8
|
-
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
9
|
-
}
|
|
10
|
-
Object.defineProperty(o, k2, desc);
|
|
11
|
-
}) : (function(o, m, k, k2) {
|
|
12
|
-
if (k2 === undefined) k2 = k;
|
|
13
|
-
o[k2] = m[k];
|
|
14
|
-
}));
|
|
15
|
-
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
16
|
-
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
17
|
-
}) : function(o, v) {
|
|
18
|
-
o["default"] = v;
|
|
19
|
-
});
|
|
20
|
-
var __importStar = (this && this.__importStar) || (function () {
|
|
21
|
-
var ownKeys = function(o) {
|
|
22
|
-
ownKeys = Object.getOwnPropertyNames || function (o) {
|
|
23
|
-
var ar = [];
|
|
24
|
-
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
|
|
25
|
-
return ar;
|
|
26
|
-
};
|
|
27
|
-
return ownKeys(o);
|
|
28
|
-
};
|
|
29
|
-
return function (mod) {
|
|
30
|
-
if (mod && mod.__esModule) return mod;
|
|
31
|
-
var result = {};
|
|
32
|
-
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
|
|
33
|
-
__setModuleDefault(result, mod);
|
|
34
|
-
return result;
|
|
35
|
-
};
|
|
36
|
-
})();
|
|
37
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
38
|
-
const hash_1 = require("@ethersproject/hash");
|
|
39
|
-
const hashModule = __importStar(require("@ethersproject/hash"));
|
|
40
|
-
const string_1 = require("./string");
|
|
41
|
-
describe('String utilities', () => {
|
|
42
|
-
describe('eventToTopic', () => {
|
|
43
|
-
it('should return hex string as-is', () => {
|
|
44
|
-
const hexInput = '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';
|
|
45
|
-
expect((0, string_1.eventToTopic)(hexInput)).toBe(hexInput);
|
|
46
|
-
});
|
|
47
|
-
it('should generate topic hash for standard event signature', () => {
|
|
48
|
-
const signature = 'Transfer(address,address,uint256)';
|
|
49
|
-
const expected = (0, hash_1.id)(signature);
|
|
50
|
-
expect((0, string_1.eventToTopic)(signature)).toBe(expected);
|
|
51
|
-
});
|
|
52
|
-
it('should generate topic hash for resolved signatures (enum as uint8)', () => {
|
|
53
|
-
// After project load-time resolution, enum types are already resolved to uint8
|
|
54
|
-
const resolvedSignature = 'DisputeOpen(uint256,address,address,uint8)';
|
|
55
|
-
const expected = (0, hash_1.id)(resolvedSignature);
|
|
56
|
-
expect((0, string_1.eventToTopic)(resolvedSignature)).toBe(expected);
|
|
57
|
-
});
|
|
58
|
-
it('should generate topic hash for resolved signatures (struct as tuple)', () => {
|
|
59
|
-
// After project load-time resolution, struct types are already resolved to tuples
|
|
60
|
-
const resolvedSignature = 'DataUpdated(address,(bytes32,bytes32))';
|
|
61
|
-
const expected = (0, hash_1.id)(resolvedSignature);
|
|
62
|
-
expect((0, string_1.eventToTopic)(resolvedSignature)).toBe(expected);
|
|
63
|
-
});
|
|
64
|
-
it('should cache results', () => {
|
|
65
|
-
// Use a unique signature to avoid cache pollution from other tests
|
|
66
|
-
const signature = 'UniqueTestEvent(bytes32,uint256,address)';
|
|
67
|
-
const idSpy = jest.spyOn(hashModule, 'id');
|
|
68
|
-
const firstResult = (0, string_1.eventToTopic)(signature);
|
|
69
|
-
const callCountAfterFirst = idSpy.mock.calls.length;
|
|
70
|
-
const secondResult = (0, string_1.eventToTopic)(signature);
|
|
71
|
-
const callCountAfterSecond = idSpy.mock.calls.length;
|
|
72
|
-
// Second call should not invoke id() again due to caching
|
|
73
|
-
expect(callCountAfterSecond).toBe(callCountAfterFirst);
|
|
74
|
-
expect(firstResult).toBe(secondResult);
|
|
75
|
-
idSpy.mockRestore();
|
|
76
|
-
});
|
|
77
|
-
});
|
|
78
|
-
describe('functionToSighash', () => {
|
|
79
|
-
it('should return hex string as-is', () => {
|
|
80
|
-
const hexInput = '0x12345678';
|
|
81
|
-
expect((0, string_1.functionToSighash)(hexInput)).toBe(hexInput);
|
|
82
|
-
});
|
|
83
|
-
it('should generate function sighash for standard function signature', () => {
|
|
84
|
-
const signature = 'transfer(address,uint256)';
|
|
85
|
-
const expected = '0xa9059cbb'; // Known sighash for transfer function
|
|
86
|
-
expect((0, string_1.functionToSighash)(signature)).toBe(expected);
|
|
87
|
-
});
|
|
88
|
-
it('should cache results', () => {
|
|
89
|
-
// Use a unique signature to avoid cache pollution from other tests
|
|
90
|
-
const signature = 'uniqueTestFunction(bytes32,uint256)';
|
|
91
|
-
const idSpy = jest.spyOn(hashModule, 'id');
|
|
92
|
-
const firstResult = (0, string_1.functionToSighash)(signature);
|
|
93
|
-
const callCountAfterFirst = idSpy.mock.calls.length;
|
|
94
|
-
const secondResult = (0, string_1.functionToSighash)(signature);
|
|
95
|
-
const callCountAfterSecond = idSpy.mock.calls.length;
|
|
96
|
-
// Second call should not invoke id() again due to caching
|
|
97
|
-
expect(callCountAfterSecond).toBe(callCountAfterFirst);
|
|
98
|
-
expect(firstResult).toBe(secondResult);
|
|
99
|
-
idSpy.mockRestore();
|
|
100
|
-
});
|
|
101
|
-
});
|
|
102
|
-
});
|
|
103
|
-
//# sourceMappingURL=string.spec.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"string.spec.js","sourceRoot":"","sources":["../../src/utils/string.spec.ts"],"names":[],"mappings":"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","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { id } from '@ethersproject/hash';\nimport * as hashModule from '@ethersproject/hash';\nimport { eventToTopic, functionToSighash } from './string';\n\ndescribe('String utilities', () => {\n describe('eventToTopic', () => {\n it('should return hex string as-is', () => {\n const hexInput =\n '0x1234567890abcdef1234567890abcdef1234567890abcdef1234567890abcdef';\n expect(eventToTopic(hexInput)).toBe(hexInput);\n });\n\n it('should generate topic hash for standard event signature', () => {\n const signature = 'Transfer(address,address,uint256)';\n const expected = id(signature);\n expect(eventToTopic(signature)).toBe(expected);\n });\n\n it('should generate topic hash for resolved signatures (enum as uint8)', () => {\n // After project load-time resolution, enum types are already resolved to uint8\n const resolvedSignature = 'DisputeOpen(uint256,address,address,uint8)';\n const expected = id(resolvedSignature);\n expect(eventToTopic(resolvedSignature)).toBe(expected);\n });\n\n it('should generate topic hash for resolved signatures (struct as tuple)', () => {\n // After project load-time resolution, struct types are already resolved to tuples\n const resolvedSignature = 'DataUpdated(address,(bytes32,bytes32))';\n const expected = id(resolvedSignature);\n expect(eventToTopic(resolvedSignature)).toBe(expected);\n });\n\n it('should cache results', () => {\n // Use a unique signature to avoid cache pollution from other tests\n const signature = 'UniqueTestEvent(bytes32,uint256,address)';\n const idSpy = jest.spyOn(hashModule, 'id');\n\n const firstResult = eventToTopic(signature);\n const callCountAfterFirst = idSpy.mock.calls.length;\n\n const secondResult = eventToTopic(signature);\n const callCountAfterSecond = idSpy.mock.calls.length;\n\n // Second call should not invoke id() again due to caching\n expect(callCountAfterSecond).toBe(callCountAfterFirst);\n expect(firstResult).toBe(secondResult);\n\n idSpy.mockRestore();\n });\n });\n\n describe('functionToSighash', () => {\n it('should return hex string as-is', () => {\n const hexInput = '0x12345678';\n expect(functionToSighash(hexInput)).toBe(hexInput);\n });\n\n it('should generate function sighash for standard function signature', () => {\n const signature = 'transfer(address,uint256)';\n const expected = '0xa9059cbb'; // Known sighash for transfer function\n expect(functionToSighash(signature)).toBe(expected);\n });\n\n it('should cache results', () => {\n // Use a unique signature to avoid cache pollution from other tests\n const signature = 'uniqueTestFunction(bytes32,uint256)';\n const idSpy = jest.spyOn(hashModule, 'id');\n\n const firstResult = functionToSighash(signature);\n const callCountAfterFirst = idSpy.mock.calls.length;\n\n const secondResult = functionToSighash(signature);\n const callCountAfterSecond = idSpy.mock.calls.length;\n\n // Second call should not invoke id() again due to caching\n expect(callCountAfterSecond).toBe(callCountAfterFirst);\n expect(firstResult).toBe(secondResult);\n\n idSpy.mockRestore();\n });\n });\n});\n"]}
|