diff --git a/packages/common-ethereum/CHANGELOG.md b/packages/common-ethereum/CHANGELOG.md index 354c1c8642..5227d039d3 100644 --- a/packages/common-ethereum/CHANGELOG.md +++ b/packages/common-ethereum/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Fixed +- Abi validation not working with `!null` filter (#341) ## [4.5.1] - 2024-08-16 ### Changed diff --git a/packages/common-ethereum/src/codegen/codegen-controller.spec.ts b/packages/common-ethereum/src/codegen/codegen-controller.spec.ts index 0eae5e984a..b7b3e9b10b 100644 --- a/packages/common-ethereum/src/codegen/codegen-controller.spec.ts +++ b/packages/common-ethereum/src/codegen/codegen-controller.spec.ts @@ -8,6 +8,7 @@ import {EthereumDatasourceKind, EthereumHandlerKind, SubqlRuntimeDatasource} fro import ejs from 'ejs'; import {upperFirst} from 'lodash'; import rimraf from 'rimraf'; +import {NOT_NULL_FILTER} from '../project/utils'; import { AbiInterface, generateAbis, @@ -352,6 +353,47 @@ describe('Codegen spec', () => { ).rejects.toThrow(/Topic: "NotExist\(address a\)" not found in erc20 contract interface/); }); + it('validates abi with !null filter', async () => { + const ds: SubqlRuntimeDatasource = { + kind: EthereumDatasourceKind.Runtime, + startBlock: 1, + options: { + abi: 'erc20', + address: '', + }, + assets: new Map([['erc20', {file: './abis/erc20.json'}]]), + mapping: { + file: '', + handlers: [ + { + handler: 'handleTransaction', + kind: EthereumHandlerKind.Event, + filter: { + topics: ['Transfer(address a,address b,uint256 c)', undefined, undefined, NOT_NULL_FILTER], + }, + }, + { + handler: 'handleTransaction', + kind: EthereumHandlerKind.Event, + filter: { + topics: ['Transfer(address a,address b,uint256 c)', undefined, undefined, null], + }, + }, + ], + }, + }; + + await expect( + generateAbis( + [ds], + PROJECT_PATH, + (p) => Promise.resolve(), + (v) => v as any, + () => Promise.resolve() + ) + ).resolves.not.toThrow(); + }); + it('doesnt validate if datasource has no abi option set', async () => { const ds: SubqlRuntimeDatasource = { kind: EthereumDatasourceKind.Runtime, diff --git a/packages/common-ethereum/src/codegen/codegen-controller.ts b/packages/common-ethereum/src/codegen/codegen-controller.ts index d19c6991ee..318787d2d4 100644 --- a/packages/common-ethereum/src/codegen/codegen-controller.ts +++ b/packages/common-ethereum/src/codegen/codegen-controller.ts @@ -9,7 +9,7 @@ import {FileReference} from '@subql/types-core'; import {EthereumHandlerKind, SubqlRuntimeDatasource} from '@subql/types-ethereum'; import {Data} from 'ejs'; import {runTypeChain, glob, parseContractPath} from 'typechain'; -import {isCustomDs, isRuntimeDs} from '../project'; +import {isCustomDs, isRuntimeDs, NOT_NULL_FILTER} from '../project'; import {CUSTOM_EVM_HANDLERS} from './constants'; import {loadReadAbi} from './utils'; @@ -81,7 +81,7 @@ function validateAbi(datasources: SubqlRuntimeDatasource[], projectPath: string) if (!mappingHandler.filter.topics || !mappingHandler.filter.topics.length) continue; const notMatch = mappingHandler.filter.topics.find( - (topic) => topic && !abiEvents.includes(EventFragment.fromString(topic).format()) + (topic) => topic && topic !== NOT_NULL_FILTER && !abiEvents.includes(EventFragment.fromString(topic).format()) ); if (notMatch) topicIssues.push(notMatch); diff --git a/packages/common-ethereum/src/project/utils.ts b/packages/common-ethereum/src/project/utils.ts index 5b1a6ba80b..4b0e2170f0 100644 --- a/packages/common-ethereum/src/project/utils.ts +++ b/packages/common-ethereum/src/project/utils.ts @@ -21,6 +21,8 @@ import {Interface} from 'ethers/lib/utils'; // Todo, this aligns with cli/src/generate-controller, but we should move this to common in next version export const DEFAULT_ABI_DIR = '/abis'; +export const NOT_NULL_FILTER = '!null'; + type DefaultFilter = Record; export function isBlockHandlerProcessor( diff --git a/packages/node/CHANGELOG.md b/packages/node/CHANGELOG.md index 00da28d166..61b1461ae4 100644 --- a/packages/node/CHANGELOG.md +++ b/packages/node/CHANGELOG.md @@ -5,6 +5,8 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/), and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html). ## [Unreleased] +### Changed +- Use constant for filter `!null` value (#341) ## [5.1.1] - 2024-08-16 ### Fixed diff --git a/packages/node/src/ethereum/api.ethereum.test.ts b/packages/node/src/ethereum/api.ethereum.test.ts index 403fe44594..31ace2099a 100644 --- a/packages/node/src/ethereum/api.ethereum.test.ts +++ b/packages/node/src/ethereum/api.ethereum.test.ts @@ -3,6 +3,7 @@ import path from 'path'; import { EventEmitter2 } from '@nestjs/event-emitter'; +import { NOT_NULL_FILTER } from '@subql/common-ethereum'; import { EthereumBlock, EthereumDatasourceKind, @@ -159,7 +160,7 @@ describe('Api.ethereum', () => { '0xddf252ad1be2c89b69c2b068fc378daa952ba7f163c4a11628f55a4df523b3ef', undefined, undefined, - '!null', + NOT_NULL_FILTER, ], }; diff --git a/packages/node/src/ethereum/block.ethereum.ts b/packages/node/src/ethereum/block.ethereum.ts index 233442a7f2..5ba9d687b3 100644 --- a/packages/node/src/ethereum/block.ethereum.ts +++ b/packages/node/src/ethereum/block.ethereum.ts @@ -1,7 +1,8 @@ // Copyright 2020-2024 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: GPL-3.0 -import { filterBlockTimestamp, getLogger } from '@subql/node-core'; +import { NOT_NULL_FILTER } from '@subql/common-ethereum'; +import { filterBlockTimestamp } from '@subql/node-core'; import { EthereumBlock, EthereumTransactionFilter, @@ -20,8 +21,6 @@ import { stringNormalizedEq, } from '../utils/string'; -const logger = getLogger('block.ethereum'); - export function filterBlocksProcessor( block: EthereumBlock, filter: EthereumBlockFilter, @@ -105,7 +104,7 @@ export function filterLogsProcessor( return false; } - if (topic === '!null') { + if (topic === NOT_NULL_FILTER) { return true; } diff --git a/packages/node/src/indexer/dictionary/v1/ethDictionaryV1.ts b/packages/node/src/indexer/dictionary/v1/ethDictionaryV1.ts index f2703b98fb..fa4284048e 100644 --- a/packages/node/src/indexer/dictionary/v1/ethDictionaryV1.ts +++ b/packages/node/src/indexer/dictionary/v1/ethDictionaryV1.ts @@ -1,6 +1,7 @@ // Copyright 2020-2024 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: GPL-3.0 +import { NOT_NULL_FILTER } from '@subql/common-ethereum'; import { NodeConfig, DictionaryV1, getLogger } from '@subql/node-core'; import { DictionaryQueryCondition, @@ -81,7 +82,7 @@ function eventFilterToQueryEntry( } const field = `topics${i}`; - if (topic === '!null') { + if (topic === NOT_NULL_FILTER) { conditions.push({ field, value: false as any, // TODO update types to allow boolean diff --git a/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.spec.ts b/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.spec.ts index 2bde078c11..71766b2d48 100644 --- a/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.spec.ts +++ b/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.spec.ts @@ -1,6 +1,7 @@ // Copyright 2020-2024 SubQuery Pte Ltd authors & contributors // SPDX-License-Identifier: GPL-3.0 +import { NOT_NULL_FILTER } from '@subql/common-ethereum'; import { BlockHeightMap, DictionaryResponse, @@ -235,7 +236,7 @@ describe('eth dictionary v2', () => { 'Transfer(address, address, uint256)', undefined, undefined, - '!null', + NOT_NULL_FILTER, ], }, }, @@ -339,7 +340,7 @@ describe('buildDictionaryV2QueryEntry', () => { 'Transfer(address, address, uint256)', undefined, undefined, - '!null', + NOT_NULL_FILTER, ], }, }, diff --git a/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.ts b/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.ts index ba2eb9187b..e769a7d6ea 100644 --- a/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.ts +++ b/packages/node/src/indexer/dictionary/v2/ethDictionaryV2.ts @@ -2,6 +2,7 @@ // SPDX-License-Identifier: GPL-3.0 import { BigNumber } from '@ethersproject/bignumber'; +import { NOT_NULL_FILTER } from '@subql/common-ethereum'; import { NodeConfig, DictionaryV2, @@ -136,7 +137,7 @@ function eventFilterToDictionaryCondition( if (!logConditions[field]) { logConditions[field] = []; } - if (topic === '!null') { + if (topic === NOT_NULL_FILTER) { logConditions[field] = []; // TODO, check if !null } else { logConditions[field].push(eventToTopic(topic));