Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Sync with main SDK for project upgrades support #148

Merged
merged 16 commits into from
Sep 29, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .eslintrc.js
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ module.exports = {
'eslint:recommended',
'plugin:@typescript-eslint/recommended',
'plugin:@typescript-eslint/recommended-requiring-type-checking',
'plugin:jest/recommended',
],
rules: {
// rules turned off in upstream project (also required when recommended-requiring-type-checking is extended)
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
"eslint-config-prettier": "^8.3.0",
"eslint-plugin-header": "^3.1.1",
"eslint-plugin-import": "^2.25.4",
"eslint-plugin-jest": "^27.4.0",
"eslint-plugin-prettier": "^4.0.0",
"eslint-plugin-sort-destructure-keys": "^1.4.0",
"husky": "^7.0.4",
Expand Down
4 changes: 4 additions & 0 deletions packages/common-ethereum/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
### Fixed
- Missing imports for multi abi `codegen` (#169)

### Added
- Parent field to manifest for project upgrades (#148)
- Light Log types to codegen (#170)

## [2.3.0] - 2023-09-12
### Changed
- Migrated abi-codegen from `@subql/cli` to `common-ethereum` (#158)
Expand Down
2 changes: 1 addition & 1 deletion packages/common-ethereum/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@
"main": "dist/index.js",
"license": "GPL-3.0",
"dependencies": {
"@subql/common": "2.6.0",
"@subql/common": "3.0.1",
"@subql/types-ethereum": "workspace:*",
"@typechain/ethers-v5": "^11.1.1",
"js-yaml": "^4.1.0",
Expand Down
21 changes: 11 additions & 10 deletions packages/common-ethereum/src/codegen/codegen-controller.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import {EthereumDatasourceKind, EthereumHandlerKind} from '@subql/types-ethereum
import ejs from 'ejs';
import {upperFirst} from 'lodash';
import rimraf from 'rimraf';
import {abiInterface, getAbiNames, joinInputAbiName, prepareAbiJob, prepareSortedAssets} from './codegen-controller';
import {AbiInterface, getAbiNames, joinInputAbiName, prepareAbiJob, prepareSortedAssets} from './codegen-controller';

describe('Codegen spec', () => {
const PROJECT_PATH = path.join(__dirname, '../../test/abiTest');
Expand All @@ -23,7 +23,7 @@ describe('Codegen spec', () => {
type: 'STRING',
},
],
} as abiInterface;
} as AbiInterface;
expect(joinInputAbiName(mockAbiInterface)).toMatch('initialize_string_');
});
it('should replace [] in input abi name', () => {
Expand All @@ -48,7 +48,7 @@ describe('Codegen spec', () => {
type: 'address[]',
},
],
} as abiInterface;
} as AbiInterface;
expect(joinInputAbiName(mockAbiInterface)).toMatch('initialize_string_string_string_address_arr_');
});

Expand All @@ -58,9 +58,7 @@ describe('Codegen spec', () => {
};

expect(() =>
prepareAbiJob(artifactAssetObj, PROJECT_PATH, (filePath) =>
require(path.join(PROJECT_PATH, './abis/bad-erc20.json'))
)
prepareAbiJob(artifactAssetObj, PROJECT_PATH, () => require(path.join(PROJECT_PATH, './abis/bad-erc20.json')))
).toThrow('Provided ABI is not a valid ABI or Artifact');
});
it('Empty abi json, should throw', () => {
Expand All @@ -70,7 +68,7 @@ describe('Codegen spec', () => {
artifact: './artifact.json',
};

expect(() => prepareAbiJob(artifactAssetObj, projectPath, (filePath) => [])).toThrow(
expect(() => prepareAbiJob(artifactAssetObj, projectPath, () => [])).toThrow(
'Invalid abi is provided at asset: artifact'
);
});
Expand Down Expand Up @@ -110,8 +108,8 @@ describe('Codegen spec', () => {
const a = path.join(projectPath, './abis/erc20.json');
const b = path.join(projectPath, './abis/Erc20.sol/Erc20.json');

const abisRendered = prepareAbiJob(abisAssetObj, projectPath, (filePath) => require(a));
const artifactRendered = prepareAbiJob(artifactAssetObj, projectPath, (filePath) => require(b));
const abisRendered = prepareAbiJob(abisAssetObj, projectPath, () => require(a));
const artifactRendered = prepareAbiJob(artifactAssetObj, projectPath, () => require(b));

// exclude name field
artifactRendered.map((e) => {
Expand Down Expand Up @@ -141,14 +139,17 @@ describe('Codegen spec', () => {
'// SPDX-License-Identifier: Apache-2.0\n' +
'\n' +
'// Auto-generated , DO NOT EDIT\n' +
'import {EthereumLog, EthereumTransaction} from "@subql/types-ethereum";\n' +
'import {EthereumLog, EthereumTransaction, LightEthereumLog} from "@subql/types-ethereum";\n' +
'\n' +
"import {ApprovalEvent, Erc20} from '../contracts/Erc20'\n" +
'\n' +
'\n' +
'export type ApprovalLog = EthereumLog<ApprovalEvent["args"]>\n' +
'\n' +
'\n' +
'export type LightApprovalLog = LightEthereumLog<ApprovalEvent["args"]>\n' +
'\n' +
'\n' +
"export type Transaction = EthereumTransaction<Parameters<Erc20['functions']['approve']>>";
const output = await fs.promises.readFile(path.join(PROJECT_PATH, 'test.ts'));
expect(output.toString()).toMatch(expectedCodegen);
Expand Down
20 changes: 10 additions & 10 deletions packages/common-ethereum/src/codegen/codegen-controller.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@

import fs from 'fs';
import path from 'path';
import {isCustomDs, isRuntimeDs} from '@subql/common-ethereum';
import {SubqlRuntimeDatasource} from '@subql/types-ethereum';
import {Data} from 'ejs';
import {runTypeChain, glob, parseContractPath} from 'typechain';
import {isCustomDs, isRuntimeDs} from '../project';
import {CUSTOM_EVM_HANDLERS} from './constants';
import {loadReadAbi} from './utils';

Expand All @@ -18,12 +18,12 @@ const CONTRACTS_DIR = 'src/types/contracts'; //generated
const FACTORIES_DIR = path.join(CONTRACTS_DIR, 'factories'); // generated
const TYPECHAIN_TARGET = 'ethers-v5';

export interface abiRenderProps {
export interface AbiRenderProps {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

line 6, import {isCustomDs, isRuntimeDs} from '@subql/common-ethereum'; been self import

name: string;
events: string[];
functions: {typeName: string; functionName: string}[];
}
export interface abiInterface {
export interface AbiInterface {
name: string;
type: 'event' | 'function';
inputs: {
Expand All @@ -37,7 +37,7 @@ function validateCustomDsDs(d: {kind: string}): boolean {
return CUSTOM_EVM_HANDLERS.includes(d.kind);
}

export function joinInputAbiName(abiObject: abiInterface): string {
export function joinInputAbiName(abiObject: AbiInterface): string {
// example: "TextChanged_bytes32_string_string_string_Event", Event name/Function type name will be joined in ejs
const inputToSnake = abiObject.inputs.map((obj) => obj.type.replace(/\[\]/g, '_arr').toLowerCase()).join('_');
return `${abiObject.name}_${inputToSnake}_`;
Expand Down Expand Up @@ -70,17 +70,17 @@ export function prepareSortedAssets(
export function prepareAbiJob(
sortedAssets: Record<string, string>,
projectPath: string,
loadReadAbi: (filePath: string) => abiInterface[] | {abi: abiInterface[]}
): abiRenderProps[] {
const renderInterfaceJobs: abiRenderProps[] = [];
loadReadAbi: (filePath: string) => AbiInterface[] | {abi: AbiInterface[]}
): AbiRenderProps[] {
const renderInterfaceJobs: AbiRenderProps[] = [];
Object.entries(sortedAssets).forEach(([key, value]) => {
const renderProps: abiRenderProps = {name: key, events: [], functions: []};
const renderProps: AbiRenderProps = {name: key, events: [], functions: []};
const readAbi = loadReadAbi(path.join(projectPath, value));
// We need to use for loop instead of map, due to events/function name could be duplicate,
// because they have different input, and following ether typegen rules, name also changed
// we need to find duplicates, and update its name rather than just unify them.

let abiArray: abiInterface[] = [];
let abiArray: AbiInterface[] = [];

if (!Array.isArray(readAbi)) {
if (!readAbi.abi) {
Expand All @@ -92,7 +92,7 @@ export function prepareAbiJob(
}

if (!abiArray.length) {
throw new Error(`Invalid abi is provided at asset: ${key}, ${value}, ${(readAbi as any).length}`);
throw new Error(`Invalid abi is provided at asset: ${key}, ${value}, ${abiArray.length}`);
}

const duplicateEventNames = abiArray
Expand Down
6 changes: 3 additions & 3 deletions packages/common-ethereum/src/codegen/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@

import {loadFromJsonOrYaml} from '@subql/common';
import {parseContractPath} from 'typechain';
import {abiInterface} from './codegen-controller';
import {AbiInterface} from './codegen-controller';

// Re-export for generate command
export {parseContractPath};

export function loadReadAbi(filePath: string): abiInterface[] | {abi: abiInterface[]} {
return loadFromJsonOrYaml(filePath) as abiInterface[] | {abi: abiInterface[]};
export function loadReadAbi(filePath: string): AbiInterface[] | {abi: AbiInterface[]} {
return loadFromJsonOrYaml(filePath) as AbiInterface[] | {abi: AbiInterface[]};
}
23 changes: 0 additions & 23 deletions packages/common-ethereum/src/project/load.ts
Original file line number Diff line number Diff line change
@@ -1,33 +1,10 @@
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import fs from 'fs';
import path from 'path';
import {loadFromJsonOrYaml} from '@subql/common';
import {EthereumProjectManifestVersioned, VersionedProjectManifest} from './versioned';

export function parseEthereumProjectManifest(raw: unknown): EthereumProjectManifestVersioned {
const projectManifest = new EthereumProjectManifestVersioned(raw as VersionedProjectManifest);
projectManifest.validate();
return projectManifest;
}

export function loadEthereumProjectManifest(file: string): EthereumProjectManifestVersioned {
let manifestPath = file;
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
const yamlFilePath = path.join(file, 'project.yaml');
const jsonFilePath = path.join(file, 'project.json');
if (fs.existsSync(yamlFilePath)) {
manifestPath = yamlFilePath;
} else if (fs.existsSync(jsonFilePath)) {
manifestPath = jsonFilePath;
} else {
throw new Error(`Could not find project manifest under dir ${file}`);
}
}

const doc = loadFromJsonOrYaml(manifestPath);
const projectManifest = new EthereumProjectManifestVersioned(doc as VersionedProjectManifest);
projectManifest.validate();
return projectManifest;
}
2 changes: 1 addition & 1 deletion packages/common-ethereum/src/project/models.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import {FileReference} from '@subql/types-core';
import {
EthereumHandlerKind,
EthereumDatasourceKind,
Expand All @@ -11,7 +12,6 @@
SubqlRuntimeHandler,
SubqlRuntimeDatasource,
SubqlCustomDatasource,
FileReference,
CustomDataSourceAsset,
EthereumBlockFilter,
SubqlBlockHandler,
Expand Down Expand Up @@ -62,8 +62,8 @@
function?: string;
}

export function forbidNonWhitelisted(keys: any, validationOptions?: ValidationOptions) {

Check warning on line 65 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

Argument 'keys' should be typed with a non-any type

Check warning on line 65 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

Unexpected any. Specify a different type
return function (object: object, propertyName: string) {

Check warning on line 66 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

Missing return type on function
registerDecorator({
name: 'forbidNonWhitelisted',
target: object.constructor,
Expand All @@ -71,7 +71,7 @@
constraints: [],
options: validationOptions,
validator: {
validate(value: any, args: ValidationArguments) {

Check warning on line 74 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

Unexpected any. Specify a different type

Check warning on line 74 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

'args' is defined but never used
const isValid = !Object.keys(value).some((key) => !(key in keys));
if (!isValid) {
throw new Error(
Expand Down Expand Up @@ -150,7 +150,7 @@
case SubqlEthereumHandlerKind.EthBlock:
return plainToClass(BlockHandler, handler);
default:
throw new Error(`handler ${(handler as any).kind} not supported`);

Check warning on line 153 in packages/common-ethereum/src/project/models.ts

View workflow job for this annotation

GitHub Actions / code-style

Unexpected any. Specify a different type
}
});
})
Expand Down
36 changes: 29 additions & 7 deletions packages/common-ethereum/src/project/project.spec.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,43 @@
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import fs from 'fs';
import path from 'path';
import {RunnerQueryBaseModel} from '@subql/common';
import {loadFromJsonOrYaml, RunnerQueryBaseModel} from '@subql/common';
import {validateSync} from 'class-validator';
import {DeploymentV1_0_0, EthereumRunnerNodeImpl, EthereumRunnerSpecsImpl} from '../project/versioned/v1_0_0';
import {loadEthereumProjectManifest} from './load';
import {EthereumProjectManifestVersioned, VersionedProjectManifest} from './versioned';

const projectsDir = path.join(__dirname, '../../test');

function loadEthereumProjectManifest(file: string): EthereumProjectManifestVersioned {
let manifestPath = file;
if (fs.existsSync(file) && fs.lstatSync(file).isDirectory()) {
const yamlFilePath = path.join(file, 'project.yaml');
const jsonFilePath = path.join(file, 'project.json');
if (fs.existsSync(yamlFilePath)) {
manifestPath = yamlFilePath;
} else if (fs.existsSync(jsonFilePath)) {
manifestPath = jsonFilePath;
} else {
throw new Error(`Could not find project manifest under dir ${file}`);
}
}

const doc = loadFromJsonOrYaml(manifestPath);
const projectManifest = new EthereumProjectManifestVersioned(doc as VersionedProjectManifest);
projectManifest.validate();
return projectManifest;
}

describe('test eth project.yaml', () => {
it('could get eth project template name from its deployment ', () => {
it('could get eth project template name from its deployment', () => {

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

imported method loadEthereumProjectManifest can be moved within test file, it only been used here

const manifest = loadEthereumProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
const deployment = manifest.toDeployment();
expect(deployment).toContain('name: Pool');
});

it('could get options in template from its deployment ', () => {
it('could get options in template from its deployment', () => {
const manifest = loadEthereumProjectManifest(path.join(projectsDir, 'project_1.0.0.yaml'));
const deployment = manifest.toDeployment();
expect(deployment).toContain('abi: Pool');
Expand Down Expand Up @@ -53,7 +74,7 @@
console.log(deployment.network.chainId);
});

it.skip('can get chainId for deployment', () => {

Check warning on line 77 in packages/common-ethereum/src/project/project.spec.ts

View workflow job for this annotation

GitHub Actions / code-style

Disabled test
const deployment = loadEthereumProjectManifest(path.join(projectsDir, 'project_1.0.0_chainId.yaml')).asV1_0_0
.deployment;
expect(deployment.network.chainId).toBe('moonbeamChainId');
Expand All @@ -66,16 +87,17 @@
deployment.specVersion = '1.0.0';
deployment.runner = new EthereumRunnerSpecsImpl();

nodeImp.name = '@subql/node';
nodeImp.version = '0.29.1';
nodeImp.name = '@subql/node-ethereum';
nodeImp.version = '*';
deployment.runner.node = nodeImp;

queryImp.name = '@subql/query';
queryImp.version = '0.213.1';

deployment.runner.query = queryImp;

validateSync(deployment.runner, {whitelist: true, forbidNonWhitelisted: true});
const errors = validateSync(deployment.runner, {whitelist: true, forbidNonWhitelisted: true});
expect(errors.length).toBe(0);
});

it('can validate a v1.0.0 project.yaml with unsupported runner node', () => {
Expand All @@ -83,7 +105,7 @@
});

//TODO, pre-release should be excluded
it.skip('can throw error with unsupported runner version', () => {

Check warning on line 108 in packages/common-ethereum/src/project/project.spec.ts

View workflow job for this annotation

GitHub Actions / code-style

Disabled test
expect(() =>
loadEthereumProjectManifest(path.join(projectsDir, 'project_1.0.0_bad_runner_version.yaml'))
).toThrow();
Expand Down
7 changes: 1 addition & 6 deletions packages/common-ethereum/src/project/types.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
// Copyright 2020-2023 SubQuery Pte Ltd authors & contributors
// SPDX-License-Identifier: GPL-3.0

import {IProjectManifest, ProjectNetworkConfig} from '@subql/common';
import {IProjectManifest} from '@subql/types-core';
import {SubqlDatasource} from '@subql/types-ethereum';

// All of these used to be redefined in this file, re-exporting for simplicity
Expand All @@ -24,11 +24,6 @@ export {

export type IEthereumProjectManifest = IProjectManifest<SubqlDatasource>;

export interface EthereumProjectNetworkConfig extends ProjectNetworkConfig {
genesisHash?: string;
chainId?: string;
}

export enum SubqlEthereumHandlerKind {
FlareBlock = 'flare/BlockHandler',
FlareCall = 'flare/TransactionHandler',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,7 @@ export class EthereumProjectManifestVersioned implements IEthereumProjectManifes
}

toDeployment(): string | undefined {
return this._impl.toDeployment();
return this._impl.deployment.toYaml();
}

validate(): void {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,4 +2,3 @@
// SPDX-License-Identifier: GPL-3.0

export * from './model';
export * from './types';
Loading
Loading