@subql/node-ethereum 6.2.1 → 6.2.2-0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.createSubQueryProject = createSubQueryProject;
6
6
  const common_ethereum_1 = require("@subql/common-ethereum");
7
7
  const node_core_1 = require("@subql/node-core");
8
+ const project_1 = require("../utils/project");
8
9
  const { version: packageVersion } = require('../../package.json');
9
10
  async function createSubQueryProject(path, rawManifest, reader, root, // If project local then directory otherwise temp directory
10
11
  networkOverrides) {
@@ -20,6 +21,12 @@ networkOverrides) {
20
21
  isRuntimeDs: common_ethereum_1.isRuntimeDs,
21
22
  isCustomDs: common_ethereum_1.isCustomDs,
22
23
  });
24
+ // Resolve custom types in topic filters at project load time
25
+ (0, project_1.resolveTopicFiltersInProject)(project.dataSources, root);
26
+ // Also resolve in templates
27
+ if (project.templates && project.templates.length > 0) {
28
+ (0, project_1.resolveTopicFiltersInProject)(project.templates, root);
29
+ }
23
30
  return project;
24
31
  }
25
32
  //# sourceMappingURL=SubqueryProject.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"SubqueryProject.js","sourceRoot":"","sources":["../../src/configure/SubqueryProject.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAqCnC,sDAqBC;AAxDD,4DAMgC;AAChC,gDAAmE;AASnE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAmB3D,KAAK,UAAU,qBAAqB,CACzC,IAAY,EACZ,WAAoB,EACpB,MAAc,EACd,IAAY,EAAE,2DAA2D;AACzE,gBAAyC;IAEzC,MAAM,OAAO,GAAG,MAAM,+BAAmB,CAAC,MAAM,CAAkB;QAChE,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,8CAA4B,EAAC,GAAG,CAAC,CAAC,QAAQ;QAClE,IAAI;QACJ,WAAW;QACX,MAAM;QACN,IAAI;QACJ,UAAU,EAAE,cAAc;QAC1B,gBAAgB,EAAE,qCAAmB,CAAC,KAAK;QAC3C,gBAAgB;QAChB,WAAW,EAAX,6BAAW;QACX,UAAU,EAAV,4BAAU;KACX,CAAC,CAAC;IAEH,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport {\n parseEthereumProjectManifest,\n SubqlEthereumDataSource,\n isRuntimeDs,\n EthereumHandlerKind,\n isCustomDs,\n} from '@subql/common-ethereum';\nimport { BaseSubqueryProject, CronFilter } from '@subql/node-core';\nimport { Reader } from '@subql/types-core';\nimport {\n EthereumNetworkConfig,\n RuntimeDatasourceTemplate,\n CustomDatasourceTemplate,\n EthereumBlockFilter,\n} from '@subql/types-ethereum';\n\nconst { version: packageVersion } = require('../../package.json');\n\nexport type EthereumProjectDs = SubqlEthereumDataSource;\n\nexport type EthereumProjectDsTemplate =\n | RuntimeDatasourceTemplate\n | CustomDatasourceTemplate;\n\nexport type SubqlProjectBlockFilter = EthereumBlockFilter & CronFilter;\n\n// This is the runtime type after we have mapped genesisHash to chainId and endpoint/dict have been provided when dealing with deployments\ntype NetworkConfig = EthereumNetworkConfig & { chainId: string };\n\nexport type SubqueryProject = BaseSubqueryProject<\n EthereumProjectDs,\n EthereumProjectDsTemplate,\n NetworkConfig\n>;\n\nexport async function createSubQueryProject(\n path: string,\n rawManifest: unknown,\n reader: Reader,\n root: string, // If project local then directory otherwise temp directory\n networkOverrides?: Partial<NetworkConfig>,\n): Promise<SubqueryProject> {\n const project = await BaseSubqueryProject.create<SubqueryProject>({\n parseManifest: (raw) => parseEthereumProjectManifest(raw).asV1_0_0,\n path,\n rawManifest,\n reader,\n root,\n nodeSemver: packageVersion,\n blockHandlerKind: EthereumHandlerKind.Block,\n networkOverrides,\n isRuntimeDs,\n isCustomDs,\n });\n\n return project;\n}\n"]}
1
+ {"version":3,"file":"SubqueryProject.js","sourceRoot":"","sources":["../../src/configure/SubqueryProject.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAsCnC,sDAgCC;AApED,4DAMgC;AAChC,gDAAmE;AAQnE,8CAAgE;AAEhE,MAAM,EAAE,OAAO,EAAE,cAAc,EAAE,GAAG,OAAO,CAAC,oBAAoB,CAAC,CAAC;AAmB3D,KAAK,UAAU,qBAAqB,CACzC,IAAY,EACZ,WAAoB,EACpB,MAAc,EACd,IAAY,EAAE,2DAA2D;AACzE,gBAAyC;IAEzC,MAAM,OAAO,GAAG,MAAM,+BAAmB,CAAC,MAAM,CAAkB;QAChE,aAAa,EAAE,CAAC,GAAG,EAAE,EAAE,CAAC,IAAA,8CAA4B,EAAC,GAAG,CAAC,CAAC,QAAQ;QAClE,IAAI;QACJ,WAAW;QACX,MAAM;QACN,IAAI;QACJ,UAAU,EAAE,cAAc;QAC1B,gBAAgB,EAAE,qCAAmB,CAAC,KAAK;QAC3C,gBAAgB;QAChB,WAAW,EAAX,6BAAW;QACX,UAAU,EAAV,4BAAU;KACX,CAAC,CAAC;IAEH,6DAA6D;IAC7D,IAAA,sCAA4B,EAAC,OAAO,CAAC,WAAW,EAAE,IAAI,CAAC,CAAC;IAExD,4BAA4B;IAC5B,IAAI,OAAO,CAAC,SAAS,IAAI,OAAO,CAAC,SAAS,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACtD,IAAA,sCAA4B,EAC1B,OAAO,CAAC,SAAsC,EAC9C,IAAI,CACL,CAAC;IACJ,CAAC;IAED,OAAO,OAAO,CAAC;AACjB,CAAC","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport {\n parseEthereumProjectManifest,\n SubqlEthereumDataSource,\n isRuntimeDs,\n EthereumHandlerKind,\n isCustomDs,\n} from '@subql/common-ethereum';\nimport { BaseSubqueryProject, CronFilter } from '@subql/node-core';\nimport { Reader } from '@subql/types-core';\nimport {\n EthereumNetworkConfig,\n RuntimeDatasourceTemplate,\n CustomDatasourceTemplate,\n EthereumBlockFilter,\n} from '@subql/types-ethereum';\nimport { resolveTopicFiltersInProject } from '../utils/project';\n\nconst { version: packageVersion } = require('../../package.json');\n\nexport type EthereumProjectDs = SubqlEthereumDataSource;\n\nexport type EthereumProjectDsTemplate =\n | RuntimeDatasourceTemplate\n | CustomDatasourceTemplate;\n\nexport type SubqlProjectBlockFilter = EthereumBlockFilter & CronFilter;\n\n// This is the runtime type after we have mapped genesisHash to chainId and endpoint/dict have been provided when dealing with deployments\ntype NetworkConfig = EthereumNetworkConfig & { chainId: string };\n\nexport type SubqueryProject = BaseSubqueryProject<\n EthereumProjectDs,\n EthereumProjectDsTemplate,\n NetworkConfig\n>;\n\nexport async function createSubQueryProject(\n path: string,\n rawManifest: unknown,\n reader: Reader,\n root: string, // If project local then directory otherwise temp directory\n networkOverrides?: Partial<NetworkConfig>,\n): Promise<SubqueryProject> {\n const project = await BaseSubqueryProject.create<SubqueryProject>({\n parseManifest: (raw) => parseEthereumProjectManifest(raw).asV1_0_0,\n path,\n rawManifest,\n reader,\n root,\n nodeSemver: packageVersion,\n blockHandlerKind: EthereumHandlerKind.Block,\n networkOverrides,\n isRuntimeDs,\n isCustomDs,\n });\n\n // Resolve custom types in topic filters at project load time\n resolveTopicFiltersInProject(project.dataSources, root);\n\n // Also resolve in templates\n if (project.templates && project.templates.length > 0) {\n resolveTopicFiltersInProject(\n project.templates as SubqlEthereumDataSource[],\n root,\n );\n }\n\n return project;\n}\n"]}
@@ -1,7 +1,20 @@
1
- import { SubqlRuntimeHandler, SubqlCustomHandler, SubqlHandler } from '@subql/common-ethereum';
1
+ import { SubqlRuntimeHandler, SubqlCustomHandler, SubqlHandler, SubqlEthereumDataSource } from '@subql/common-ethereum';
2
2
  import { EthereumProjectDs, SubqueryProject } from '../configure/SubqueryProject';
3
3
  export declare function isBaseHandler(handler: SubqlHandler): handler is SubqlRuntimeHandler;
4
4
  export declare function isCustomHandler(handler: SubqlHandler): handler is SubqlCustomHandler;
5
5
  export declare function retryOnFailEth<T>(request: () => Promise<T>, errors?: string[]): Promise<T>;
6
6
  export declare function onlyHasLogDataSources(dataSources: EthereumProjectDs[]): boolean;
7
7
  export declare function isOnlyEventHandlers(project: SubqueryProject): boolean;
8
+ /**
9
+ * Resolves custom types (enums, structs) in topic filters for all datasources at project load time.
10
+ * Mutates the datasource handlers' topic filters in-place.
11
+ *
12
+ * This ensures that:
13
+ * - Enums are replaced with uint8
14
+ * - Structs are replaced with tuple notation (type1,type2,...)
15
+ * - No runtime ABI resolution is needed
16
+ *
17
+ * @param dataSources - Array of datasources to process (mutated in-place)
18
+ * @param projectPath - The project root path for loading ABI files
19
+ */
20
+ export declare function resolveTopicFiltersInProject(dataSources: SubqlEthereumDataSource[], projectPath: string): void;
@@ -7,8 +7,11 @@ exports.isCustomHandler = isCustomHandler;
7
7
  exports.retryOnFailEth = retryOnFailEth;
8
8
  exports.onlyHasLogDataSources = onlyHasLogDataSources;
9
9
  exports.isOnlyEventHandlers = isOnlyEventHandlers;
10
+ exports.resolveTopicFiltersInProject = resolveTopicFiltersInProject;
10
11
  const common_ethereum_1 = require("@subql/common-ethereum");
11
12
  const node_core_1 = require("@subql/node-core");
13
+ const string_1 = require("./string");
14
+ const logger = (0, node_core_1.getLogger)('project');
12
15
  function isBaseHandler(handler) {
13
16
  return Object.values(common_ethereum_1.EthereumHandlerKind).includes(handler.kind);
14
17
  }
@@ -45,4 +48,101 @@ function isOnlyEventHandlers(project) {
45
48
  const hasNonEventTemplate = !!project.templates.find((ds) => dsContainsNonEventHandlers(ds));
46
49
  return !hasNonEventHandler && !hasNonEventTemplate;
47
50
  }
51
+ /**
52
+ * Helper function to load ABI interface from a datasource
53
+ */
54
+ function getAbiInterfaceFromDs(ds, projectPath) {
55
+ try {
56
+ if (!ds?.options?.abi || !projectPath) {
57
+ return undefined;
58
+ }
59
+ return (0, common_ethereum_1.getAbiInterface)(projectPath, ds.options.abi);
60
+ }
61
+ catch (error) {
62
+ logger.warn(`Failed to load ABI interface for datasource: ${error instanceof Error ? error.message : 'Unknown error'}`);
63
+ return undefined;
64
+ }
65
+ }
66
+ /**
67
+ * Process event handler topic filters and resolve custom types
68
+ */
69
+ function processEventHandlerFilters(handler, customTypes) {
70
+ if (handler.kind !== common_ethereum_1.EthereumHandlerKind.Event) {
71
+ return;
72
+ }
73
+ const logFilter = handler.filter;
74
+ if (!logFilter?.topics) {
75
+ return;
76
+ }
77
+ // Only resolve custom types in the first topic (topic0), which is the event signature
78
+ const topic = logFilter.topics[0];
79
+ if (typeof topic === 'string' &&
80
+ topic.trim() !== '' &&
81
+ !topic.startsWith('0x')) {
82
+ const resolved = (0, string_1.resolveCustomTypesInSignature)(topic, customTypes);
83
+ if (resolved !== topic) {
84
+ logFilter.topics[0] = resolved;
85
+ logger.info(`Resolved topic filter: "${topic}" -> "${resolved}"`);
86
+ }
87
+ }
88
+ }
89
+ /**
90
+ * Process transaction handler function filters and resolve custom types
91
+ */
92
+ function processTransactionHandlerFilters(handler, customTypes) {
93
+ if (handler.kind !== common_ethereum_1.EthereumHandlerKind.Call) {
94
+ return;
95
+ }
96
+ const txFilter = handler.filter;
97
+ if (!txFilter?.function) {
98
+ return;
99
+ }
100
+ const funcSig = txFilter.function;
101
+ if (typeof funcSig === 'string' && !funcSig.startsWith('0x')) {
102
+ const resolved = (0, string_1.resolveCustomTypesInSignature)(funcSig, customTypes);
103
+ if (resolved !== funcSig) {
104
+ txFilter.function = resolved;
105
+ logger.info(`Resolved function filter: "${funcSig}" -> "${resolved}"`);
106
+ }
107
+ }
108
+ }
109
+ /**
110
+ * Resolves custom types (enums, structs) in topic filters for all datasources at project load time.
111
+ * Mutates the datasource handlers' topic filters in-place.
112
+ *
113
+ * This ensures that:
114
+ * - Enums are replaced with uint8
115
+ * - Structs are replaced with tuple notation (type1,type2,...)
116
+ * - No runtime ABI resolution is needed
117
+ *
118
+ * @param dataSources - Array of datasources to process (mutated in-place)
119
+ * @param projectPath - The project root path for loading ABI files
120
+ */
121
+ function resolveTopicFiltersInProject(dataSources, projectPath) {
122
+ for (const ds of dataSources) {
123
+ try {
124
+ // Load ABI interface
125
+ const abiInterface = getAbiInterfaceFromDs(ds, projectPath);
126
+ if (!abiInterface) {
127
+ continue;
128
+ }
129
+ // Extract custom types once per datasource
130
+ const customTypes = (0, string_1.extractCustomTypesFromAbi)(abiInterface);
131
+ if (customTypes.size === 0) {
132
+ continue; // No custom types, no need to process
133
+ }
134
+ // Log discovered custom types
135
+ const customTypeNames = Array.from(customTypes.keys()).join(', ');
136
+ logger.info(`Found custom types in ABI '${ds.options?.abi}': ${customTypeNames}`);
137
+ // Process each handler
138
+ for (const handler of ds.mapping.handlers) {
139
+ processEventHandlerFilters(handler, customTypes);
140
+ processTransactionHandlerFilters(handler, customTypes);
141
+ }
142
+ }
143
+ catch (error) {
144
+ logger.warn(`Failed to resolve custom types for datasource: ${error instanceof Error ? error.message : 'Unknown error'}`);
145
+ }
146
+ }
147
+ }
48
148
  //# sourceMappingURL=project.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/utils/project.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAiBnC,sCAIC;AAED,0CAIC;AAID,wCAKC;AAED,sDAYC;AAcD,kDASC;AAvED,4DAQgC;AAChC,gDAA+C;AAM/C,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,MAAM,aAAa,GAAG,CAAC,SAAS,CAAC,CAAC;AAClC,4DAA4D;AACrD,KAAK,UAAU,cAAc,CAClC,OAAyB,EACzB,MAAM,GAAG,aAAa;IAEtB,OAAO,IAAA,uBAAW,EAAC,OAAO,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,KAAK,CAAC,EAAE,MAAM,CAAC,CAAC,CAAC;AAC5E,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,QAAQ,EAAE,CAAC;gBACvD,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-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport {\n SubqlRuntimeHandler,\n SubqlCustomHandler,\n SubqlHandler,\n EthereumHandlerKind,\n SubqlEthereumHandlerKind,\n isCustomDs,\n isRuntimeDs,\n} from '@subql/common-ethereum';\nimport { retryOnFail } from '@subql/node-core';\nimport {\n EthereumProjectDs,\n SubqueryProject,\n} from '../configure/SubqueryProject';\n\nexport function isBaseHandler(\n handler: SubqlHandler,\n): handler is SubqlRuntimeHandler {\n return Object.values<string>(EthereumHandlerKind).includes(handler.kind);\n}\n\nexport function isCustomHandler(\n handler: SubqlHandler,\n): handler is SubqlCustomHandler {\n return !isBaseHandler(handler);\n}\n\nconst handledErrors = ['timeout'];\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function retryOnFailEth<T>(\n request: () => Promise<T>,\n errors = handledErrors,\n): Promise<T> {\n return retryOnFail(request, (e) => !!errors.find((t) => t === e?.reason));\n}\n\nexport function onlyHasLogDataSources(\n dataSources: EthereumProjectDs[],\n): boolean {\n for (const ds of dataSources) {\n for (const handler of ds.mapping.handlers) {\n if (handler.kind !== SubqlEthereumHandlerKind.EthEvent) {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction dsContainsNonEventHandlers(ds: EthereumProjectDs): boolean {\n if (isRuntimeDs(ds)) {\n return !!ds.mapping.handlers.find(\n (handler) => handler.kind !== EthereumHandlerKind.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 EthereumProjectDs),\n );\n\n return !hasNonEventHandler && !hasNonEventTemplate;\n}\n"]}
1
+ {"version":3,"file":"project.js","sourceRoot":"","sources":["../../src/utils/project.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 { Interface } from '@ethersproject/abi';\nimport {\n SubqlRuntimeHandler,\n SubqlCustomHandler,\n SubqlHandler,\n EthereumHandlerKind,\n SubqlEthereumHandlerKind,\n isCustomDs,\n isRuntimeDs,\n SubqlEthereumDataSource,\n EthereumLogFilter,\n EthereumTransactionFilter,\n getAbiInterface,\n} from '@subql/common-ethereum';\nimport { getLogger, retryOnFail } from '@subql/node-core';\nimport {\n EthereumProjectDs,\n SubqueryProject,\n} from '../configure/SubqueryProject';\nimport {\n extractCustomTypesFromAbi,\n resolveCustomTypesInSignature,\n} from './string';\n\nconst logger = getLogger('project');\n\nexport function isBaseHandler(\n handler: SubqlHandler,\n): handler is SubqlRuntimeHandler {\n return Object.values<string>(EthereumHandlerKind).includes(handler.kind);\n}\n\nexport function isCustomHandler(\n handler: SubqlHandler,\n): handler is SubqlCustomHandler {\n return !isBaseHandler(handler);\n}\n\nconst handledErrors = ['timeout'];\n// eslint-disable-next-line @typescript-eslint/require-await\nexport async function retryOnFailEth<T>(\n request: () => Promise<T>,\n errors = handledErrors,\n): Promise<T> {\n return retryOnFail(request, (e) => !!errors.find((t) => t === e?.reason));\n}\n\nexport function onlyHasLogDataSources(\n dataSources: EthereumProjectDs[],\n): boolean {\n for (const ds of dataSources) {\n for (const handler of ds.mapping.handlers) {\n if (handler.kind !== SubqlEthereumHandlerKind.EthEvent) {\n return false;\n }\n }\n }\n\n return true;\n}\n\nfunction dsContainsNonEventHandlers(ds: EthereumProjectDs): boolean {\n if (isRuntimeDs(ds)) {\n return !!ds.mapping.handlers.find(\n (handler) => handler.kind !== EthereumHandlerKind.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 EthereumProjectDs),\n );\n\n return !hasNonEventHandler && !hasNonEventTemplate;\n}\n\n/**\n * Helper function to load ABI interface from a datasource\n */\nfunction getAbiInterfaceFromDs(\n ds: SubqlEthereumDataSource,\n projectPath: string,\n): Interface | undefined {\n try {\n if (!ds?.options?.abi || !projectPath) {\n return undefined;\n }\n\n return getAbiInterface(projectPath, ds.options.abi);\n } catch (error) {\n logger.warn(\n `Failed to load ABI interface for datasource: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n return undefined;\n }\n}\n\n/**\n * Process event handler topic filters and resolve custom types\n */\nfunction processEventHandlerFilters(\n handler: SubqlHandler,\n customTypes: Map<string, any>,\n): void {\n if (handler.kind !== EthereumHandlerKind.Event) {\n return;\n }\n\n const logFilter = handler.filter as EthereumLogFilter | undefined;\n if (!logFilter?.topics) {\n return;\n }\n\n // Only resolve custom types in the first topic (topic0), which is the event signature\n const topic = logFilter.topics[0];\n if (\n typeof topic === 'string' &&\n topic.trim() !== '' &&\n !topic.startsWith('0x')\n ) {\n const resolved = resolveCustomTypesInSignature(topic, customTypes);\n if (resolved !== topic) {\n logFilter.topics[0] = resolved;\n logger.info(`Resolved topic filter: \"${topic}\" -> \"${resolved}\"`);\n }\n }\n}\n\n/**\n * Process transaction handler function filters and resolve custom types\n */\nfunction processTransactionHandlerFilters(\n handler: SubqlHandler,\n customTypes: Map<string, any>,\n): void {\n if (handler.kind !== EthereumHandlerKind.Call) {\n return;\n }\n\n const txFilter = handler.filter as EthereumTransactionFilter | undefined;\n if (!txFilter?.function) {\n return;\n }\n\n const funcSig = txFilter.function;\n if (typeof funcSig === 'string' && !funcSig.startsWith('0x')) {\n const resolved = resolveCustomTypesInSignature(funcSig, customTypes);\n if (resolved !== funcSig) {\n txFilter.function = resolved;\n logger.info(`Resolved function filter: \"${funcSig}\" -> \"${resolved}\"`);\n }\n }\n}\n\n/**\n * Resolves custom types (enums, structs) in topic filters for all datasources at project load time.\n * Mutates the datasource handlers' topic filters in-place.\n *\n * This ensures that:\n * - Enums are replaced with uint8\n * - Structs are replaced with tuple notation (type1,type2,...)\n * - No runtime ABI resolution is needed\n *\n * @param dataSources - Array of datasources to process (mutated in-place)\n * @param projectPath - The project root path for loading ABI files\n */\nexport function resolveTopicFiltersInProject(\n dataSources: SubqlEthereumDataSource[],\n projectPath: string,\n): void {\n for (const ds of dataSources) {\n try {\n // Load ABI interface\n const abiInterface = getAbiInterfaceFromDs(ds, projectPath);\n if (!abiInterface) {\n continue;\n }\n\n // Extract custom types once per datasource\n const customTypes = extractCustomTypesFromAbi(abiInterface);\n if (customTypes.size === 0) {\n continue; // No custom types, no need to process\n }\n\n // Log discovered custom types\n const customTypeNames = Array.from(customTypes.keys()).join(', ');\n logger.info(\n `Found custom types in ABI '${ds.options?.abi}': ${customTypeNames}`,\n );\n\n // Process each handler\n for (const handler of ds.mapping.handlers) {\n processEventHandlerFilters(handler, customTypes);\n processTransactionHandlerFilters(handler, customTypes);\n }\n } catch (error) {\n logger.warn(\n `Failed to resolve custom types for datasource: ${\n error instanceof Error ? error.message : 'Unknown error'\n }`,\n );\n }\n }\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,99 @@
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
@@ -0,0 +1 @@
1
+ {"version":3,"file":"project.spec.js","sourceRoot":"","sources":["../../src/utils/project.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 { 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,4 +1,12 @@
1
+ import { Interface } from '@ethersproject/abi';
1
2
  export declare function stringNormalizedEq(a: string, b?: string): boolean;
2
3
  export declare function hexStringEq(a: string, b: string): boolean;
4
+ export interface AbiCustomType {
5
+ name: string;
6
+ type: 'enum' | 'struct';
7
+ resolvedType: string;
8
+ }
9
+ export declare function extractCustomTypesFromAbi(abiInterface: Interface): Map<string, AbiCustomType>;
10
+ export declare function resolveCustomTypesInSignature(signature: string, customTypes: Map<string, AbiCustomType>): string;
3
11
  export declare function eventToTopic(input: string): string;
4
12
  export declare function functionToSighash(input: string): string;
@@ -4,6 +4,8 @@
4
4
  Object.defineProperty(exports, "__esModule", { value: true });
5
5
  exports.stringNormalizedEq = stringNormalizedEq;
6
6
  exports.hexStringEq = hexStringEq;
7
+ exports.extractCustomTypesFromAbi = extractCustomTypesFromAbi;
8
+ exports.resolveCustomTypesInSignature = resolveCustomTypesInSignature;
7
9
  exports.eventToTopic = eventToTopic;
8
10
  exports.functionToSighash = functionToSighash;
9
11
  const abi_1 = require("@ethersproject/abi");
@@ -20,6 +22,204 @@ function hexStringEq(a, b) {
20
22
  }
21
23
  const eventTopicsCache = {};
22
24
  const functionSighashCache = {};
25
+ function extractCustomTypesFromAbi(abiInterface) {
26
+ const customTypes = new Map();
27
+ try {
28
+ // Process event fragments
29
+ Object.values(abiInterface.events).forEach((eventFragment) => {
30
+ eventFragment.inputs.forEach((input) => {
31
+ extractCustomTypeFromInput(input, customTypes);
32
+ });
33
+ });
34
+ // Process function fragments
35
+ Object.values(abiInterface.functions).forEach((functionFragment) => {
36
+ functionFragment.inputs.forEach((input) => {
37
+ extractCustomTypeFromInput(input, customTypes);
38
+ });
39
+ });
40
+ }
41
+ catch (error) {
42
+ // Silently handle extraction errors in runtime
43
+ }
44
+ return customTypes;
45
+ }
46
+ function extractCustomTypeFromInput(input, customTypes) {
47
+ // Handle tuple types (structs)
48
+ if (input.type === 'tuple' && input.internalType) {
49
+ // Extract struct name from internal type (e.g., "struct MoreData" -> "MoreData", "contract.MoreData" -> "MoreData")
50
+ const structName = input.internalType
51
+ .replace(/^struct\s+/, '') // Remove "struct " prefix
52
+ .replace(/^.*\./, ''); // Remove contract prefix if present (e.g., "contract.Name" -> "Name")
53
+ if (!customTypes.has(structName) && input.components) {
54
+ const tupleType = `(${input.components
55
+ .map((comp) => resolveBaseType(comp.type))
56
+ .join(',')})`;
57
+ customTypes.set(structName, {
58
+ name: structName,
59
+ type: 'struct',
60
+ resolvedType: tupleType,
61
+ });
62
+ }
63
+ }
64
+ // Handle enum types - look for custom internal types that aren't standard solidity types
65
+ if (input.internalType && input.internalType !== input.type) {
66
+ // Extract enum name from internal type (e.g., "enum DisputeType" -> "DisputeType", "contract.DisputeType" -> "DisputeType")
67
+ const enumName = input.internalType
68
+ .replace(/^enum\s+/, '') // Remove "enum " prefix
69
+ .replace(/^.*\./, ''); // Remove contract prefix if present (e.g., "contract.Name" -> "Name")
70
+ // Check if it's likely an enum (uint8/uint256 type with custom internal type)
71
+ if ((input.type === 'uint8' || input.type === 'uint256') &&
72
+ !enumName.startsWith('struct ') &&
73
+ !isStandardSolidityType(enumName)) {
74
+ if (!customTypes.has(enumName)) {
75
+ customTypes.set(enumName, {
76
+ name: enumName,
77
+ type: 'enum',
78
+ resolvedType: 'uint8',
79
+ });
80
+ }
81
+ }
82
+ }
83
+ // Recursively handle components for nested tuples
84
+ if (input.components) {
85
+ input.components.forEach((comp) => {
86
+ extractCustomTypeFromInput(comp, customTypes);
87
+ });
88
+ }
89
+ }
90
+ function resolveBaseType(type) {
91
+ // Map common type aliases to their canonical forms
92
+ const typeMapping = {
93
+ uint: 'uint256',
94
+ int: 'int256',
95
+ };
96
+ return typeMapping[type] || type;
97
+ }
98
+ function isStandardSolidityType(type) {
99
+ const standardTypes = [
100
+ 'address',
101
+ 'bool',
102
+ 'string',
103
+ 'bytes',
104
+ // uint variants
105
+ 'uint',
106
+ 'uint8',
107
+ 'uint16',
108
+ 'uint24',
109
+ 'uint32',
110
+ 'uint40',
111
+ 'uint48',
112
+ 'uint56',
113
+ 'uint64',
114
+ 'uint72',
115
+ 'uint80',
116
+ 'uint88',
117
+ 'uint96',
118
+ 'uint104',
119
+ 'uint112',
120
+ 'uint120',
121
+ 'uint128',
122
+ 'uint136',
123
+ 'uint144',
124
+ 'uint152',
125
+ 'uint160',
126
+ 'uint168',
127
+ 'uint176',
128
+ 'uint184',
129
+ 'uint192',
130
+ 'uint200',
131
+ 'uint208',
132
+ 'uint216',
133
+ 'uint224',
134
+ 'uint232',
135
+ 'uint240',
136
+ 'uint248',
137
+ 'uint256',
138
+ // int variants
139
+ 'int',
140
+ 'int8',
141
+ 'int16',
142
+ 'int24',
143
+ 'int32',
144
+ 'int40',
145
+ 'int48',
146
+ 'int56',
147
+ 'int64',
148
+ 'int72',
149
+ 'int80',
150
+ 'int88',
151
+ 'int96',
152
+ 'int104',
153
+ 'int112',
154
+ 'int120',
155
+ 'int128',
156
+ 'int136',
157
+ 'int144',
158
+ 'int152',
159
+ 'int160',
160
+ 'int168',
161
+ 'int176',
162
+ 'int184',
163
+ 'int192',
164
+ 'int200',
165
+ 'int208',
166
+ 'int216',
167
+ 'int224',
168
+ 'int232',
169
+ 'int240',
170
+ 'int248',
171
+ 'int256',
172
+ // bytes variants
173
+ 'bytes1',
174
+ 'bytes2',
175
+ 'bytes3',
176
+ 'bytes4',
177
+ 'bytes5',
178
+ 'bytes6',
179
+ 'bytes7',
180
+ 'bytes8',
181
+ 'bytes9',
182
+ 'bytes10',
183
+ 'bytes11',
184
+ 'bytes12',
185
+ 'bytes13',
186
+ 'bytes14',
187
+ 'bytes15',
188
+ 'bytes16',
189
+ 'bytes17',
190
+ 'bytes18',
191
+ 'bytes19',
192
+ 'bytes20',
193
+ 'bytes21',
194
+ 'bytes22',
195
+ 'bytes23',
196
+ 'bytes24',
197
+ 'bytes25',
198
+ 'bytes26',
199
+ 'bytes27',
200
+ 'bytes28',
201
+ 'bytes29',
202
+ 'bytes30',
203
+ 'bytes31',
204
+ 'bytes32',
205
+ ];
206
+ // handle array types
207
+ const baseType = type.replace(/\[\d*\]$/, '');
208
+ return standardTypes.includes(baseType);
209
+ }
210
+ function resolveCustomTypesInSignature(signature, customTypes) {
211
+ let resolvedSignature = signature;
212
+ // Replace custom types in the signature
213
+ customTypes.forEach((customType, typeName) => {
214
+ // Create regex to match the custom type name as a parameter type
215
+ const regex = new RegExp(`\\b${escapeRegex(typeName)}\\b`, 'g');
216
+ resolvedSignature = resolvedSignature.replace(regex, customType.resolvedType);
217
+ });
218
+ return resolvedSignature;
219
+ }
220
+ function escapeRegex(string) {
221
+ return string.replace(/[.*+?^${}()|[\]\\]/g, '\\$&');
222
+ }
23
223
  function eventToTopic(input) {
24
224
  if ((0, bytes_1.isHexString)(input))
25
225
  return input;
@@ -1 +1 @@
1
- {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/utils/string.ts"],"names":[],"mappings":";AAAA,8DAA8D;AAC9D,mCAAmC;;AAMnC,gDAEC;AAED,kCAKC;AAKD,oCAQC;AAED,8CAYC;AAxCD,4CAAqE;AACrE,gDAAgF;AAChF,8CAAyC;AAEzC,SAAgB,kBAAkB,CAAC,CAAS,EAAE,CAAU;IACtD,OAAO,CAAC,CAAC,WAAW,EAAE,KAAK,CAAC,EAAE,WAAW,EAAE,CAAC;AAC9C,CAAC;AAED,SAAgB,WAAW,CAAC,CAAS,EAAE,CAAS;IAC9C,IAAI,CAAC,IAAA,mBAAW,EAAC,CAAC,CAAC,IAAI,CAAC,IAAA,mBAAW,EAAC,CAAC,CAAC,EAAE,CAAC;QACvC,MAAM,IAAI,KAAK,CAAC,4BAA4B,CAAC,CAAC;IAChD,CAAC;IACD,OAAO,kBAAkB,CAAC,IAAA,qBAAa,EAAC,CAAC,CAAC,EAAE,IAAA,qBAAa,EAAC,CAAC,CAAC,CAAC,CAAC;AAChE,CAAC;AAED,MAAM,gBAAgB,GAA2B,EAAE,CAAC;AACpD,MAAM,oBAAoB,GAA2B,EAAE,CAAC;AAExD,SAAgB,YAAY,CAAC,KAAa;IACxC,IAAI,IAAA,mBAAW,EAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAErC,IAAI,CAAC,gBAAgB,CAAC,KAAK,CAAC,EAAE,CAAC;QAC7B,gBAAgB,CAAC,KAAK,CAAC,GAAG,IAAA,SAAE,EAAC,mBAAa,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC;IACzE,CAAC;IAED,OAAO,gBAAgB,CAAC,KAAK,CAAC,CAAC;AACjC,CAAC;AAED,SAAgB,iBAAiB,CAAC,KAAa;IAC7C,IAAI,IAAA,mBAAW,EAAC,KAAK,CAAC;QAAE,OAAO,KAAK,CAAC;IAErC,IAAI,CAAC,oBAAoB,CAAC,KAAK,CAAC,EAAE,CAAC;QACjC,oBAAoB,CAAC,KAAK,CAAC,GAAG,IAAA,oBAAY,EACxC,IAAA,SAAE,EAAC,sBAAgB,CAAC,UAAU,CAAC,KAAK,CAAC,CAAC,MAAM,EAAE,CAAC,EAC/C,CAAC,EACD,CAAC,CACF,CAAC;IACJ,CAAC;IAED,OAAO,oBAAoB,CAAC,KAAK,CAAC,CAAC;AACrC,CAAC","sourcesContent":["// Copyright 2020-2025 SubQuery Pte Ltd authors & contributors\n// SPDX-License-Identifier: GPL-3.0\n\nimport { EventFragment, FunctionFragment } from '@ethersproject/abi';\nimport { isHexString, hexStripZeros, hexDataSlice } from '@ethersproject/bytes';\nimport { id } from '@ethersproject/hash';\n\nexport function stringNormalizedEq(a: string, b?: string): boolean {\n return a.toLowerCase() === b?.toLowerCase();\n}\n\nexport function hexStringEq(a: string, b: string): boolean {\n if (!isHexString(a) || !isHexString(b)) {\n throw new Error('Inputs are not hex strings');\n }\n return stringNormalizedEq(hexStripZeros(a), hexStripZeros(b));\n}\n\nconst eventTopicsCache: Record<string, string> = {};\nconst functionSighashCache: Record<string, string> = {};\n\nexport function eventToTopic(input: string): string {\n if (isHexString(input)) return input;\n\n if (!eventTopicsCache[input]) {\n eventTopicsCache[input] = id(EventFragment.fromString(input).format());\n }\n\n return eventTopicsCache[input];\n}\n\nexport function functionToSighash(input: string): string {\n if (isHexString(input)) return input;\n\n if (!functionSighashCache[input]) {\n functionSighashCache[input] = hexDataSlice(\n id(FunctionFragment.fromString(input).format()),\n 0,\n 4,\n );\n }\n\n return functionSighashCache[input];\n}\n"]}
1
+ {"version":3,"file":"string.js","sourceRoot":"","sources":["../../src/utils/string.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 { EventFragment, FunctionFragment, Interface } from '@ethersproject/abi';\nimport { isHexString, hexStripZeros, hexDataSlice } from '@ethersproject/bytes';\nimport { id } from '@ethersproject/hash';\n\nexport function stringNormalizedEq(a: string, b?: string): boolean {\n return a.toLowerCase() === b?.toLowerCase();\n}\n\nexport function hexStringEq(a: string, b: string): boolean {\n if (!isHexString(a) || !isHexString(b)) {\n throw new Error('Inputs are not hex strings');\n }\n return stringNormalizedEq(hexStripZeros(a), hexStripZeros(b));\n}\n\nconst eventTopicsCache: Record<string, string> = {};\nconst functionSighashCache: Record<string, string> = {};\n\nexport interface AbiCustomType {\n name: string;\n type: 'enum' | 'struct';\n resolvedType: string;\n}\n\nexport function extractCustomTypesFromAbi(\n abiInterface: Interface,\n): Map<string, AbiCustomType> {\n const customTypes = new Map<string, AbiCustomType>();\n\n try {\n // Process event fragments\n Object.values(abiInterface.events).forEach(\n (eventFragment: EventFragment) => {\n eventFragment.inputs.forEach((input) => {\n extractCustomTypeFromInput(input, customTypes);\n });\n },\n );\n\n // Process function fragments\n Object.values(abiInterface.functions).forEach(\n (functionFragment: FunctionFragment) => {\n functionFragment.inputs.forEach((input) => {\n extractCustomTypeFromInput(input, customTypes);\n });\n },\n );\n } catch (error) {\n // Silently handle extraction errors in runtime\n }\n\n return customTypes;\n}\n\nfunction extractCustomTypeFromInput(\n input: { type: string; internalType?: string; components?: any[] },\n customTypes: Map<string, AbiCustomType>,\n): void {\n // Handle tuple types (structs)\n if (input.type === 'tuple' && input.internalType) {\n // Extract struct name from internal type (e.g., \"struct MoreData\" -> \"MoreData\", \"contract.MoreData\" -> \"MoreData\")\n const structName = input.internalType\n .replace(/^struct\\s+/, '') // Remove \"struct \" prefix\n .replace(/^.*\\./, ''); // Remove contract prefix if present (e.g., \"contract.Name\" -> \"Name\")\n\n if (!customTypes.has(structName) && input.components) {\n const tupleType = `(${input.components\n .map((comp: any) => resolveBaseType(comp.type))\n .join(',')})`;\n customTypes.set(structName, {\n name: structName,\n type: 'struct',\n resolvedType: tupleType,\n });\n }\n }\n\n // Handle enum types - look for custom internal types that aren't standard solidity types\n if (input.internalType && input.internalType !== input.type) {\n // Extract enum name from internal type (e.g., \"enum DisputeType\" -> \"DisputeType\", \"contract.DisputeType\" -> \"DisputeType\")\n const enumName = input.internalType\n .replace(/^enum\\s+/, '') // Remove \"enum \" prefix\n .replace(/^.*\\./, ''); // Remove contract prefix if present (e.g., \"contract.Name\" -> \"Name\")\n\n // Check if it's likely an enum (uint8/uint256 type with custom internal type)\n if (\n (input.type === 'uint8' || input.type === 'uint256') &&\n !enumName.startsWith('struct ') &&\n !isStandardSolidityType(enumName)\n ) {\n if (!customTypes.has(enumName)) {\n customTypes.set(enumName, {\n name: enumName,\n type: 'enum',\n resolvedType: 'uint8',\n });\n }\n }\n }\n\n // Recursively handle components for nested tuples\n if (input.components) {\n input.components.forEach((comp: any) => {\n extractCustomTypeFromInput(comp, customTypes);\n });\n }\n}\n\nfunction resolveBaseType(type: string): string {\n // Map common type aliases to their canonical forms\n const typeMapping: Record<string, string> = {\n uint: 'uint256',\n int: 'int256',\n };\n return typeMapping[type] || type;\n}\n\nfunction isStandardSolidityType(type: string): boolean {\n const standardTypes = [\n 'address',\n 'bool',\n 'string',\n 'bytes',\n // uint variants\n 'uint',\n 'uint8',\n 'uint16',\n 'uint24',\n 'uint32',\n 'uint40',\n 'uint48',\n 'uint56',\n 'uint64',\n 'uint72',\n 'uint80',\n 'uint88',\n 'uint96',\n 'uint104',\n 'uint112',\n 'uint120',\n 'uint128',\n 'uint136',\n 'uint144',\n 'uint152',\n 'uint160',\n 'uint168',\n 'uint176',\n 'uint184',\n 'uint192',\n 'uint200',\n 'uint208',\n 'uint216',\n 'uint224',\n 'uint232',\n 'uint240',\n 'uint248',\n 'uint256',\n // int variants\n 'int',\n 'int8',\n 'int16',\n 'int24',\n 'int32',\n 'int40',\n 'int48',\n 'int56',\n 'int64',\n 'int72',\n 'int80',\n 'int88',\n 'int96',\n 'int104',\n 'int112',\n 'int120',\n 'int128',\n 'int136',\n 'int144',\n 'int152',\n 'int160',\n 'int168',\n 'int176',\n 'int184',\n 'int192',\n 'int200',\n 'int208',\n 'int216',\n 'int224',\n 'int232',\n 'int240',\n 'int248',\n 'int256',\n // bytes variants\n 'bytes1',\n 'bytes2',\n 'bytes3',\n 'bytes4',\n 'bytes5',\n 'bytes6',\n 'bytes7',\n 'bytes8',\n 'bytes9',\n 'bytes10',\n 'bytes11',\n 'bytes12',\n 'bytes13',\n 'bytes14',\n 'bytes15',\n 'bytes16',\n 'bytes17',\n 'bytes18',\n 'bytes19',\n 'bytes20',\n 'bytes21',\n 'bytes22',\n 'bytes23',\n 'bytes24',\n 'bytes25',\n 'bytes26',\n 'bytes27',\n 'bytes28',\n 'bytes29',\n 'bytes30',\n 'bytes31',\n 'bytes32',\n ];\n\n // handle array types\n const baseType = type.replace(/\\[\\d*\\]$/, '');\n return standardTypes.includes(baseType);\n}\n\nexport function resolveCustomTypesInSignature(\n signature: string,\n customTypes: Map<string, AbiCustomType>,\n): string {\n let resolvedSignature = signature;\n\n // Replace custom types in the signature\n customTypes.forEach((customType, typeName) => {\n // Create regex to match the custom type name as a parameter type\n const regex = new RegExp(`\\\\b${escapeRegex(typeName)}\\\\b`, 'g');\n resolvedSignature = resolvedSignature.replace(\n regex,\n customType.resolvedType,\n );\n });\n\n return resolvedSignature;\n}\n\nfunction escapeRegex(string: string): string {\n return string.replace(/[.*+?^${}()|[\\]\\\\]/g, '\\\\$&');\n}\n\nexport function eventToTopic(input: string): string {\n if (isHexString(input)) return input;\n\n if (!eventTopicsCache[input]) {\n eventTopicsCache[input] = id(EventFragment.fromString(input).format());\n }\n\n return eventTopicsCache[input];\n}\n\nexport function functionToSighash(input: string): string {\n if (isHexString(input)) return input;\n\n if (!functionSighashCache[input]) {\n functionSighashCache[input] = hexDataSlice(\n id(FunctionFragment.fromString(input).format()),\n 0,\n 4,\n );\n }\n\n return functionSighashCache[input];\n}\n"]}
@@ -0,0 +1 @@
1
+ export {};