From 1780bf4a6adcf906163fd089915107c160d43d1d Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Wed, 9 Aug 2023 14:04:32 -0500 Subject: [PATCH 1/7] create core package --- packages/core/package.json | 19 + packages/core/src/manifest.spec.ts | 23 + packages/core/src/manifest.ts | 175 +++++++ packages/core/stubs/block-handler.yaml | 22 + packages/core/stubs/call-handler.yaml | 22 + pnpm-lock.yaml | 654 ++++++++++++++++++++++++- 6 files changed, 893 insertions(+), 22 deletions(-) create mode 100644 packages/core/package.json create mode 100644 packages/core/src/manifest.spec.ts create mode 100644 packages/core/src/manifest.ts create mode 100644 packages/core/stubs/block-handler.yaml create mode 100644 packages/core/stubs/call-handler.yaml diff --git a/packages/core/package.json b/packages/core/package.json new file mode 100644 index 000000000..ed87d3508 --- /dev/null +++ b/packages/core/package.json @@ -0,0 +1,19 @@ +{ + "name": "@graphprotocol/graph-cli-core", + "version": "0.0.1", + "description": "", + "author": "Saihajpreet Singh (https://saihaj.dev)", + "license": "MIT", + "private": true, + "scripts": { + "test": "vitest" + }, + "dependencies": { + "js-yaml": "3.14.1", + "zod": "^3.21.4" + }, + "devDependencies": { + "@types/js-yaml": "^3.12.7", + "vitest": "^0.34.1" + } +} diff --git a/packages/core/src/manifest.spec.ts b/packages/core/src/manifest.spec.ts new file mode 100644 index 000000000..72fa67e44 --- /dev/null +++ b/packages/core/src/manifest.spec.ts @@ -0,0 +1,23 @@ +import { expect, test } from 'vitest'; +import { parseManifest } from './manifest'; +import { safeLoad } from 'js-yaml'; +import { join } from 'path'; +import { readFile } from 'fs/promises'; + +const stubsPath = join(__dirname, '..', 'stubs'); + +test('parse "blockHandlers"', async () => { + const yaml = safeLoad(await readFile(join(stubsPath, 'block-handler.yaml'), 'utf8')); + const manifest = parseManifest(yaml); + const blockHandler = manifest.dataSources.map(({ mapping }) => mapping.blockHandlers).flat(); + + expect(blockHandler.length).toBe(1); +}); + +test('parse "callHandlers"', async () => { + const yaml = safeLoad(await readFile(join(stubsPath, 'block-handler.yaml'), 'utf8')); + const manifest = parseManifest(yaml); + const blockHandler = manifest.dataSources.map(({ mapping }) => mapping.callHandlers).flat(); + + expect(blockHandler.length).toBe(1); +}); diff --git a/packages/core/src/manifest.ts b/packages/core/src/manifest.ts new file mode 100644 index 000000000..0d7bfd6ac --- /dev/null +++ b/packages/core/src/manifest.ts @@ -0,0 +1,175 @@ +import { z } from 'zod'; + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#14-schema +const Schema = z.object({ + file: z.string().describe('The path of the GraphQL IDL file, either local or on IPFS.'), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#151-ethereumcontractsource +const EthereumContractSource = z.object({ + address: z.string(), + abi: z.string(), + startBlock: z.union([z.bigint(), z.number()]).optional(), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1522-eventhandler +const EventHandler = z.object({ + event: z + .string() + .describe( + 'An identifier for an event that will be handled in the mapping script. For Ethereum contracts, this must be the full event signature to distinguish from events that may share the same name. No alias types can be used.', + ), + handler: z + .string() + .describe( + 'The name of an exported function in the mapping script that should handle the specified event.', + ), + topic0: z + .string() + .optional() + .describe( + 'A 0x prefixed hex string. If provided, events whose topic0 is equal to this value will be processed by the given handler. When topic0 is provided, only the topic0 value will be matched, and not the hash of the event signature. This is useful for processing anonymous events in Solidity, which can have their topic0 set to anything. By default, topic0 is equal to the hash of the event signature.', + ), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1523-callhandler +const CallHandler = z.object({ + function: z + .string() + .describe( + 'An identifier for a function that will be handled in the mapping script. For Ethereum contracts, this is the normalized function signature to filter calls by.', + ), + handler: z + .string() + .describe( + 'The name of an exported function in the mapping script that should handle the specified event.', + ), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#15241-blockhandlerfilter +const BlockHandlerFilter = z.object({ + kind: z.literal('call').describe('The selected block handler filter.'), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1524-blockhandler +const BlockHandler = z.object({ + handler: z + .string() + .describe( + 'The name of an exported function in the mapping script that should handle the specified event.', + ), + filter: BlockHandlerFilter.optional().describe( + 'Definition of the filter to apply. If none is supplied, the handler will be called on every block.', + ), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1521-ethereum-mapping +const EthereumMapping = z.object({ + kind: z + .literal('ethereum/events') + .describe('Must be "ethereum/events" for Ethereum Events Mapping.'), + apiVersion: z + .string() + .describe( + 'Semver string of the version of the Mappings API that will be used by the mapping script.', + ), + language: z + .literal('wasm/assemblyscript') + .describe('The language of the runtime for the Mapping API.'), + entities: z + .array(z.string()) + .describe( + 'A list of entities that will be ingested as part of this mapping. Must correspond to names of entities in the GraphQL IDL.', + ), + eventHandlers: z + .array(EventHandler) + .optional() + .describe('Handlers for specific events, which will be defined in the mapping script.'), + callHandlers: z + .array(CallHandler) + .optional() + .describe( + 'A list of functions that will trigger a handler and the name of the corresponding handlers in the mapping.', + ), + blockHandlers: z + .array(BlockHandler) + .optional() + .describe('Defines block filters and handlers to process matching blocks.'), + file: z.string().describe('The path of the mapping script.'), + abis: z + .array( + z.object({ + name: z.string(), + file: z.string(), + }), + ) + .describe( + 'ABIs for the contract classes that should be generated in the Mapping ABI. Name is also used to reference the ABI elsewhere in the manifest.', + ), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#152-mapping +const Mapping = EthereumMapping; + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#15-data-source +const DataSource = z.object({ + kind: z.string().describe('The type of data source. Possible values: ethereum/contract.'), + name: z + .string() + .describe( + 'The name of the source data. Will be used to generate APIs in the mapping and also for self-documentation purposes.', + ), + network: z + .string() + .describe('For blockchains, this describes which network the subgraph targets'), + source: EthereumContractSource.describe('The source data on a blockchain such as Ethereum.'), + mapping: Mapping.describe('The mapping that defines how to ingest the data.'), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#17-data-source-templates +const TemplateSource = z.object({ + kind: z.string().describe('The type of data source. Possible values: ethereum/contract.'), + name: z + .string() + .describe( + 'The name of the source data. Will be used to generate APIs in the mapping and also for self-documentation purposes.', + ), + mapping: Mapping.describe('The mapping that defines how to ingest the data.'), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#18-graft-base +const GraftBase = z.object({ + base: z.string().describe('The subgraph ID of the base subgraph'), + block: z.bigint().describe('The block number up to which to use data from the base subgraph'), +}); + +// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#13-top-level-api +const Manifest = z.object({ + specVersion: z + .string() + .describe('A Semver version indicating which version of this API is being used.'), + schema: Schema.describe('The GraphQL schema of this subgraph.'), + description: z.string().describe("An optional description of the subgraph's purpose.").optional(), + repository: z.string().describe('An optional link to where the subgraph lives.').optional(), + graft: GraftBase.describe('An optional base to graft onto.').optional(), + dataSources: z + .array(DataSource) + .describe( + "Each data source spec defines the data that will be ingested as well as the transformation logic to derive the state of the subgraph's entities based on the source data.", + ), + templates: z + .array(TemplateSource) + .optional() + .describe( + 'Each data source template defines a data source that can be created dynamically from the mappings.', + ), +}); + +export type Manifest = z.infer; + +/** + * Provide a JSON object and get a typesafe Manifest object. + */ +export function parseManifest(manifest: unknown): Manifest { + return Manifest.parse(manifest); +} diff --git a/packages/core/stubs/block-handler.yaml b/packages/core/stubs/block-handler.yaml new file mode 100644 index 000000000..8b5e2698f --- /dev/null +++ b/packages/core/stubs/block-handler.yaml @@ -0,0 +1,22 @@ +specVersion: 0.0.7 +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: Contract + network: test + source: + address: '0x0000000000000000000000000000000000000000' + abi: Contract + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Gravatar + abis: + - name: Contract + file: ./abis/Contract.json + blockHandlers: + - handler: handleBlock + file: ./src/mapping.ts diff --git a/packages/core/stubs/call-handler.yaml b/packages/core/stubs/call-handler.yaml new file mode 100644 index 000000000..1b356d12d --- /dev/null +++ b/packages/core/stubs/call-handler.yaml @@ -0,0 +1,22 @@ +specVersion: 0.0.7 +schema: + file: ./schema.graphql +dataSources: + - kind: ethereum/contract + name: Contract + network: test + source: + address: '0x0000000000000000000000000000000000000000' + abi: Contract + mapping: + kind: ethereum/events + apiVersion: 0.0.7 + language: wasm/assemblyscript + entities: + - Gravatar + abis: + - name: Contract + file: ./abis/Contract.json + callHandlers: + - handler: handleBlock + file: ./src/mapping.ts diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index c42c1b194..bfd147938 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -1,5 +1,9 @@ lockfileVersion: '6.0' +settings: + autoInstallPeers: true + excludeLinksFromLockfile: false + patchedDependencies: oclif@3.8.1: hash: rxmtqiusuyruv7tkwux5gvotbm @@ -46,7 +50,7 @@ importers: examples/arweave-blocks-transactions: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -62,7 +66,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -81,7 +85,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -100,7 +104,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -119,7 +123,7 @@ importers: version: 6.26.0 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -171,7 +175,7 @@ importers: version: 5.0.4 devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -189,14 +193,14 @@ importers: examples/ethereum-gravatar: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 version: link:../../packages/ts '@nomicfoundation/hardhat-toolbox': specifier: ^2.0.2 - version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.4)(@types/mocha@10.0.1)(@types/node@18.11.9)(chai@4.3.7)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4) + version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.5)(@types/mocha@10.0.1)(@types/node@18.11.9)(chai@4.3.7)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4) hardhat: specifier: ^2.13.1 version: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) @@ -210,7 +214,7 @@ importers: examples/near-blocks: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -219,7 +223,7 @@ importers: examples/near-receipts: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli '@graphprotocol/graph-ts': specifier: 0.31.0 @@ -228,7 +232,7 @@ importers: examples/substreams-powered-subgraph: devDependencies: '@graphprotocol/graph-cli': - specifier: 0.52.0 + specifier: 0.54.0 version: link:../../packages/cli packages/cli: @@ -352,6 +356,22 @@ importers: specifier: ^5.0.0 version: 5.0.2 + packages/core: + dependencies: + js-yaml: + specifier: 3.14.1 + version: 3.14.1 + zod: + specifier: ^3.21.4 + version: 3.21.4 + devDependencies: + '@types/js-yaml': + specifier: ^3.12.7 + version: 3.12.7 + vitest: + specifier: ^0.34.1 + version: 0.34.1 + packages/ts: dependencies: assemblyscript: @@ -1782,6 +1802,204 @@ packages: dependencies: '@jridgewell/trace-mapping': 0.3.9 + /@esbuild/android-arm64@0.18.20: + resolution: {integrity: sha512-Nz4rJcchGDtENV0eMKUNa6L12zz2zBDXuhj/Vjh18zGqB44Bi7MBMSXjgunJgjRhCmKOjnPuZp4Mb6OKqtMHLQ==} + engines: {node: '>=12'} + cpu: [arm64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-arm@0.18.20: + resolution: {integrity: sha512-fyi7TDI/ijKKNZTUJAQqiG5T7YjJXgnzkURqmGj13C6dCqckZBLdl4h7bkhHt/t0WP+zO9/zwroDvANaOqO5Sw==} + engines: {node: '>=12'} + cpu: [arm] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/android-x64@0.18.20: + resolution: {integrity: sha512-8GDdlePJA8D6zlZYJV/jnrRAi6rOiNaCC/JclcXpB+KIuvfBN4owLtgzY2bsxnx666XjJx2kDPUmnTtR8qKQUg==} + engines: {node: '>=12'} + cpu: [x64] + os: [android] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-arm64@0.18.20: + resolution: {integrity: sha512-bxRHW5kHU38zS2lPTPOyuyTm+S+eobPUnTNkdJEfAddYgEcll4xkT8DB9d2008DtTbl7uJag2HuE5NZAZgnNEA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/darwin-x64@0.18.20: + resolution: {integrity: sha512-pc5gxlMDxzm513qPGbCbDukOdsGtKhfxD1zJKXjCCcU7ju50O7MeAZ8c4krSJcOIJGFR+qx21yMMVYwiQvyTyQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [darwin] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-arm64@0.18.20: + resolution: {integrity: sha512-yqDQHy4QHevpMAaxhhIwYPMv1NECwOvIpGCZkECn8w2WFHXjEwrBn3CeNIYsibZ/iZEUemj++M26W3cNR5h+Tw==} + engines: {node: '>=12'} + cpu: [arm64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/freebsd-x64@0.18.20: + resolution: {integrity: sha512-tgWRPPuQsd3RmBZwarGVHZQvtzfEBOreNuxEMKFcd5DaDn2PbBxfwLcj4+aenoh7ctXcbXmOQIn8HI6mCSw5MQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [freebsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm64@0.18.20: + resolution: {integrity: sha512-2YbscF+UL7SQAVIpnWvYwM+3LskyDmPhe31pE7/aoTMFKKzIc9lLbyGUpmmb8a8AixOL61sQ/mFh3jEjHYFvdA==} + engines: {node: '>=12'} + cpu: [arm64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-arm@0.18.20: + resolution: {integrity: sha512-/5bHkMWnq1EgKr1V+Ybz3s1hWXok7mDFUMQ4cG10AfW3wL02PSZi5kFpYKrptDsgb2WAJIvRcDm+qIvXf/apvg==} + engines: {node: '>=12'} + cpu: [arm] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ia32@0.18.20: + resolution: {integrity: sha512-P4etWwq6IsReT0E1KHU40bOnzMHoH73aXp96Fs8TIT6z9Hu8G6+0SHSw9i2isWrD2nbx2qo5yUqACgdfVGx7TA==} + engines: {node: '>=12'} + cpu: [ia32] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-loong64@0.18.20: + resolution: {integrity: sha512-nXW8nqBTrOpDLPgPY9uV+/1DjxoQ7DoB2N8eocyq8I9XuqJ7BiAMDMf9n1xZM9TgW0J8zrquIb/A7s3BJv7rjg==} + engines: {node: '>=12'} + cpu: [loong64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-mips64el@0.18.20: + resolution: {integrity: sha512-d5NeaXZcHp8PzYy5VnXV3VSd2D328Zb+9dEq5HE6bw6+N86JVPExrA6O68OPwobntbNJ0pzCpUFZTo3w0GyetQ==} + engines: {node: '>=12'} + cpu: [mips64el] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-ppc64@0.18.20: + resolution: {integrity: sha512-WHPyeScRNcmANnLQkq6AfyXRFr5D6N2sKgkFo2FqguP44Nw2eyDlbTdZwd9GYk98DZG9QItIiTlFLHJHjxP3FA==} + engines: {node: '>=12'} + cpu: [ppc64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-riscv64@0.18.20: + resolution: {integrity: sha512-WSxo6h5ecI5XH34KC7w5veNnKkju3zBRLEQNY7mv5mtBmrP/MjNBCAlsM2u5hDBlS3NGcTQpoBvRzqBcRtpq1A==} + engines: {node: '>=12'} + cpu: [riscv64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-s390x@0.18.20: + resolution: {integrity: sha512-+8231GMs3mAEth6Ja1iK0a1sQ3ohfcpzpRLH8uuc5/KVDFneH6jtAJLFGafpzpMRO6DzJ6AvXKze9LfFMrIHVQ==} + engines: {node: '>=12'} + cpu: [s390x] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/linux-x64@0.18.20: + resolution: {integrity: sha512-UYqiqemphJcNsFEskc73jQ7B9jgwjWrSayxawS6UVFZGWrAAtkzjxSqnoclCXxWtfwLdzU+vTpcNYhpn43uP1w==} + engines: {node: '>=12'} + cpu: [x64] + os: [linux] + requiresBuild: true + dev: true + optional: true + + /@esbuild/netbsd-x64@0.18.20: + resolution: {integrity: sha512-iO1c++VP6xUBUmltHZoMtCUdPlnPGdBom6IrO4gyKPFFVBKioIImVooR5I83nTew5UOYrk3gIJhbZh8X44y06A==} + engines: {node: '>=12'} + cpu: [x64] + os: [netbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/openbsd-x64@0.18.20: + resolution: {integrity: sha512-e5e4YSsuQfX4cxcygw/UCPIEP6wbIL+se3sxPdCiMbFLBWu0eiZOJ7WoD+ptCLrmjZBK1Wk7I6D/I3NglUGOxg==} + engines: {node: '>=12'} + cpu: [x64] + os: [openbsd] + requiresBuild: true + dev: true + optional: true + + /@esbuild/sunos-x64@0.18.20: + resolution: {integrity: sha512-kDbFRFp0YpTQVVrqUd5FTYmWo45zGaXe0X8E1G/LKFC0v8x0vWrhOWSLITcCn63lmZIxfOMXtCfti/RxN/0wnQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [sunos] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-arm64@0.18.20: + resolution: {integrity: sha512-ddYFR6ItYgoaq4v4JmQQaAI5s7npztfV4Ag6NrhiaW0RrnOXqBkgwZLofVTlq1daVTQNhtI5oieTvkRPfZrePg==} + engines: {node: '>=12'} + cpu: [arm64] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-ia32@0.18.20: + resolution: {integrity: sha512-Wv7QBi3ID/rROT08SABTS7eV4hX26sVduqDOTe1MvGMjNd3EjOz4b7zeexIR62GTIEKrfJXKL9LFxTYgkyeu7g==} + engines: {node: '>=12'} + cpu: [ia32] + os: [win32] + requiresBuild: true + dev: true + optional: true + + /@esbuild/win32-x64@0.18.20: + resolution: {integrity: sha512-kTdfRcSiDfQca/y9QIkng02avJ+NCaQvrMejlsB3RRv5sE9rRoeBPISaZpKxHELzRxZyLvNts1P27W3wV+8geQ==} + engines: {node: '>=12'} + cpu: [x64] + os: [win32] + requiresBuild: true + dev: true + optional: true + /@eslint-community/eslint-utils@4.1.2(eslint@8.31.0): resolution: {integrity: sha512-7qELuQWWjVDdVsFQ5+beUl+KPczrEDA7S3zM4QUd/bJl7oXgsmpXaEVqrRTnOBqenOV4rWf2kVZk2Ot085zPWA==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} @@ -2407,14 +2625,14 @@ packages: engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 /@jridgewell/gen-mapping@0.3.2: resolution: {integrity: sha512-mh65xKQAzI6iBcFzwv28KVWSmCkdRBWoOh+bYQGW3+6OZvbbN3TqMGo5hqYxQniRcH9F2VZIoJCm4pa3BPDK/A==} engines: {node: '>=6.0.0'} dependencies: '@jridgewell/set-array': 1.1.2 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 '@jridgewell/trace-mapping': 0.3.17 /@jridgewell/resolve-uri@3.1.0: @@ -2428,6 +2646,9 @@ packages: /@jridgewell/sourcemap-codec@1.4.14: resolution: {integrity: sha512-XPSJHWmi394fuUuzDnGz1wiKqWfo1yXecHQMRf2l6hztTO+nPru658AyDngaBe7isIxEkRsPR3FZh+s7iVa4Uw==} + /@jridgewell/sourcemap-codec@1.4.15: + resolution: {integrity: sha512-eF2rxCRulEKXHTRiDrDy6erMYWqNw4LPdQ8UQA4huuxaQsVeRPFl2oM8oDGxMFhJUWZf9McpLtJasDDZb/Bpeg==} + /@jridgewell/trace-mapping@0.3.17: resolution: {integrity: sha512-MCNzAp77qzKca9+W/+I0+sEpaUnZoeasnghNeVc41VZCEKaCH73Vq3BZZ/SzWIgrqE4H4ceI+p+b6C0mHf9T4g==} dependencies: @@ -2438,7 +2659,7 @@ packages: resolution: {integrity: sha512-3Belt6tdc8bPgAtbcmdtNJlirVoTmEb5e2gC94PnkwEW9jI6CAHUeoG85tjWP5WquqfavoMtMwiG4P926ZKKuQ==} dependencies: '@jridgewell/resolve-uri': 3.1.0 - '@jridgewell/sourcemap-codec': 1.4.14 + '@jridgewell/sourcemap-codec': 1.4.15 /@manypkg/find-root@1.1.0: resolution: {integrity: sha512-mki5uBvhHzO8kYYix/WRy2WX8S3B5wdVSc9D6KcU5lQNglP2yt58/VfLuAK49glRXChosY8ap2oJ1qgma3GUVA==} @@ -2728,6 +2949,50 @@ packages: typescript: 5.0.4 dev: true + /@nomicfoundation/hardhat-toolbox@2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.5)(@types/mocha@10.0.1)(@types/node@18.11.9)(chai@4.3.7)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4): + resolution: {integrity: sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==} + peerDependencies: + '@ethersproject/abi': ^5.4.7 + '@ethersproject/providers': ^5.4.7 + '@nomicfoundation/hardhat-chai-matchers': ^1.0.0 + '@nomicfoundation/hardhat-network-helpers': ^1.0.0 + '@nomiclabs/hardhat-ethers': ^2.0.0 + '@nomiclabs/hardhat-etherscan': ^3.0.0 + '@typechain/ethers-v5': ^10.1.0 + '@typechain/hardhat': ^6.1.2 + '@types/chai': ^4.2.0 + '@types/mocha': '>=9.1.0' + '@types/node': '>=12.0.0' + chai: ^4.2.0 + ethers: ^5.4.7 + hardhat: ^2.11.0 + hardhat-gas-reporter: ^1.0.8 + solidity-coverage: ^0.8.1 + ts-node: '>=8.0.0' + typechain: ^8.1.0 + typescript: '>=4.5.0' + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/hardhat-chai-matchers': 1.0.6(@nomiclabs/hardhat-ethers@2.2.3)(chai@4.3.7)(ethers@5.7.2)(hardhat@2.13.1) + '@nomicfoundation/hardhat-network-helpers': 1.0.8(hardhat@2.13.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.13.1) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.13.1) + '@typechain/ethers-v5': 10.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.1.1)(typescript@5.0.4) + '@typechain/hardhat': 6.1.5(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@10.2.0)(ethers@5.7.2)(hardhat@2.13.1)(typechain@8.1.1) + '@types/chai': 4.3.5 + '@types/mocha': 10.0.1 + '@types/node': 18.11.9 + chai: 4.3.7 + ethers: 5.7.2 + hardhat: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) + hardhat-gas-reporter: 1.0.9(hardhat@2.13.1) + solidity-coverage: 0.8.2(hardhat@2.13.1) + ts-node: 10.9.1(@types/node@18.11.9)(typescript@5.0.4) + typechain: 8.1.1(jest@29.5.0)(typescript@5.0.4) + typescript: 5.0.4 + dev: true + /@nomicfoundation/solidity-analyzer-darwin-arm64@0.1.1: resolution: {integrity: sha512-KcTodaQw8ivDZyF+D76FokN/HdpgGpfjc/gFCImdLUyqB6eSWVaZPazMbeAjmfhx3R0zm/NYVzxwAokFKgrc0w==} engines: {node: '>= 10'} @@ -3558,9 +3823,19 @@ packages: dependencies: '@types/chai': 4.3.4 + /@types/chai-subset@1.3.3: + resolution: {integrity: sha512-frBecisrNGz+F4T6bcc+NLeolfiojh5FxW2klu669+8BARtyQv2C/GkNW6FUodVe4BroGMP/wER/YDGc7rEllw==} + dependencies: + '@types/chai': 4.3.5 + dev: true + /@types/chai@4.3.4: resolution: {integrity: sha512-KnRanxnpfpjUTqTCXslZSEdLfXExwgNxYPdiO2WGUj8+HDjFi8R3k5RVKPeSCzLjCcshCAtVO2QBbVuAV4kTnw==} + /@types/chai@4.3.5: + resolution: {integrity: sha512-mEo1sAde+UCE6b2hxn332f1g1E8WfYRu6p5SvTKr2ZKC1f7gFJXk4h5PyGP9Dt6gCaG8y8XhwnXWC6Iy2cmBng==} + dev: true + /@types/cli-progress@3.11.0: resolution: {integrity: sha512-XhXhBv1R/q2ahF3BM7qT5HLzJNlIL0wbcGyZVjqOTqAybAnsLisd7gy1UCyIqpL+5Iv6XhlSyzjLCnI2sIdbCg==} dependencies: @@ -3920,6 +4195,44 @@ packages: eslint-visitor-keys: 3.3.0 dev: true + /@vitest/expect@0.34.1: + resolution: {integrity: sha512-q2CD8+XIsQ+tHwypnoCk8Mnv5e6afLFvinVGCq3/BOT4kQdVQmY6rRfyKkwcg635lbliLPqbunXZr+L1ssUWiQ==} + dependencies: + '@vitest/spy': 0.34.1 + '@vitest/utils': 0.34.1 + chai: 4.3.7 + dev: true + + /@vitest/runner@0.34.1: + resolution: {integrity: sha512-YfQMpYzDsYB7yqgmlxZ06NI4LurHWfrH7Wy3Pvf/z/vwUSgq1zLAb1lWcItCzQG+NVox+VvzlKQrYEXb47645g==} + dependencies: + '@vitest/utils': 0.34.1 + p-limit: 4.0.0 + pathe: 1.1.1 + dev: true + + /@vitest/snapshot@0.34.1: + resolution: {integrity: sha512-0O9LfLU0114OqdF8lENlrLsnn024Tb1CsS9UwG0YMWY2oGTQfPtkW+B/7ieyv0X9R2Oijhi3caB1xgGgEgclSQ==} + dependencies: + magic-string: 0.30.2 + pathe: 1.1.1 + pretty-format: 29.5.0 + dev: true + + /@vitest/spy@0.34.1: + resolution: {integrity: sha512-UT4WcI3EAPUNO8n6y9QoEqynGGEPmmRxC+cLzneFFXpmacivjHZsNbiKD88KUScv5DCHVDgdBsLD7O7s1enFcQ==} + dependencies: + tinyspy: 2.1.1 + dev: true + + /@vitest/utils@0.34.1: + resolution: {integrity: sha512-/ql9dsFi4iuEbiNcjNHQWXBum7aL8pyhxvfnD9gNtbjR9fUKAjxhj4AA3yfLXg6gJpMGGecvtF8Au2G9y3q47Q==} + dependencies: + diff-sequences: 29.4.3 + loupe: 2.3.6 + pretty-format: 29.5.0 + dev: true + /@whatwg-node/events@0.0.2: resolution: {integrity: sha512-WKj/lI4QjnLuPrim0cfO7i+HsDSXHxNv1y0CrJhdntuO3hxWZmnXCwNDnwOvry11OjRin6cgWNF+j/9Pn8TN4w==} dev: false @@ -3984,6 +4297,14 @@ packages: module-error: 1.0.2 queue-microtask: 1.2.3 + /acorn-jsx@5.3.2(acorn@8.10.0): + resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} + peerDependencies: + acorn: ^6.0.0 || ^7.0.0 || ^8.0.0 + dependencies: + acorn: 8.10.0 + dev: true + /acorn-jsx@5.3.2(acorn@8.8.1): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -4014,10 +4335,16 @@ packages: hasBin: true dev: true + /acorn@8.10.0: + resolution: {integrity: sha512-F0SAmZ8iUtS//m8DmCTA0jlh6TDKkHQyK6xc6V4KDTyZKA9dnvX9/3sRTVQrWm79glUAZbnmmNcdYwUIHWVybw==} + engines: {node: '>=0.4.0'} + hasBin: true + /acorn@8.8.1: resolution: {integrity: sha512-7zFpHzhnqYKrkYdUjF1HI1bzd0VygEGX8lFk4k5zVMqHEoES+P+7TKI+EvLO9WVMJ8eekdO0aDEK044xTXwPPA==} engines: {node: '>=0.4.0'} hasBin: true + dev: true /address@1.2.2: resolution: {integrity: sha512-4B/qKCfeE/ODUaAUpSwfzazo5x29WD4r3vXiWsB7I2mSDAihwEqKO+g8GELZUQSSAo5e1XTYh3ZVfLyxBc12nA==} @@ -4945,6 +5272,11 @@ packages: resolution: {integrity: sha512-/Nf7TyzTx6S3yRJObOAV7956r8cr2+Oj8AC5dt8wSP3BQAoeX58NoHyCU8P8zGkNXStjTSi6fzO6F0pBdcYbEg==} engines: {node: '>= 0.8'} + /cac@6.7.14: + resolution: {integrity: sha512-b6Ilus+c3RrdDk+JhLKUAQfzzgLEPy6wcXqS7f/xe1EETvsDP6GORG7SFuOs6cID5YkqchW/LXZbX5bc8j7ZcQ==} + engines: {node: '>=8'} + dev: true + /cacache@15.3.0: resolution: {integrity: sha512-VVdYzXEn+cnbXpFgWs5hTT7OScegHVmLhJIR8Ufqk3iFD6A6j5iSX1KuBTfNEv4tdJWE2PzA6IVFtcLC7fN9wQ==} engines: {node: '>= 10'} @@ -6173,6 +6505,36 @@ packages: es6-promise: 4.2.8 dev: false + /esbuild@0.18.20: + resolution: {integrity: sha512-ceqxoedUrcayh7Y7ZX6NdbbDzGROiyVBgC4PriJThBKSVPWnnFHZAkfI1lJT8QFkOwH4qOS2SJkS4wvpGl8BpA==} + engines: {node: '>=12'} + hasBin: true + requiresBuild: true + optionalDependencies: + '@esbuild/android-arm': 0.18.20 + '@esbuild/android-arm64': 0.18.20 + '@esbuild/android-x64': 0.18.20 + '@esbuild/darwin-arm64': 0.18.20 + '@esbuild/darwin-x64': 0.18.20 + '@esbuild/freebsd-arm64': 0.18.20 + '@esbuild/freebsd-x64': 0.18.20 + '@esbuild/linux-arm': 0.18.20 + '@esbuild/linux-arm64': 0.18.20 + '@esbuild/linux-ia32': 0.18.20 + '@esbuild/linux-loong64': 0.18.20 + '@esbuild/linux-mips64el': 0.18.20 + '@esbuild/linux-ppc64': 0.18.20 + '@esbuild/linux-riscv64': 0.18.20 + '@esbuild/linux-s390x': 0.18.20 + '@esbuild/linux-x64': 0.18.20 + '@esbuild/netbsd-x64': 0.18.20 + '@esbuild/openbsd-x64': 0.18.20 + '@esbuild/sunos-x64': 0.18.20 + '@esbuild/win32-arm64': 0.18.20 + '@esbuild/win32-ia32': 0.18.20 + '@esbuild/win32-x64': 0.18.20 + dev: true + /escalade@3.1.1: resolution: {integrity: sha512-k0er2gUkLf8O0zKJiAhmkTnJlTvINGv7ygDNPbeIsX/TJjGJZHuh9B2UxbsaEkmlEo9MfhrSzmhIlhRlI2GXnw==} engines: {node: '>=6'} @@ -6246,8 +6608,8 @@ packages: peerDependencies: eslint: '>=8.0.0' dependencies: - acorn: 8.8.1 - acorn-jsx: 5.3.2(acorn@8.8.1) + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) cosmiconfig: 7.1.0 eslint: 8.31.0 espree: 9.4.1 @@ -7951,7 +8313,7 @@ packages: resolution: {integrity: sha512-AmCS+9CT34pp2u0QQVXjKztkuq3y5T+BIciuiHDDtDZucZD8VudosnSdUyXJV6IsRkN5jc4RFDhCk1O6Q3Gxjg==} dependencies: interface-store: 2.0.2 - nanoid: 3.3.3 + nanoid: 3.3.6 uint8arrays: 3.1.1 dev: false @@ -9107,12 +9469,16 @@ packages: resolution: {integrity: sha512-qCRJWlbP2v6HbmKW7R3lFbeiVWHo+oMJ0j+MizwvauqnCV/EvtAeEeuCgoc/ErtsuoKgYB8U4Ih8AxJbXoE6/g==} engines: {node: ^12.22.0 || ^14.17.0 || >=16.0.0} dependencies: - acorn: 8.8.1 + acorn: 8.10.0 eslint-visitor-keys: 3.3.0 espree: 9.4.1 semver: 7.4.0 dev: true + /jsonc-parser@3.2.0: + resolution: {integrity: sha512-gfFQZrcTc8CnKXp6Y4/CBT3fTc0OVuDofpre4aEeEpSBPV5X5v4+Vmx+8snU7RLPrNHPKSgLxGo9YuQzz20o+w==} + dev: true + /jsonfile@2.4.0: resolution: {integrity: sha512-PKllAqbgLgxHaj8TElYymKCAgrASebJrWpTnEkOaTowt23VKXXN0sUeriJ+eh7y6ufb/CC5ap11pz71/cM0hUw==} optionalDependencies: @@ -9255,6 +9621,11 @@ packages: strip-bom: 3.0.0 dev: true + /local-pkg@0.4.3: + resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} + engines: {node: '>=14'} + dev: true + /locate-path@2.0.0: resolution: {integrity: sha512-NCI2kiDkyR7VeEKm27Kda/iQHyKJe1Bu0FlTbYp3CqJu+9IFe9bLyAjMxf5ZDDbEg+iMPzB5zYyUTSm8wVTKmA==} engines: {node: '>=4'} @@ -9419,6 +9790,13 @@ packages: /lru_map@0.3.3: resolution: {integrity: sha512-Pn9cox5CsMYngeDbmChANltQl+5pi6XmTrraMSzhPmMBbmgcxmqWry0U3PGapCU1yB4/LqCcom7qhHZiF/jGfQ==} + /magic-string@0.30.2: + resolution: {integrity: sha512-lNZdu7pewtq/ZvWUp9Wpf/x7WzMTsR26TWV03BRZrXFsv+BI6dy8RAiKgm1uM/kyR0rCfUcqvOlXKG66KhIGug==} + engines: {node: '>=12'} + dependencies: + '@jridgewell/sourcemap-codec': 1.4.15 + dev: true + /make-dir@3.1.0: resolution: {integrity: sha512-g3FeP20LNwhALb/6Cz6Dd4F2ngze0jz7tbzrD2wAV+o9FeNHe4rL+yK2md0J/fiSf1sa1ADhXqi5+oVwOM/eGw==} engines: {node: '>=8'} @@ -9766,8 +10144,8 @@ packages: /micromark-extension-mdxjs@1.0.0: resolution: {integrity: sha512-TZZRZgeHvtgm+IhtgC2+uDMR7h8eTKF0QUX9YsgoL9+bADBpBY6SiLvWqnBlLbCEevITmTqmEuY3FoxMKVs1rQ==} dependencies: - acorn: 8.8.1 - acorn-jsx: 5.3.2(acorn@8.8.1) + acorn: 8.10.0 + acorn-jsx: 5.3.2(acorn@8.10.0) micromark-extension-mdx-expression: 1.0.3 micromark-extension-mdx-jsx: 1.0.3 micromark-extension-mdx-md: 1.0.0 @@ -10154,6 +10532,15 @@ packages: engines: {node: '>=10'} hasBin: true + /mlly@1.4.0: + resolution: {integrity: sha512-ua8PAThnTwpprIaU47EPeZ/bPUVp2QYBbWMphUQpVdBI3Lgqzm5KZQ45Agm3YJedHXaIHl6pBGabaLSUPPSptg==} + dependencies: + acorn: 8.10.0 + pathe: 1.1.1 + pkg-types: 1.0.3 + ufo: 1.2.0 + dev: true + /mnemonist@0.38.5: resolution: {integrity: sha512-bZTFT5rrPKtPJxj8KSV0WkPyNxl72vQepqqVUAW2ARUpUSF2qXMB6jZj7hW5/k7C1rtpzqbD/IIbJwLXUjCHeg==} dependencies: @@ -10326,6 +10713,11 @@ packages: engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} hasBin: true + /nanoid@3.3.6: + resolution: {integrity: sha512-BGcqMMJuToF7i1rt+2PWSNVnWIkGCU78jBG3RxO/bZlnZPK2Cmi2QaffxGO/2RvWi9sL+FAiRiXMgsyxQ1DIDA==} + engines: {node: ^10 || ^12 || ^13.7 || ^14 || >=15.0.1} + hasBin: true + /napi-macros@2.2.2: resolution: {integrity: sha512-hmEVtAGYzVQpCKdbQea4skABsdXW4RUh5t5mJ2zzqowJS2OyXZTU1KhDVFhx+NlWZ4ap9mqR9TcDO3LTTttd+g==} @@ -10848,6 +11240,13 @@ packages: dependencies: yocto-queue: 0.1.0 + /p-limit@4.0.0: + resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} + engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} + dependencies: + yocto-queue: 1.0.0 + dev: true + /p-locate@2.0.0: resolution: {integrity: sha512-nQja7m7gSKuewoVRen45CtVfODR3crN3goVQ0DDZ9N3yHxgpkuBhZqsaiotSQRrADUrne346peY7kT3TSACykg==} engines: {node: '>=4'} @@ -11053,6 +11452,10 @@ packages: resolution: {integrity: sha512-gDKb8aZMDeD/tZWs9P6+q0J9Mwkdl6xMV8TjnGP3qJVJ06bdMgkbBlLU8IdfOsIsFz2BW1rNVT3XuNEl8zPAvw==} engines: {node: '>=8'} + /pathe@1.1.1: + resolution: {integrity: sha512-d+RQGp0MAYTIaDBIMmOfMwz3E+LOZnxx1HZd5R18mmCZY0QBlK0LDZfPc8FW8Ed2DlvsuE6PRjroDY+wg4+j/Q==} + dev: true + /pathval@1.1.1: resolution: {integrity: sha512-Dp6zGqpTdETdR63lehJYPeIOqpiNBNtc7BpWSLrOje7UaIsE5aY92r/AunQA7rsXvet3lrJ3JnZX29UPTKXyKQ==} @@ -11095,10 +11498,27 @@ packages: dependencies: find-up: 4.1.0 + /pkg-types@1.0.3: + resolution: {integrity: sha512-nN7pYi0AQqJnoLPC9eHFQ8AcyaixBUOwvqc5TDnIKCMEE6I0y8P7OKA7fPexsXGCGxQDl/cmrLAp26LhcwxZ4A==} + dependencies: + jsonc-parser: 3.2.0 + mlly: 1.4.0 + pathe: 1.1.1 + dev: true + /pluralize@8.0.0: resolution: {integrity: sha512-Nc3IT5yHzflTfbjgqWcCPpo7DaKy4FnpB0l/zCAW0Tc7jxAiuqSxHasntB3D7887LSrA93kDJ9IXovxJYxyLCA==} engines: {node: '>=4'} + /postcss@8.4.27: + resolution: {integrity: sha512-gY/ACJtJPSmUFPDCHtX78+01fHa64FaU4zaaWfuh1MhGJISufJAH4cun6k/8fwsHYeK4UQmENQK+tRLCFJE8JQ==} + engines: {node: ^10 || ^12 || >=14} + dependencies: + nanoid: 3.3.6 + picocolors: 1.0.0 + source-map-js: 1.0.2 + dev: true + /preferred-pm@3.0.3: resolution: {integrity: sha512-+wZgbxNES/KlJs9q40F/1sfOd/j7f1O9JaHcW5Dsn3aUUOZg3L2bjpVUcKV2jvtElYfoTuQiNeMfQJ4kwUAhCQ==} engines: {node: '>=10'} @@ -11757,6 +12177,14 @@ packages: dependencies: bn.js: 5.2.1 + /rollup@3.28.0: + resolution: {integrity: sha512-d7zhvo1OUY2SXSM6pfNjgD5+d0Nz87CUp4mt8l/GgVP3oBsPwzNvSzyu1me6BSG9JIgWNTVcafIXBIyM8yQ3yw==} + engines: {node: '>=14.18.0', npm: '>=8.0.0'} + hasBin: true + optionalDependencies: + fsevents: 2.3.2 + dev: true + /run-async@2.4.1: resolution: {integrity: sha512-tvVnVv01b8c1RrA6Ep7JkStj85Guv/YrMcwqYQnwjsAS2cTmmPGBBjAjpCW7RrSodNSoE2/qg9O4bceNvUuDgQ==} engines: {node: '>=0.12.0'} @@ -11953,6 +12381,10 @@ packages: get-intrinsic: 1.1.3 object-inspect: 1.12.2 + /siginfo@2.0.0: + resolution: {integrity: sha512-ybx0WO1/8bSBLEWXZvEd7gMW3Sn3JFlW3TvX1nREbDLRNQNaeNN8WK0meBwPdAaOI7TtRRRJn/Es1zhrrCHu7g==} + dev: true + /signal-exit@3.0.7: resolution: {integrity: sha512-wnD2ZE+l+SPC/uoS0vXeE9L1+0wuaMqKlfz9AMUo38JsyLSBWSFcHR1Rri62LZc12vLr1gb3jl7iwQhgwpAbGQ==} @@ -12083,6 +12515,11 @@ packages: is-plain-obj: 2.1.0 dev: true + /source-map-js@1.0.2: + resolution: {integrity: sha512-R0XvVJ9WusLiqTCEiGCmICCMplcCkIwwR11mOSD9CR5u+IXYdiseeEuXCVAjS54zqwkLcPNnmU4OeJ6tUrWhDw==} + engines: {node: '>=0.10.0'} + dev: true + /source-map-support@0.4.18: resolution: {integrity: sha512-try0/JqxPLF9nOjvSta7tVondkP5dwgyLDjVoyMDlmjugT2lRZ1OfsrYTkCd2hkDnJTKRbO/Rl3orm8vlsUzbA==} dependencies: @@ -12193,6 +12630,10 @@ packages: dependencies: escape-string-regexp: 2.0.0 + /stackback@0.0.2: + resolution: {integrity: sha512-1XMJE5fQo1jGH6Y/7ebnwPOBEkIEnT4QF32d5R1+VXdXveM0IBMJt8zfaxX1P3QhVwrYe+576+jkANtSS2mBbw==} + dev: true + /stacktrace-parser@0.1.10: resolution: {integrity: sha512-KJP1OCML99+8fhOHxwwzyWrlUuVX5GQ0ZpJTd1DFXhdkrvg1szxfHhawXUZ3g9TkXORQd4/WG68jMlQZ2p8wlg==} engines: {node: '>=6'} @@ -12203,6 +12644,10 @@ packages: resolution: {integrity: sha512-RwNA9Z/7PrK06rYLIzFMlaF+l73iwpzsqRIFgbMLbTcLD6cOao82TaWefPXQvB2fOC4AjuYSEndS7N/mTCbkdQ==} engines: {node: '>= 0.8'} + /std-env@3.3.3: + resolution: {integrity: sha512-Rz6yejtVyWnVjC1RFvNmYL10kgjC49EOghxWn0RFqlCHGFpQx+Xe7yW3I4ceK1SGrWIGMjD5Kbue8W/udkbMJg==} + dev: true + /stealthy-require@1.1.1: resolution: {integrity: sha512-ZnWpYnYugiOVEY5GkcuJK1io5V8QmNYChG62gSit9pQVGErXtrKuPC55ITaVSukmMta5qpMU7vqLt2Lnni4f/g==} engines: {node: '>=0.10.0'} @@ -12399,6 +12844,12 @@ packages: resolution: {integrity: sha512-6fPc+R4ihwqP6N/aIv2f1gMH8lOVtWQHoqC4yK6oSDVVocumAsfCqjkXnqiYMhmMwS/mEHLp7Vehlt3ql6lEig==} engines: {node: '>=8'} + /strip-literal@1.3.0: + resolution: {integrity: sha512-PugKzOsyXpArk0yWmUwqOZecSO0GH0bPoctLcqNDH9J04pVW3lflYE0ujElBGTloevcxF5MofAOZ7C5l2b+wLg==} + dependencies: + acorn: 8.10.0 + dev: true + /supports-color@2.0.0: resolution: {integrity: sha512-KKNVtd6pCYgPIKU4cp2733HWYCpplQhddZLBUryaAHou723x+FRzQ5Df824Fj+IyyuiQTRoub4SnIFfIcrp70g==} engines: {node: '>=0.8.0'} @@ -12610,6 +13061,20 @@ packages: globrex: 0.1.2 dev: true + /tinybench@2.5.0: + resolution: {integrity: sha512-kRwSG8Zx4tjF9ZiyH4bhaebu+EDz1BOx9hOigYHlUW4xxI/wKIUQUqo018UlU4ar6ATPBsaMrdbKZ+tmPdohFA==} + dev: true + + /tinypool@0.7.0: + resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} + engines: {node: '>=14.0.0'} + dev: true + + /tinyspy@2.1.1: + resolution: {integrity: sha512-XPJL2uSzcOyBMky6OFrusqWlzfFrXtE0hPuMgW8A2HmaqrPo4ZQHRN/V0QXN3FSjKxpsbRrFc5LI7KOwBsT1/w==} + engines: {node: '>=14.0.0'} + dev: true + /tmp-promise@3.0.3: resolution: {integrity: sha512-RwM7MoPojPxsOBYnyd2hy0bxtIlVrihNs9pj5SUvY8Zz1sQcQG2tG1hSr8PDxfgEB8RNKDhqbIlroIarSNDNsQ==} dependencies: @@ -12729,7 +13194,7 @@ packages: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 '@types/node': 18.11.9 - acorn: 8.8.1 + acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -12759,7 +13224,7 @@ packages: '@tsconfig/node14': 1.0.3 '@tsconfig/node16': 1.0.3 '@types/node': 18.11.9 - acorn: 8.8.1 + acorn: 8.10.0 acorn-walk: 8.2.0 arg: 4.1.3 create-require: 1.1.1 @@ -12913,6 +13378,10 @@ packages: resolution: {integrity: sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==} engines: {node: '>=8'} + /ufo@1.2.0: + resolution: {integrity: sha512-RsPyTbqORDNDxqAdQPQBpgqhWle1VcTSou/FraClYlHf6TZnQcGslpLcAphNR+sQW4q5lLWLbOsRlh9j24baQg==} + dev: true + /uglify-js@3.17.4: resolution: {integrity: sha512-T9q82TJI9e/C1TAxYvfb16xO120tMVFZrGA3f9/P4424DNu6ypK103y0GPFVa17yotwSyZW5iYXgjYHkGrJW/g==} engines: {node: '>=0.8.0'} @@ -13225,6 +13694,129 @@ packages: replace-ext: 1.0.1 dev: true + /vite-node@0.34.1(@types/node@18.11.9): + resolution: {integrity: sha512-odAZAL9xFMuAg8aWd7nSPT+hU8u2r9gU3LRm9QKjxBEF2rRdWpMuqkrkjvyVQEdNFiBctqr2Gg4uJYizm5Le6w==} + engines: {node: '>=v14.18.0'} + hasBin: true + dependencies: + cac: 6.7.14 + debug: 4.3.4(supports-color@8.1.1) + mlly: 1.4.0 + pathe: 1.1.1 + picocolors: 1.0.0 + vite: 4.4.9(@types/node@18.11.9) + transitivePeerDependencies: + - '@types/node' + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + + /vite@4.4.9(@types/node@18.11.9): + resolution: {integrity: sha512-2mbUn2LlUmNASWwSCNSJ/EG2HuSRTnVNaydp6vMCm5VIqJsjMfbIWtbH2kDuwUVW5mMUKKZvGPX/rqeqVvv1XA==} + engines: {node: ^14.18.0 || >=16.0.0} + hasBin: true + peerDependencies: + '@types/node': '>= 14' + less: '*' + lightningcss: ^1.21.0 + sass: '*' + stylus: '*' + sugarss: '*' + terser: ^5.4.0 + peerDependenciesMeta: + '@types/node': + optional: true + less: + optional: true + lightningcss: + optional: true + sass: + optional: true + stylus: + optional: true + sugarss: + optional: true + terser: + optional: true + dependencies: + '@types/node': 18.11.9 + esbuild: 0.18.20 + postcss: 8.4.27 + rollup: 3.28.0 + optionalDependencies: + fsevents: 2.3.2 + dev: true + + /vitest@0.34.1: + resolution: {integrity: sha512-G1PzuBEq9A75XSU88yO5G4vPT20UovbC/2osB2KEuV/FisSIIsw7m5y2xMdB7RsAGHAfg2lPmp2qKr3KWliVlQ==} + engines: {node: '>=v14.18.0'} + hasBin: true + peerDependencies: + '@edge-runtime/vm': '*' + '@vitest/browser': '*' + '@vitest/ui': '*' + happy-dom: '*' + jsdom: '*' + playwright: '*' + safaridriver: '*' + webdriverio: '*' + peerDependenciesMeta: + '@edge-runtime/vm': + optional: true + '@vitest/browser': + optional: true + '@vitest/ui': + optional: true + happy-dom: + optional: true + jsdom: + optional: true + playwright: + optional: true + safaridriver: + optional: true + webdriverio: + optional: true + dependencies: + '@types/chai': 4.3.5 + '@types/chai-subset': 1.3.3 + '@types/node': 18.11.9 + '@vitest/expect': 0.34.1 + '@vitest/runner': 0.34.1 + '@vitest/snapshot': 0.34.1 + '@vitest/spy': 0.34.1 + '@vitest/utils': 0.34.1 + acorn: 8.10.0 + acorn-walk: 8.2.0 + cac: 6.7.14 + chai: 4.3.7 + debug: 4.3.4(supports-color@8.1.1) + local-pkg: 0.4.3 + magic-string: 0.30.2 + pathe: 1.1.1 + picocolors: 1.0.0 + std-env: 3.3.3 + strip-literal: 1.3.0 + tinybench: 2.5.0 + tinypool: 0.7.0 + vite: 4.4.9(@types/node@18.11.9) + vite-node: 0.34.1(@types/node@18.11.9) + why-is-node-running: 2.2.2 + transitivePeerDependencies: + - less + - lightningcss + - sass + - stylus + - sugarss + - supports-color + - terser + dev: true + /walk-up-path@1.0.0: resolution: {integrity: sha512-hwj/qMDUEjCU5h0xr90KGCf0tg0/LgJbmOWgrWKYlcJZM7XvquvUJZ0G/HMGr7F7OQMOUuPHWP9JpriinkAlkg==} dev: true @@ -13354,6 +13946,15 @@ packages: dependencies: isexe: 2.0.0 + /why-is-node-running@2.2.2: + resolution: {integrity: sha512-6tSwToZxTOcotxHeA+qGCq1mVzKR3CwcJGmVcY+QE8SHy6TnpFnh8PAvPNHYr7EcuVeG0QSMxtYCuO1ta/G/oA==} + engines: {node: '>=8'} + hasBin: true + dependencies: + siginfo: 2.0.0 + stackback: 0.0.2 + dev: true + /wide-align@1.1.3: resolution: {integrity: sha512-QGkOQc8XL6Bt5PwnsExKBPuMKBxnGxWWW3fU55Xt4feHozMUhdUMaBCk290qpm/wG5u/RSKzwdAC4i51YigihA==} dependencies: @@ -13693,6 +14294,11 @@ packages: resolution: {integrity: sha512-rVksvsnNCdJ/ohGc6xgPwyN8eheCxsiLM8mxuE/t/mOVqJewPuO1miLpTHQiRgTKCLexL4MeAFVagts7HmNZ2Q==} engines: {node: '>=10'} + /yocto-queue@1.0.0: + resolution: {integrity: sha512-9bnSc/HEW2uRy67wc+T8UwauLuPJVn28jb+GtJY16iiKWyvmYJRXVT4UamsAEGQfPohgr2q4Tq0sQbQlxTfi1g==} + engines: {node: '>=12.20'} + dev: true + /yosay@2.0.2: resolution: {integrity: sha512-avX6nz2esp7IMXGag4gu6OyQBsMh/SEn+ZybGu3yKPlOTE6z9qJrzG/0X5vCq/e0rPFy0CUYCze0G5hL310ibA==} engines: {node: '>=4'} @@ -13709,6 +14315,10 @@ packages: wrap-ansi: 2.1.0 dev: true + /zod@3.21.4: + resolution: {integrity: sha512-m46AKbrzKVzOzs/DZgVnG5H55N1sv1M8qZU3A8RIKbs3mrACDNeIOeilDymVb2HdmP8uwshOCF4uJ8uM9rCqJw==} + dev: false + /zwitch@2.0.4: resolution: {integrity: sha512-bXE4cR/kVZhKZX/RjPEflHaKVhUVl85noU3v6b8apfQEc1x4A+zBxjZ4lN8LqGd6WZ3dl98pY4o717VFmoPp+A==} dev: true From ca6b41bb3f59a478c51f5500c731d2801bea495c Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 13 Nov 2023 15:52:19 -0500 Subject: [PATCH 2/7] support substreams --- packages/core/src/manifest.spec.ts | 37 ++++++++++- packages/core/src/manifest.ts | 63 ++++++++++++++++--- .../stubs/substream-subgraph-with-params.yaml | 20 ++++++ packages/core/stubs/substream-subgraph.yaml | 16 +++++ 4 files changed, 126 insertions(+), 10 deletions(-) create mode 100644 packages/core/stubs/substream-subgraph-with-params.yaml create mode 100644 packages/core/stubs/substream-subgraph.yaml diff --git a/packages/core/src/manifest.spec.ts b/packages/core/src/manifest.spec.ts index 72fa67e44..f3344560f 100644 --- a/packages/core/src/manifest.spec.ts +++ b/packages/core/src/manifest.spec.ts @@ -1,8 +1,9 @@ +import assert from 'assert'; +import { readFile } from 'fs/promises'; +import { join } from 'path'; +import { safeLoad } from 'js-yaml'; import { expect, test } from 'vitest'; import { parseManifest } from './manifest'; -import { safeLoad } from 'js-yaml'; -import { join } from 'path'; -import { readFile } from 'fs/promises'; const stubsPath = join(__dirname, '..', 'stubs'); @@ -21,3 +22,33 @@ test('parse "callHandlers"', async () => { expect(blockHandler.length).toBe(1); }); + +test('parse "eventHandlers"', async () => { + const yaml = safeLoad(await readFile(join(stubsPath, 'block-handler.yaml'), 'utf8')); + const manifest = parseManifest(yaml); + const eventHandlers = manifest.dataSources.map(({ mapping }) => mapping.eventHandlers).flat(); + + expect(eventHandlers.length).toBe(1); +}); + +test('parse "package source"', async () => { + const yaml = safeLoad(await readFile(join(stubsPath, 'substream-subgraph.yaml'), 'utf8')); + const manifest = parseManifest(yaml); + const substream = manifest.dataSources.find(({ kind }) => kind === 'substreams'); + + assert(substream); + assert(substream.kind === 'substreams'); + expect(substream.source.package.moduleName).equal('graph_out'); +}); + +test('parse "substreams params"', async () => { + const yaml = safeLoad( + await readFile(join(stubsPath, 'substream-subgraph-with-params.yaml'), 'utf8'), + ); + const manifest = parseManifest(yaml); + const substream = manifest.dataSources.find(({ kind }) => kind === 'substreams'); + + assert(substream); + assert(substream.kind === 'substreams'); + expect(substream.source.package.params).toEqual(['a', 'b', 123]); +}); diff --git a/packages/core/src/manifest.ts b/packages/core/src/manifest.ts index 0d7bfd6ac..c8972b905 100644 --- a/packages/core/src/manifest.ts +++ b/packages/core/src/manifest.ts @@ -12,6 +12,14 @@ const EthereumContractSource = z.object({ startBlock: z.union([z.bigint(), z.number()]).optional(), }); +const SubstreamSource = z.object({ + package: z.object({ + moduleName: z.string(), + file: z.string(), + params: z.union([z.string(), z.array(z.union([z.string(), z.number()]))]).optional(), + }), +}); + // https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#1522-eventhandler const EventHandler = z.object({ event: z @@ -108,12 +116,21 @@ const EthereumMapping = z.object({ ), }); -// https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#152-mapping -const Mapping = EthereumMapping; - // https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#15-data-source -const DataSource = z.object({ - kind: z.string().describe('The type of data source. Possible values: ethereum/contract.'), +const EthereumDataSource = z.object({ + kind: z + .enum([ + // https://github.com/graphprotocol/graph-node/blob/79703bad55dd905cef1aa38ba9fae6ab389746e2/chain/arweave/src/data_source.rs#L21-L22 + 'arweave', + // https://github.com/graphprotocol/graph-node/blob/79703bad55dd905cef1aa38ba9fae6ab389746e2/chain/cosmos/src/data_source.rs#L20-L21 + 'cosmos', + // https://github.com/graphprotocol/graph-node/blob/79703bad55dd905cef1aa38ba9fae6ab389746e2/chain/ethereum/src/data_source.rs#L38-L39 + 'ethereum/contract', // for backwards compatibility + 'ethereum', // preferred + // https://github.com/graphprotocol/graph-node/blob/79703bad55dd905cef1aa38ba9fae6ab389746e2/chain/near/src/data_source.rs#L21-L22 + 'near', + ]) + .describe('The type of data source'), name: z .string() .describe( @@ -123,9 +140,41 @@ const DataSource = z.object({ .string() .describe('For blockchains, this describes which network the subgraph targets'), source: EthereumContractSource.describe('The source data on a blockchain such as Ethereum.'), - mapping: Mapping.describe('The mapping that defines how to ingest the data.'), + mapping: EthereumMapping.describe('The mapping that defines how to ingest the data.'), +}); + +const SubstreamMapping = z.object({ + kind: z + .literal('substreams/graph-entities') + .describe('Must be "ethereum/events" for Ethereum Events Mapping.'), + apiVersion: z + .string() + .describe( + 'Semver string of the version of the Mappings API that will be used by the mapping script.', + ), }); +const SubstreamDataSource = z.object({ + kind: z + .enum([ + // https://github.com/graphprotocol/graph-node/blob/79703bad55dd905cef1aa38ba9fae6ab389746e2/chain/substreams/src/data_source.rs#L17 + 'substreams', + ]) + .describe('The type of data source'), + name: z + .string() + .describe( + 'The name of the source data. Will be used to generate APIs in the mapping and also for self-documentation purposes.', + ), + network: z + .string() + .describe('For blockchains, this describes which network the subgraph targets'), + source: SubstreamSource.describe('The source data on a blockchain such as Ethereum.'), + mapping: SubstreamMapping.describe('The mapping that defines how to ingest the data.'), +}); + +const DataSource = z.union([EthereumDataSource, SubstreamDataSource]); + // https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#17-data-source-templates const TemplateSource = z.object({ kind: z.string().describe('The type of data source. Possible values: ethereum/contract.'), @@ -134,7 +183,7 @@ const TemplateSource = z.object({ .describe( 'The name of the source data. Will be used to generate APIs in the mapping and also for self-documentation purposes.', ), - mapping: Mapping.describe('The mapping that defines how to ingest the data.'), + mapping: EthereumMapping.describe('The mapping that defines how to ingest the data.'), }); // https://github.com/graphprotocol/graph-node/blob/master/docs/subgraph-manifest.md#18-graft-base diff --git a/packages/core/stubs/substream-subgraph-with-params.yaml b/packages/core/stubs/substream-subgraph-with-params.yaml new file mode 100644 index 000000000..876b80b71 --- /dev/null +++ b/packages/core/stubs/substream-subgraph-with-params.yaml @@ -0,0 +1,20 @@ +specVersion: 0.0.4 +description: Ethereum Contract Tracking Subgraph (powered by Substreams) +repository: https://github.com/graphprotocol/graph-tooling +schema: + file: schema.graphql +dataSources: + - kind: substreams + name: substream_test + network: mainnet + source: + package: + moduleName: graph_out + file: substreams-test-v1.0.1.spkg + params: + - a + - b + - 123 + mapping: + kind: substreams/graph-entities + apiVersion: 0.0.5 diff --git a/packages/core/stubs/substream-subgraph.yaml b/packages/core/stubs/substream-subgraph.yaml new file mode 100644 index 000000000..f8f061f5b --- /dev/null +++ b/packages/core/stubs/substream-subgraph.yaml @@ -0,0 +1,16 @@ +specVersion: 0.0.4 +description: Ethereum Contract Tracking Subgraph (powered by Substreams) +repository: https://github.com/graphprotocol/graph-tooling +schema: + file: schema.graphql +dataSources: + - kind: substreams + name: substream_test + network: mainnet + source: + package: + moduleName: graph_out + file: substreams-test-v1.0.1.spkg + mapping: + kind: substreams/graph-entities + apiVersion: 0.0.5 From dffabf7449bcf86cd13e421f6cffdf4ac7484c73 Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 13 Nov 2023 16:59:22 -0500 Subject: [PATCH 3/7] start re-writing codegen --- packages/cli/package.json | 1 + packages/cli/src/command-helpers/abi.ts | 287 +++++----- packages/cli/src/command-helpers/network.ts | 81 ++- packages/cli/src/commands/add.ts | 2 +- packages/cli/src/commands/codegen.ts | 81 +-- packages/cli/src/commands/init.ts | 496 ++++++++++-------- packages/cli/src/commands/local.ts | 350 ++++++------ packages/cli/src/compiler/index.ts | 2 +- packages/cli/src/migrations.ts | 35 +- .../cli/src/migrations/util/load-manifest.ts | 15 +- packages/cli/src/protocols/ethereum/abi.ts | 109 ++-- .../src/protocols/ethereum/type-generator.ts | 167 +++--- packages/cli/src/protocols/new-protocol.ts | 60 +++ packages/cli/src/protocols/utils.ts | 8 + packages/cli/src/type-generator.ts | 195 ++++--- packages/core/package.json | 3 +- packages/core/src/index.ts | 1 + pnpm-lock.yaml | 77 ++- 18 files changed, 1171 insertions(+), 799 deletions(-) create mode 100644 packages/cli/src/protocols/new-protocol.ts create mode 100644 packages/cli/src/protocols/utils.ts create mode 100644 packages/core/src/index.ts diff --git a/packages/cli/package.json b/packages/cli/package.json index 691bc75b8..4c95f04da 100644 --- a/packages/cli/package.json +++ b/packages/cli/package.json @@ -29,6 +29,7 @@ }, "dependencies": { "@float-capital/float-subgraph-uncrashable": "^0.0.0-alpha.4", + "@graphprotocol/graph-cli-core": "workspace:*", "@oclif/core": "2.8.6", "@oclif/plugin-autocomplete": "^2.3.6", "@oclif/plugin-not-found": "^2.4.0", diff --git a/packages/cli/src/command-helpers/abi.ts b/packages/cli/src/command-helpers/abi.ts index 9c07df230..f67185ba8 100644 --- a/packages/cli/src/command-helpers/abi.ts +++ b/packages/cli/src/command-helpers/abi.ts @@ -1,12 +1,12 @@ -import immutable from 'immutable'; -import { fetch } from '@whatwg-node/fetch'; -import ABI from '../protocols/ethereum/abi'; -import { withSpinner } from './spinner'; +import immutable from "immutable"; +import { fetch } from "@whatwg-node/fetch"; +import ABI from "../protocols/ethereum/abi"; +import withSpinner from "./spinner"; export const loadAbiFromEtherscan = async ( ABICtor: typeof ABI, network: string, - address: string, + address: string ): Promise => await withSpinner( `Fetching ABI from Etherscan`, @@ -14,22 +14,28 @@ export const loadAbiFromEtherscan = async ( `Warnings while fetching ABI from Etherscan`, async () => { const scanApiUrl = getEtherscanLikeAPIUrl(network); - const result = await fetch(`${scanApiUrl}?module=contract&action=getabi&address=${address}`); + const result = await fetch( + `${scanApiUrl}?module=contract&action=getabi&address=${address}` + ); const json = await result.json(); // Etherscan returns a JSON object that has a `status`, a `message` and // a `result` field. The `status` is '0' in case of errors and '1' in // case of success - if (json.status === '1') { - return new ABICtor('Contract', undefined, immutable.fromJS(JSON.parse(json.result))); + if (json.status === "1") { + return new ABICtor( + "Contract", + undefined, + immutable.fromJS(JSON.parse(json.result)) + ); } - throw new Error('ABI not found, try loading it from a local file'); - }, + throw new Error("ABI not found, try loading it from a local file"); + } ); export const loadStartBlockForContract = async ( network: string, - address: string, + address: string ): Promise => await withSpinner( `Fetching Start Block`, @@ -37,19 +43,19 @@ export const loadStartBlockForContract = async ( `Warnings while fetching deploy contract transaction from Etherscan`, async () => { return getStartBlockForContract(network, address); - }, + } ); export const fetchDeployContractTransactionFromEtherscan = async ( network: string, - address: string, + address: string ): Promise => { const scanApiUrl = getEtherscanLikeAPIUrl(network); const json = await fetchContractCreationHashWithRetry( `${scanApiUrl}?module=contract&action=getcontractcreation&contractaddresses=${address}`, - 5, + 5 ); - if (json.status === '1') { + if (json.status === "1") { return json.result[0].txHash; } @@ -58,14 +64,14 @@ export const fetchDeployContractTransactionFromEtherscan = async ( export const fetchContractCreationHashWithRetry = async ( url: string, - retryCount: number, + retryCount: number ): Promise => { let json; for (let i = 0; i < retryCount; i++) { try { const result = await fetch(url); json = await result.json(); - if (json.status !== '0') { + if (json.status !== "0") { return json; } } catch (error) { @@ -77,20 +83,20 @@ export const fetchContractCreationHashWithRetry = async ( export const fetchTransactionByHashFromRPC = async ( network: string, - transactionHash: string, + transactionHash: string ): Promise => { let json: any; try { const RPCURL = getPublicRPCEndpoint(network); if (!RPCURL) throw new Error(`Unable to fetch RPC URL for ${network}`); const result = await fetch(String(RPCURL), { - method: 'POST', + method: "POST", headers: { - 'Content-Type': 'application/json', + "Content-Type": "application/json", }, body: JSON.stringify({ - jsonrpc: '2.0', - method: 'eth_getTransactionByHash', + jsonrpc: "2.0", + method: "eth_getTransactionByHash", params: [transactionHash], id: 1, }), @@ -98,16 +104,19 @@ export const fetchTransactionByHashFromRPC = async ( json = await result.json(); return json; } catch (error) { - throw new Error('Failed to fetch contract creation transaction'); + throw new Error("Failed to fetch contract creation transaction"); } }; export const getStartBlockForContract = async ( network: string, - address: string, + address: string ): Promise => { try { - const transactionHash = await fetchDeployContractTransactionFromEtherscan(network, address); + const transactionHash = await fetchDeployContractTransactionFromEtherscan( + network, + address + ); const txn = await fetchTransactionByHashFromRPC(network, transactionHash); return parseInt(txn.result.blockNumber, 16); } catch (error) { @@ -118,7 +127,7 @@ export const getStartBlockForContract = async ( export const loadAbiFromBlockScout = async ( ABICtor: typeof ABI, network: string, - address: string, + address: string ) => await withSpinner( `Fetching ABI from BlockScout`, @@ -127,85 +136,89 @@ export const loadAbiFromBlockScout = async ( async () => { const result = await fetch( `https://blockscout.com/${network.replace( - '-', - '/', - )}/api?module=contract&action=getabi&address=${address}`, + "-", + "/" + )}/api?module=contract&action=getabi&address=${address}` ); const json = await result.json(); // BlockScout returns a JSON object that has a `status`, a `message` and // a `result` field. The `status` is '0' in case of errors and '1' in // case of success - if (json.status === '1') { - return new ABICtor('Contract', undefined, immutable.fromJS(JSON.parse(json.result))); + if (json.status === "1") { + return new ABICtor( + "Contract", + undefined, + immutable.fromJS(JSON.parse(json.result)) + ); } - throw new Error('ABI not found, try loading it from a local file'); - }, + throw new Error("ABI not found, try loading it from a local file"); + } ); const getEtherscanLikeAPIUrl = (network: string) => { switch (network) { - case 'mainnet': + case "mainnet": return `https://api.etherscan.io/api`; - case 'arbitrum-one': + case "arbitrum-one": return `https://api.arbiscan.io/api`; - case 'arbitrum-goerli': + case "arbitrum-goerli": return `https://api-goerli.arbiscan.io/api`; - case 'arbitrum-sepolia': + case "arbitrum-sepolia": return `https://api-sepolia.arbiscan.io/api`; - case 'bsc': + case "bsc": return `https://api.bscscan.com/api`; - case 'base-testnet': + case "base-testnet": return `https://api-goerli.basescan.org/api`; - case 'base': + case "base": return `https://api.basescan.org/api`; - case 'chapel': + case "chapel": return `https://api-testnet.bscscan.com/api`; - case 'matic': + case "matic": return `https://api.polygonscan.com/api`; - case 'mumbai': + case "mumbai": return `https://api-testnet.polygonscan.com/api`; - case 'aurora': + case "aurora": return `https://explorer.mainnet.aurora.dev/api`; - case 'aurora-testnet': + case "aurora-testnet": return `https://explorer.testnet.aurora.dev/api`; - case 'optimism-goerli': + case "optimism-goerli": return `https://api-goerli-optimistic.etherscan.io/api`; - case 'optimism': + case "optimism": return `https://api-optimistic.etherscan.io/api`; - case 'moonbeam': + case "moonbeam": return `https://api-moonbeam.moonscan.io/api`; - case 'moonriver': + case "moonriver": return `https://api-moonriver.moonscan.io/api`; - case 'mbase': + case "mbase": return `https://api-moonbase.moonscan.io/api`; - case 'avalanche': + case "avalanche": return `https://api.snowtrace.io/api`; - case 'fuji': + case "fuji": return `https://api-testnet.snowtrace.io/api`; - case 'celo': + case "celo": return `https://api.celoscan.io/api`; - case 'celo-alfajores': + case "celo-alfajores": return `https://alfajores.celoscan.io/api`; - case 'gnosis': + case "gnosis": return `https://api.gnosisscan.io/api`; - case 'fantom': + case "fantom": return `https://api.ftmscan.com/api`; - case 'fantom-testnet': + case "fantom-testnet": return `https://api-testnet.ftmscan.com/api`; - case 'zksync-era': + case "zksync-era": return `https://block-explorer-api.mainnet.zksync.io/api`; - case 'zksync-era-testnet': + case "zksync-era-testnet": return `https://block-explorer-api.testnets.zksync.dev/api`; - case 'polygon-zkevm-testnet': + case "polygon-zkevm-testnet": return `https://testnet-zkevm.polygonscan.com/api`; - case 'polygon-zkevm': + case "polygon-zkevm": return `https://zkevm.polygonscan.com/api`; - case 'sepolia': + case "sepolia": return `https://api-sepolia.etherscan.io/api`; - case 'scroll-sepolia': + case "scroll-sepolia": return `https://api-sepolia.scrollscan.dev/api`; - case 'scroll': + case "scroll": return `https://blockscout.scroll.io/api`; default: return `https://api-${network}.etherscan.io/api`; @@ -213,80 +226,80 @@ const getEtherscanLikeAPIUrl = (network: string) => { }; const getPublicRPCEndpoint = (network: string) => { switch (network) { - case 'arbitrum-goerli': - return 'https://goerli-rollup.arbitrum.io/rpc'; - case 'arbitrum-one': - return 'https://arb1.arbitrum.io/rpc'; - case 'arbitrum-sepolia': + case "arbitrum-goerli": + return "https://goerli-rollup.arbitrum.io/rpc"; + case "arbitrum-one": + return "https://arb1.arbitrum.io/rpc"; + case "arbitrum-sepolia": return `https://sepolia-rollup.arbitrum.io/rpc`; - case 'aurora': - return 'https://rpc.mainnet.aurora.dev'; - case 'aurora-testnet': - return 'https://rpc.testnet.aurora.dev'; - case 'avalanche': - return 'https://api.avax.network/ext/bc/C/rpc'; - case 'base-testnet': - return 'https://goerli.base.org'; - case 'base': - return 'https://rpc.base.org'; - case 'bsc': - return 'https://bsc-dataseed.binance.org'; - case 'celo': - return 'https://forno.celo.org'; - case 'celo-alfajores': - return 'https://alfajores-forno.celo-testnet.org'; - case 'chapel': - return 'https://rpc.chapel.dev'; - case 'clover': - return 'https://rpc.clover.finance'; - case 'fantom': - return 'https://rpcapi.fantom.network'; - case 'fantom-testnet': - return 'https://rpc.testnet.fantom.network'; - case 'fuji': - return 'https://api.avax-test.network/ext/bc/C/rpc'; - case 'fuse': - return 'https://rpc.fuse.io'; - case 'goerli': - return 'https://rpc.ankr.com/eth_goerli'; - case 'gnosis': - return 'https://safe-transaction.gnosis.io'; - case 'mainnet': - return 'https://rpc.ankr.com/eth'; - case 'matic': - return 'https://rpc-mainnet.maticvigil.com'; - case 'mbase': - return 'https://rpc.moonbase.moonbeam.network'; - case 'mumbai': - return 'https://rpc-mumbai.maticvigil.com'; - case 'moonbeam': - return 'https://rpc.api.moonbeam.network'; - case 'moonriver': - return 'https://moonriver.public.blastapi.io'; - case 'optimism': - return 'https://mainnet.optimism.io'; - case 'optimism-goerli': - return 'https://goerli.optimism.io'; - case 'poa-core': - return 'https://core.poa.network'; - case 'poa-sokol': - return 'https://sokol.poa.network'; - case 'polygon-zkevm-testnet': - return 'https://rpc.public.zkevm-test.net'; - case 'polygon-zkevm': - return 'https://zkevm-rpc.com'; - case 'rinkeby': - return 'https://rpc.ankr.com/eth_rinkeby'; - case 'zksync-era': - return 'https://mainnet.era.zksync.io'; - case 'zksync-era-testnet': - return 'https://testnet.era.zksync.dev'; - case 'sepolia': - return 'https://rpc.ankr.com/eth_sepolia'; - case 'scroll-sepolia': - return 'https://rpc.ankr.com/scroll_sepolia_testnet'; - case 'scroll': - return 'https://rpc.ankr.com/scroll'; + case "aurora": + return "https://rpc.mainnet.aurora.dev"; + case "aurora-testnet": + return "https://rpc.testnet.aurora.dev"; + case "avalanche": + return "https://api.avax.network/ext/bc/C/rpc"; + case "base-testnet": + return "https://goerli.base.org"; + case "base": + return "https://rpc.base.org"; + case "bsc": + return "https://bsc-dataseed.binance.org"; + case "celo": + return "https://forno.celo.org"; + case "celo-alfajores": + return "https://alfajores-forno.celo-testnet.org"; + case "chapel": + return "https://rpc.chapel.dev"; + case "clover": + return "https://rpc.clover.finance"; + case "fantom": + return "https://rpcapi.fantom.network"; + case "fantom-testnet": + return "https://rpc.testnet.fantom.network"; + case "fuji": + return "https://api.avax-test.network/ext/bc/C/rpc"; + case "fuse": + return "https://rpc.fuse.io"; + case "goerli": + return "https://rpc.ankr.com/eth_goerli"; + case "gnosis": + return "https://safe-transaction.gnosis.io"; + case "mainnet": + return "https://rpc.ankr.com/eth"; + case "matic": + return "https://rpc-mainnet.maticvigil.com"; + case "mbase": + return "https://rpc.moonbase.moonbeam.network"; + case "mumbai": + return "https://rpc-mumbai.maticvigil.com"; + case "moonbeam": + return "https://rpc.api.moonbeam.network"; + case "moonriver": + return "https://moonriver.public.blastapi.io"; + case "optimism": + return "https://mainnet.optimism.io"; + case "optimism-goerli": + return "https://goerli.optimism.io"; + case "poa-core": + return "https://core.poa.network"; + case "poa-sokol": + return "https://sokol.poa.network"; + case "polygon-zkevm-testnet": + return "https://rpc.public.zkevm-test.net"; + case "polygon-zkevm": + return "https://zkevm-rpc.com"; + case "rinkeby": + return "https://rpc.ankr.com/eth_rinkeby"; + case "zksync-era": + return "https://mainnet.era.zksync.io"; + case "zksync-era-testnet": + return "https://testnet.era.zksync.dev"; + case "sepolia": + return "https://rpc.ankr.com/eth_sepolia"; + case "scroll-sepolia": + return "https://rpc.ankr.com/scroll_sepolia_testnet"; + case "scroll": + return "https://rpc.ankr.com/scroll"; default: throw new Error(`Unknown network: ${network}`); } diff --git a/packages/cli/src/command-helpers/network.ts b/packages/cli/src/command-helpers/network.ts index 7f36e64ff..822e442f3 100644 --- a/packages/cli/src/command-helpers/network.ts +++ b/packages/cli/src/command-helpers/network.ts @@ -1,48 +1,62 @@ -import path from 'path'; -import { filesystem, patching } from 'gluegun'; -import yaml from 'yaml'; -import { step, withSpinner } from './spinner'; +import path from "path"; +import { filesystem, patching } from "gluegun"; +import yaml from "yaml"; +import withSpinner, { step } from "./spinner"; export const updateSubgraphNetwork = async ( manifest: any, network: string, networksFile: string, - identifierName: string, + identifierName: string ) => await withSpinner( `Update sources network`, `Failed to update sources network`, `Warnings while updating sources network`, - async spinner => { + async (spinner) => { step(spinner, `Reading networks config`); - const allNetworks = await filesystem.read(networksFile, 'json'); + const allNetworks = await filesystem.read(networksFile, "json"); const networkConfig = allNetworks[network]; // Exit if the network passed with --network does not exits in networks.json if (!networkConfig) { - throw new Error(`Network '${network}' was not found in '${networksFile}'`); + throw new Error( + `Network '${network}' was not found in '${networksFile}'` + ); } - await patching.update(manifest, content => { + await patching.update(manifest, (content) => { const subgraph = yaml.parse(content); const networkSources = Object.keys(networkConfig); - const subgraphSources = subgraph.dataSources.map((value: any) => value.name); + const subgraphSources = subgraph.dataSources.map( + (value: any) => value.name + ); // Update the dataSources network config subgraph.dataSources = subgraph.dataSources.map((source: any) => { if (!networkSources.includes(source.name)) { throw new Error( - `'${source.name}' was not found in the '${network}' configuration, please update!`, + `'${source.name}' was not found in the '${network}' configuration, please update!` ); } - if (hasChanges(identifierName, network, networkConfig[source.name], source)) { + if ( + hasChanges( + identifierName, + network, + networkConfig[source.name], + source + ) + ) { step(spinner, `Update '${source.name}' network configuration`); source.network = network; source.source = source.source.abi ? { abi: source.source.abi } : {}; Object.assign(source.source, networkConfig[source.name]); } else { - step(spinner, `Skip '${source.name}': No changes to network configuration`); + step( + spinner, + `Skip '${source.name}': No changes to network configuration` + ); } return source; @@ -57,26 +71,36 @@ export const updateSubgraphNetwork = async ( network, }))); - const unusedSources = networkSources.filter(x => !subgraphSources.includes(x)); + const unusedSources = networkSources.filter( + (x) => !subgraphSources.includes(x) + ); for (const source of unusedSources) { - step(spinner, `dataSource '${source}' from '${networksFile}' not found in ${manifest}`); + step( + spinner, + `dataSource '${source}' from '${networksFile}' not found in ${manifest}` + ); } const yaml_doc = new yaml.Document(); yaml_doc.contents = subgraph; return yaml_doc.toString(); }); - }, + } ); -export const initNetworksConfig = async (directory: string, identifierName: string) => +export const initNetworksConfig = async ( + directory: string, + identifierName: string +) => await withSpinner( `Initialize networks config`, `Failed to initialize networks config`, `Warnings while initializing networks config`, async () => { - const subgraphStr = filesystem.read(path.join(directory, 'subgraph.yaml')); + const subgraphStr = filesystem.read( + path.join(directory, "subgraph.yaml") + ); const subgraph = yaml.parse(subgraphStr!); const networks = subgraph.dataSources.reduce( @@ -89,25 +113,32 @@ export const initNetworksConfig = async (directory: string, identifierName: stri }, }, }), - {}, + {} ); filesystem.write(`${directory}/networks.json`, networks); return true; - }, + } ); // Checks if any network attribute has been changed -function hasChanges(identifierName: string, network: string, networkConfig: any, dataSource: any) { +function hasChanges( + identifierName: string, + network: string, + networkConfig: any, + dataSource: any +) { const networkChanged = dataSource.network !== network; // Return directly if the network is different if (networkChanged) return networkChanged; - const addressChanged = networkConfig[identifierName] !== dataSource.source[identifierName]; + const addressChanged = + networkConfig[identifierName] !== dataSource.source[identifierName]; - const startBlockChanged = networkConfig.startBlock !== dataSource.source.startBlock; + const startBlockChanged = + networkConfig.startBlock !== dataSource.source.startBlock; return networkChanged || addressChanged || startBlockChanged; } @@ -116,9 +147,9 @@ export async function updateNetworksFile( network: string, dataSource: any, address: string, - networksFile: string, + networksFile: string ) { - await patching.update(networksFile, config => { + await patching.update(networksFile, (config) => { if (Object.keys(config).includes(network)) { Object.assign(config[network], { [dataSource]: { address } }); } else { diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts index 1e8bffad2..702852002 100644 --- a/packages/cli/src/commands/add.ts +++ b/packages/cli/src/commands/add.ts @@ -16,7 +16,7 @@ import { writeSchema, writeTestsFiles, } from '../command-helpers/scaffold'; -import { withSpinner } from '../command-helpers/spinner'; +import withSpinner from '../command-helpers/spinner'; import Protocol from '../protocols'; import EthereumABI from '../protocols/ethereum/abi'; import Subgraph from '../subgraph'; diff --git a/packages/cli/src/commands/codegen.ts b/packages/cli/src/commands/codegen.ts index 13789465e..6b34d7346 100644 --- a/packages/cli/src/commands/codegen.ts +++ b/packages/cli/src/commands/codegen.ts @@ -1,67 +1,71 @@ -import path from 'path'; -import { Args, Command, Flags } from '@oclif/core'; -import * as DataSourcesExtractor from '../command-helpers/data-sources'; -import { assertGraphTsVersion, assertManifestApiVersion } from '../command-helpers/version'; -import debug from '../debug'; -import Protocol from '../protocols'; -import TypeGenerator from '../type-generator'; +import path from "path"; +import { Args, Command, Flags } from "@oclif/core"; +import * as DataSourcesExtractor from "../command-helpers/data-sources"; +import { + assertGraphTsVersion, + assertManifestApiVersion, +} from "../command-helpers/version"; +import debug from "../debug"; +import { loadManifest } from "../migrations/util/load-manifest"; +import Protocol from "../protocols/new-protocol"; +import TypeGenerator from "../type-generator"; -const codegenDebug = debug('graph-cli:codegen'); +const codegenDebug = debug("graph-cli:codegen"); export default class CodegenCommand extends Command { - static description = 'Generates AssemblyScript types for a subgraph.'; + static description = "Generates AssemblyScript types for a subgraph."; static args = { - 'subgraph-manifest': Args.string({ - default: 'subgraph.yaml', + "subgraph-manifest": Args.string({ + default: "subgraph.yaml", }), }; static flags = { help: Flags.help({ - char: 'h', + char: "h", }), - 'output-dir': Flags.directory({ - summary: 'Output directory for generated types.', - char: 'o', - default: 'generated/', + "output-dir": Flags.directory({ + summary: "Output directory for generated types.", + char: "o", + default: "generated/", }), - 'skip-migrations': Flags.boolean({ - summary: 'Skip subgraph migrations.', + "skip-migrations": Flags.boolean({ + summary: "Skip subgraph migrations.", }), watch: Flags.boolean({ - summary: 'Regenerate types when subgraph files change.', - char: 'w', + summary: "Regenerate types when subgraph files change.", + char: "w", }), uncrashable: Flags.boolean({ - summary: 'Generate Float Subgraph Uncrashable helper file.', - char: 'u', + summary: "Generate Float Subgraph Uncrashable helper file.", + char: "u", }), - 'uncrashable-config': Flags.file({ - summary: 'Directory for uncrashable config.', - aliases: ['uc'], + "uncrashable-config": Flags.file({ + summary: "Directory for uncrashable config.", + aliases: ["uc"], // TODO: using a default sets the value and therefore requires --uncrashable // default: 'uncrashable-config.yaml', - dependsOn: ['uncrashable'], + dependsOn: ["uncrashable"], }), }; async run() { const { - args: { 'subgraph-manifest': manifest }, + args: { "subgraph-manifest": manifest }, flags: { - 'output-dir': outputDir, - 'skip-migrations': skipMigrations, + "output-dir": outputDir, + "skip-migrations": skipMigrations, watch, uncrashable, - 'uncrashable-config': uncrashableConfig, + "uncrashable-config": uncrashableConfig, }, } = await this.parse(CodegenCommand); - codegenDebug('Initialized codegen manifest: %o', manifest); + codegenDebug("Initialized codegen manifest: %o", manifest); - let protocol; + let protocol: Protocol; try { // Checks to make sure codegen doesn't run against // older subgraphs (both apiVersion and graph-ts version). @@ -69,12 +73,15 @@ export default class CodegenCommand extends Command { // We don't want codegen to run without these conditions // because that would mean the CLI would generate code to // the wrong AssemblyScript version. - await assertManifestApiVersion(manifest, '0.0.5'); - await assertGraphTsVersion(path.dirname(manifest), '0.25.0'); - const dataSourcesAndTemplates = await DataSourcesExtractor.fromFilePath(manifest); + // TODO: rewrite these functions to utilize manfiest data + await assertManifestApiVersion(manifest, "0.0.5"); + await assertGraphTsVersion(path.dirname(manifest), "0.25.0"); + // ---- - protocol = Protocol.fromDataSources(dataSourcesAndTemplates); + const manifestData = await loadManifest(manifest); + + protocol = new Protocol(manifestData); } catch (e) { this.error(e, { exit: 1 }); } @@ -85,7 +92,7 @@ export default class CodegenCommand extends Command { skipMigrations, protocol, uncrashable, - uncrashableConfig: uncrashableConfig || 'uncrashable-config.yaml', + uncrashableConfig: uncrashableConfig || "uncrashable-config.yaml", }); // Watch working directory for file updates or additions, trigger diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index 9cc03d0ee..8db4419c0 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -1,33 +1,36 @@ -import fs from 'fs'; -import os from 'os'; -import path from 'path'; -import { filesystem, prompt, system } from 'gluegun'; -import * as toolbox from 'gluegun'; -import { Args, Command, Flags, ux } from '@oclif/core'; +import fs from "fs"; +import os from "os"; +import path from "path"; +import { filesystem, prompt, system } from "gluegun"; +import * as toolbox from "gluegun"; +import { Args, Command, Flags, ux } from "@oclif/core"; import { loadAbiFromBlockScout, loadAbiFromEtherscan, loadStartBlockForContract, -} from '../command-helpers/abi'; -import { initNetworksConfig } from '../command-helpers/network'; -import { chooseNodeUrl } from '../command-helpers/node'; -import { generateScaffold, writeScaffold } from '../command-helpers/scaffold'; -import { withSpinner } from '../command-helpers/spinner'; -import { validateStudioNetwork } from '../command-helpers/studio'; -import { getSubgraphBasename, validateSubgraphName } from '../command-helpers/subgraph'; -import Protocol, { ProtocolName } from '../protocols'; -import EthereumABI from '../protocols/ethereum/abi'; -import { abiEvents } from '../scaffold/schema'; -import { validateContract } from '../validation'; -import AddCommand from './add'; +} from "../command-helpers/abi"; +import { initNetworksConfig } from "../command-helpers/network"; +import { chooseNodeUrl } from "../command-helpers/node"; +import { generateScaffold, writeScaffold } from "../command-helpers/scaffold"; +import withSpinner from "../command-helpers/spinner"; +import { validateStudioNetwork } from "../command-helpers/studio"; +import { + getSubgraphBasename, + validateSubgraphName, +} from "../command-helpers/subgraph"; +import Protocol, { ProtocolName } from "../protocols"; +import EthereumABI from "../protocols/ethereum/abi"; +import { abiEvents } from "../scaffold/schema"; +import { validateContract } from "../validation"; +import AddCommand from "./add"; const protocolChoices = Array.from(Protocol.availableProtocols().keys()); const availableNetworks = Protocol.availableNetworks(); -const DEFAULT_EXAMPLE_SUBGRAPH = 'ethereum-gravatar'; +const DEFAULT_EXAMPLE_SUBGRAPH = "ethereum-gravatar"; export default class InitCommand extends Command { - static description = 'Creates a new subgraph with basic scaffolding.'; + static description = "Creates a new subgraph with basic scaffolding."; static args = { subgraphName: Args.string(), @@ -36,78 +39,78 @@ export default class InitCommand extends Command { static flags = { help: Flags.help({ - char: 'h', + char: "h", }), protocol: Flags.string({ options: protocolChoices, }), product: Flags.string({ - summary: 'Selects the product for which to initialize.', - options: ['subgraph-studio', 'hosted-service'], + summary: "Selects the product for which to initialize.", + options: ["subgraph-studio", "hosted-service"], }), studio: Flags.boolean({ summary: 'Shortcut for "--product subgraph-studio".', - exclusive: ['product'], + exclusive: ["product"], }), node: Flags.string({ - summary: 'Graph node for which to initialize.', - char: 'g', + summary: "Graph node for which to initialize.", + char: "g", }), - 'allow-simple-name': Flags.boolean({ - description: 'Use a subgraph name without a prefix.', + "allow-simple-name": Flags.boolean({ + description: "Use a subgraph name without a prefix.", default: false, }), - 'from-contract': Flags.string({ - description: 'Creates a scaffold based on an existing contract.', - exclusive: ['from-example'], + "from-contract": Flags.string({ + description: "Creates a scaffold based on an existing contract.", + exclusive: ["from-example"], }), - 'from-example': Flags.string({ - description: 'Creates a scaffold based on an example subgraph.', + "from-example": Flags.string({ + description: "Creates a scaffold based on an example subgraph.", // TODO: using a default sets the value and therefore requires not to have --from-contract // default: 'Contract', - exclusive: ['from-contract'], + exclusive: ["from-contract"], }), - 'contract-name': Flags.string({ - helpGroup: 'Scaffold from contract', - description: 'Name of the contract.', - dependsOn: ['from-contract'], + "contract-name": Flags.string({ + helpGroup: "Scaffold from contract", + description: "Name of the contract.", + dependsOn: ["from-contract"], }), - 'index-events': Flags.boolean({ - helpGroup: 'Scaffold from contract', - description: 'Index contract events as entities.', - dependsOn: ['from-contract'], + "index-events": Flags.boolean({ + helpGroup: "Scaffold from contract", + description: "Index contract events as entities.", + dependsOn: ["from-contract"], }), - 'skip-install': Flags.boolean({ - summary: 'Skip installing dependencies.', + "skip-install": Flags.boolean({ + summary: "Skip installing dependencies.", default: false, }), - 'start-block': Flags.string({ - helpGroup: 'Scaffold from contract', - description: 'Block number to start indexing from.', + "start-block": Flags.string({ + helpGroup: "Scaffold from contract", + description: "Block number to start indexing from.", // TODO: using a default sets the value and therefore requires --from-contract // default: '0', - dependsOn: ['from-contract'], + dependsOn: ["from-contract"], }), abi: Flags.string({ - summary: 'Path to the contract ABI', + summary: "Path to the contract ABI", // TODO: using a default sets the value and therefore requires --from-contract // default: '*Download from Etherscan*', - dependsOn: ['from-contract'], + dependsOn: ["from-contract"], }), spkg: Flags.string({ - summary: 'Path to the SPKG file', + summary: "Path to the SPKG file", }), network: Flags.string({ - summary: 'Network the contract is deployed to.', - dependsOn: ['from-contract'], + summary: "Network the contract is deployed to.", + dependsOn: ["from-contract"], options: [ - ...availableNetworks.get('ethereum')!, - ...availableNetworks.get('near')!, - ...availableNetworks.get('cosmos')!, + ...availableNetworks.get("ethereum")!, + ...availableNetworks.get("near")!, + ...availableNetworks.get("cosmos")!, ], }), }; @@ -120,15 +123,15 @@ export default class InitCommand extends Command { product, studio, node: nodeFlag, - 'allow-simple-name': allowSimpleNameFlag, - 'from-contract': fromContract, - 'contract-name': contractName, - 'from-example': fromExample, - 'index-events': indexEvents, - 'skip-install': skipInstall, + "allow-simple-name": allowSimpleNameFlag, + "from-contract": fromContract, + "contract-name": contractName, + "from-example": fromExample, + "index-events": indexEvents, + "skip-install": skipInstall, network, abi: abiPath, - 'start-block': startBlock, + "start-block": startBlock, spkg: spkgPath, }, } = await this.parse(InitCommand); @@ -142,33 +145,44 @@ export default class InitCommand extends Command { }); if (fromContract && fromExample) { - this.error('Only one of "--from-example" and "--from-contract" can be used at a time.', { - exit: 1, - }); + this.error( + 'Only one of "--from-example" and "--from-contract" can be used at a time.', + { + exit: 1, + } + ); } // Detect git - const git = system.which('git'); + const git = system.which("git"); if (!git) { - this.error('Git was not found on your system. Please install "git" so it is in $PATH.', { - exit: 1, - }); + this.error( + 'Git was not found on your system. Please install "git" so it is in $PATH.', + { + exit: 1, + } + ); } // Detect Yarn and/or NPM - const yarn = system.which('yarn'); - const npm = system.which('npm'); + const yarn = system.which("yarn"); + const npm = system.which("npm"); if (!yarn && !npm) { - this.error(`Neither Yarn nor NPM were found on your system. Please install one of them.`, { - exit: 1, - }); + this.error( + `Neither Yarn nor NPM were found on your system. Please install one of them.`, + { + exit: 1, + } + ); } const commands = { - link: yarn ? 'yarn link @graphprotocol/graph-cli' : 'npm link @graphprotocol/graph-cli', - install: yarn ? 'yarn' : 'npm install', - codegen: yarn ? 'yarn codegen' : 'npm run codegen', - deploy: yarn ? 'yarn deploy' : 'npm run deploy', + link: yarn + ? "yarn link @graphprotocol/graph-cli" + : "npm link @graphprotocol/graph-cli", + install: yarn ? "yarn" : "npm install", + codegen: yarn ? "yarn codegen" : "npm run codegen", + deploy: yarn ? "yarn deploy" : "npm run deploy", }; // If all parameters are provided from the command-line, @@ -182,7 +196,7 @@ export default class InitCommand extends Command { subgraphName, skipInstall, }, - { commands }, + { commands } ); // Exit with success return this.exit(0); @@ -193,13 +207,20 @@ export default class InitCommand extends Command { // If all parameters are provided from the command-line, // go straight to creating the subgraph from an existing contract - if (fromContract && protocol && subgraphName && directory && network && node) { + if ( + fromContract && + protocol && + subgraphName && + directory && + network && + node + ) { if (!protocolChoices.includes(protocol as ProtocolName)) { this.error( `Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join( - ', ', + ", " )}`, - { exit: 1 }, + { exit: 1 } ); } @@ -215,7 +236,7 @@ export default class InitCommand extends Command { } } else { try { - if (network === 'poa-core') { + if (network === "poa-core") { abi = await loadAbiFromBlockScout(ABI, network, fromContract); } else { abi = await loadAbiFromEtherscan(ABI, network, fromContract); @@ -245,7 +266,7 @@ export default class InitCommand extends Command { spkgPath, skipInstall, }, - { commands, addContract: false }, + { commands, addContract: false } ); // Exit with success return this.exit(0); @@ -271,7 +292,7 @@ export default class InitCommand extends Command { directory: answers.directory, skipInstall, }, - { commands }, + { commands } ); } else { // Otherwise, take the user through the interactive form @@ -322,7 +343,7 @@ export default class InitCommand extends Command { spkgPath: answers.spkgPath, skipInstall, }, - { commands, addContract: true }, + { commands, addContract: true } ); } // Exit with success @@ -340,7 +361,7 @@ async function processFromExampleInitForm( directory?: string; subgraphName?: string; allowSimpleName: boolean | undefined; - }, + } ): Promise< | { subgraphName: string; @@ -351,12 +372,12 @@ async function processFromExampleInitForm( try { const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { - type: 'input', - name: 'subgraphName', + type: "input", + name: "subgraphName", // TODO: is defaulting to studio ok? - message: () => 'Subgraph slug', + message: () => "Subgraph slug", initial: initSubgraphName, - validate: name => { + validate: (name) => { try { validateSubgraphName(name, { allowSimpleName: initAllowSimpleName, @@ -376,13 +397,15 @@ async function processFromExampleInitForm( const { directory } = await prompt.ask<{ directory: string }>([ { - type: 'input', - name: 'directory', - message: 'Directory to create the subgraph in', + type: "input", + name: "directory", + message: "Directory to create the subgraph in", initial: () => initDirectory || getSubgraphBasename(subgraphName), - validate: value => - filesystem.exists(value || initDirectory || getSubgraphBasename(subgraphName)) - ? 'Directory already exists' + validate: (value) => + filesystem.exists( + value || initDirectory || getSubgraphBasename(subgraphName) + ) + ? "Directory already exists" : true, }, ]); @@ -396,15 +419,17 @@ async function processFromExampleInitForm( } } -async function retryWithPrompt(func: () => Promise): Promise { +async function retryWithPrompt( + func: () => Promise +): Promise { for (;;) { try { return await func(); } catch (_) { const { retry } = await toolbox.prompt.ask({ - type: 'confirm', - name: 'retry', - message: 'Do you want to retry?', + type: "confirm", + name: "retry", + message: "Do you want to retry?", initial: true, }); @@ -452,7 +477,7 @@ async function processInitForm( contractName?: string; startBlock?: string; spkgPath?: string; - }, + } ): Promise< | { abi: EthereumABI; @@ -475,41 +500,41 @@ async function processInitForm( try { const { protocol } = await prompt.ask<{ protocol: ProtocolName }>({ - type: 'select', - name: 'protocol', - message: 'Protocol', + type: "select", + name: "protocol", + message: "Protocol", choices: protocolChoices, skip: protocolChoices.includes(String(initProtocol) as ProtocolName), }); const protocolInstance = new Protocol(protocol); - const isSubstreams = protocol === 'substreams'; + const isSubstreams = protocol === "substreams"; const { product } = await prompt.ask<{ - product: 'subgraph-studio' | 'hosted-service'; + product: "subgraph-studio" | "hosted-service"; }>([ { - type: 'select', - name: 'product', - message: 'Product for which to initialize', - choices: ['subgraph-studio', 'hosted-service'], + type: "select", + name: "product", + message: "Product for which to initialize", + choices: ["subgraph-studio", "hosted-service"], skip: - protocol === 'arweave' || - protocol === 'cosmos' || - protocol === 'near' || - initProduct === 'subgraph-studio' || - initProduct === 'hosted-service' || + protocol === "arweave" || + protocol === "cosmos" || + protocol === "near" || + initProduct === "subgraph-studio" || + initProduct === "hosted-service" || initStudio !== undefined || initNode !== undefined, - result: value => { + result: (value) => { if (initProduct) return initProduct; - if (initStudio) return 'subgraph-studio'; + if (initStudio) return "subgraph-studio"; // For now we only support NEAR subgraphs in the Hosted Service - if (protocol === 'near') { - return 'hosted-service'; + if (protocol === "near") { + return "hosted-service"; } - if (value == 'subgraph-studio') { + if (value == "subgraph-studio") { initAllowSimpleName = true; } @@ -520,11 +545,12 @@ async function processInitForm( const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { - type: 'input', - name: 'subgraphName', - message: () => (product == 'subgraph-studio' ? 'Subgraph slug' : 'Subgraph name'), + type: "input", + name: "subgraphName", + message: () => + product == "subgraph-studio" ? "Subgraph slug" : "Subgraph name", initial: initSubgraphName, - validate: name => { + validate: (name) => { try { validateSubgraphName(name, { allowSimpleName: initAllowSimpleName, @@ -544,27 +570,29 @@ async function processInitForm( const { directory } = await prompt.ask<{ directory: string }>([ { - type: 'input', - name: 'directory', - message: 'Directory to create the subgraph in', + type: "input", + name: "directory", + message: "Directory to create the subgraph in", initial: () => initDirectory || getSubgraphBasename(subgraphName), - validate: value => - filesystem.exists(value || initDirectory || getSubgraphBasename(subgraphName)) - ? 'Directory already exists' + validate: (value) => + filesystem.exists( + value || initDirectory || getSubgraphBasename(subgraphName) + ) + ? "Directory already exists" : true, }, ]); const { network } = await prompt.ask<{ network: string }>([ { - type: 'select', - name: 'network', + type: "select", + name: "network", message: () => `${protocolInstance.displayName()} network`, choices: availableNetworks .get(protocol as ProtocolName) // Get networks related to the chosen protocol. - ?.toArray() || ['mainnet'], + ?.toArray() || ["mainnet"], skip: initFromExample !== undefined, - result: value => { + result: (value) => { if (initNetwork) return initNetwork; return value; }, @@ -577,20 +605,25 @@ async function processInitForm( // - arweave // - cosmos { - type: 'input', - name: 'contract', + type: "input", + name: "contract", message: `Contract ${protocolInstance.getContract()?.identifierName()}`, skip: () => - initFromExample !== undefined || !protocolInstance.hasContract() || isSubstreams, + initFromExample !== undefined || + !protocolInstance.hasContract() || + isSubstreams, initial: initContract, validate: async (value: string) => { - if (initFromExample !== undefined || !protocolInstance.hasContract()) { + if ( + initFromExample !== undefined || + !protocolInstance.hasContract() + ) { return true; } const protocolContract = protocolInstance.getContract(); if (!protocolContract) { - return 'Contract not found.'; + return "Contract not found."; } // Validate whether the contract is valid const { valid, error } = validateContract(value, protocolContract); @@ -606,13 +639,13 @@ async function processInitForm( // Try loading the ABI from Etherscan, if none was provided if (protocolInstance.hasABIs() && !initAbi) { - if (network === 'poa-core') { + if (network === "poa-core") { abiFromEtherscan = await retryWithPrompt(() => - loadAbiFromBlockScout(ABI, network, value), + loadAbiFromBlockScout(ABI, network, value) ); } else { abiFromEtherscan = await retryWithPrompt(() => - loadAbiFromEtherscan(ABI, network, value), + loadAbiFromEtherscan(ABI, network, value) ); } } @@ -620,7 +653,7 @@ async function processInitForm( if (!initStartBlock) { // Load startBlock for this contract const startBlock = await retryWithPrompt(() => - loadStartBlockForContract(network, value), + loadStartBlockForContract(network, value) ); if (startBlock) { initStartBlock = Number(startBlock).toString(); @@ -633,21 +666,23 @@ async function processInitForm( const { spkg } = await prompt.ask<{ spkg: string }>([ { - type: 'input', - name: 'spkg', - message: 'SPKG file (path)', + type: "input", + name: "spkg", + message: "SPKG file (path)", initial: () => initSpkgPath, skip: () => !isSubstreams || !!initSpkgPath, - validate: value => - filesystem.exists(initSpkgPath || value) ? true : 'SPKG file does not exist', + validate: (value) => + filesystem.exists(initSpkgPath || value) + ? true + : "SPKG file does not exist", }, ]); const { abi: abiFromFile } = await prompt.ask<{ abi: EthereumABI }>([ { - type: 'input', - name: 'abi', - message: 'ABI file (path)', + type: "input", + name: "abi", + message: "ABI file (path)", initial: initAbi, skip: () => !protocolInstance.hasABIs() || @@ -656,7 +691,11 @@ async function processInitForm( isSubstreams || !!initAbiPath, validate: async (value: string) => { - if (initFromExample || abiFromEtherscan || !protocolInstance.hasABIs()) { + if ( + initFromExample || + abiFromEtherscan || + !protocolInstance.hasABIs() + ) { return true; } @@ -678,7 +717,11 @@ async function processInitForm( } }, result: async (value: string) => { - if (initFromExample || abiFromEtherscan || !protocolInstance.hasABIs()) { + if ( + initFromExample || + abiFromEtherscan || + !protocolInstance.hasABIs() + ) { return null; } const ABI = protocolInstance.getABI(); @@ -701,12 +744,12 @@ async function processInitForm( const { startBlock } = await prompt.ask<{ startBlock: string }>([ { - type: 'input', - name: 'startBlock', - message: 'Start Block', - initial: initStartBlock || '0', + type: "input", + name: "startBlock", + message: "Start Block", + initial: initStartBlock || "0", skip: () => initFromExample !== undefined || isSubstreams, - validate: value => parseInt(value) >= 0, + validate: (value) => parseInt(value) >= 0, result(value) { if (initStartBlock) return initStartBlock; return value; @@ -716,20 +759,21 @@ async function processInitForm( const { contractName } = await prompt.ask<{ contractName: string }>([ { - type: 'input', - name: 'contractName', - message: 'Contract Name', - initial: initContractName || 'Contract' || isSubstreams, - skip: () => initFromExample !== undefined || !protocolInstance.hasContract(), - validate: value => value && value.length > 0, + type: "input", + name: "contractName", + message: "Contract Name", + initial: initContractName || "Contract" || isSubstreams, + skip: () => + initFromExample !== undefined || !protocolInstance.hasContract(), + validate: (value) => value && value.length > 0, }, ]); const { indexEvents } = await prompt.ask<{ indexEvents: boolean }>([ { - type: 'confirm', - name: 'indexEvents', - message: 'Index contract events as entities', + type: "confirm", + name: "indexEvents", + message: "Index contract events as entities", initial: true, skip: () => !!initIndexEvents || isSubstreams, }, @@ -740,7 +784,7 @@ async function processInitForm( protocolInstance, subgraphName, directory, - studio: product === 'subgraph-studio', + studio: product === "subgraph-studio", startBlock, fromExample: !!initFromExample, product, @@ -759,20 +803,20 @@ const loadAbiFromFile = (ABI: typeof EthereumABI, filename: string) => { const exists = filesystem.exists(filename); if (!exists) { - throw Error('File does not exist.'); - } else if (exists === 'dir') { - throw Error('Path points to a directory, not a file.'); - } else if (exists === 'other') { - throw Error('Not sure what this path points to.'); + throw Error("File does not exist."); + } else if (exists === "dir") { + throw Error("Path points to a directory, not a file."); + } else if (exists === "other") { + throw Error("Not sure what this path points to."); } else { - return ABI.load('Contract', filename); + return ABI.load("Contract", filename); } }; function revalidateSubgraphName( this: InitCommand, subgraphName: string, - { allowSimpleName }: { allowSimpleName: boolean | undefined }, + { allowSimpleName }: { allowSimpleName: boolean | undefined } ) { // Fail if the subgraph name is invalid try { @@ -791,13 +835,13 @@ function revalidateSubgraphName( // Inspired from: https://github.com/graphprotocol/graph-tooling/issues/1450#issuecomment-1713992618 async function isInRepo() { try { - const result = await system.run('git rev-parse --is-inside-work-tree'); + const result = await system.run("git rev-parse --is-inside-work-tree"); // It seems like we are returning "true\n" instead of "true". // Don't think it is great idea to check for new line character here. // So best to just check if the result includes "true". - return result.includes('true'); + return result.includes("true"); } catch (err) { - if (err.stderr.includes('not a git repository')) { + if (err.stderr.includes("not a git repository")) { return false; } throw Error(err.stderr); @@ -812,24 +856,24 @@ const initRepository = async (directory: string) => async () => { // Remove .git dir in --from-example mode; in --from-contract, we're // starting from an empty directory - const gitDir = path.join(directory, '.git'); + const gitDir = path.join(directory, ".git"); if (filesystem.exists(gitDir)) { filesystem.remove(gitDir); } if (await isInRepo()) { - await system.run('git add --all', { cwd: directory }); + await system.run("git add --all", { cwd: directory }); await system.run('git commit -m "Initialize subgraph"', { cwd: directory, }); } else { - await system.run('git init', { cwd: directory }); - await system.run('git add --all', { cwd: directory }); + await system.run("git init", { cwd: directory }); + await system.run("git add --all", { cwd: directory }); await system.run('git commit -m "Initial commit"', { cwd: directory, }); } return true; - }, + } ); const installDependencies = async ( @@ -837,7 +881,7 @@ const installDependencies = async ( commands: { link: string; install: string; - }, + } ) => await withSpinner( `Install dependencies with ${commands.install}`, @@ -851,7 +895,7 @@ const installDependencies = async ( await system.run(commands.install, { cwd: directory }); return true; - }, + } ); const runCodegen = async (directory: string, codegenCommand: string) => @@ -862,7 +906,7 @@ const runCodegen = async (directory: string, codegenCommand: string) => async () => { await system.run(codegenCommand, { cwd: directory }); return true; - }, + } ); function printNextSteps( @@ -876,7 +920,7 @@ function printNextSteps( codegen: string; deploy: string; }; - }, + } ) { const relativeDir = path.relative(process.cwd(), directory); @@ -884,7 +928,7 @@ function printNextSteps( this.log( ` Subgraph ${subgraphName} created in ${relativeDir} -`, +` ); this.log(`Next steps: @@ -921,7 +965,7 @@ async function initSubgraphFromExample( codegen: string; deploy: string; }; - }, + } ) { // Fail if the subgraph name is invalid if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { @@ -941,22 +985,28 @@ async function initSubgraphFromExample( `Warnings while cloning example subgraph`, async () => { // Create a temporary directory - const prefix = path.join(os.tmpdir(), 'example-subgraph-'); + const prefix = path.join(os.tmpdir(), "example-subgraph-"); const tmpDir = fs.mkdtempSync(prefix); try { - await system.run(`git clone https://github.com/graphprotocol/graph-tooling ${tmpDir}`); + await system.run( + `git clone https://github.com/graphprotocol/graph-tooling ${tmpDir}` + ); // If an example is not specified, use the default one if (fromExample === undefined || fromExample === true) { fromExample = DEFAULT_EXAMPLE_SUBGRAPH; } // Legacy purposes when everything existed in examples repo - if (fromExample === 'ethereum/gravatar') { + if (fromExample === "ethereum/gravatar") { fromExample = DEFAULT_EXAMPLE_SUBGRAPH; } - const exampleSubgraphPath = path.join(tmpDir, 'examples', String(fromExample)); + const exampleSubgraphPath = path.join( + tmpDir, + "examples", + String(fromExample) + ); if (!filesystem.exists(exampleSubgraphPath)) { return { result: false, error: `Example not found: ${fromExample}` }; } @@ -966,14 +1016,14 @@ async function initSubgraphFromExample( } finally { filesystem.remove(tmpDir); } - }, + } ); if (!cloned) { this.exit(1); return; } - const networkConf = await initNetworksConfig(directory, 'address'); + const networkConf = await initNetworksConfig(directory, "address"); if (networkConf !== true) { this.exit(1); return; @@ -987,19 +1037,22 @@ async function initSubgraphFromExample( async () => { try { // Load package.json - const pkgJsonFilename = filesystem.path(directory, 'package.json'); - const pkgJson = await filesystem.read(pkgJsonFilename, 'json'); + const pkgJsonFilename = filesystem.path(directory, "package.json"); + const pkgJson = await filesystem.read(pkgJsonFilename, "json"); pkgJson.name = getSubgraphBasename(subgraphName); for (const name of Object.keys(pkgJson.scripts)) { - pkgJson.scripts[name] = pkgJson.scripts[name].replace('example', subgraphName); + pkgJson.scripts[name] = pkgJson.scripts[name].replace( + "example", + subgraphName + ); } - delete pkgJson['license']; - delete pkgJson['repository']; + delete pkgJson["license"]; + delete pkgJson["repository"]; // Remove example's cli in favor of the local one (added via `npm link`) if (process.env.GRAPH_CLI_TESTS) { - delete pkgJson['devDependencies']['@graphprotocol/graph-cli']; + delete pkgJson["devDependencies"]["@graphprotocol/graph-cli"]; } // Write package.json @@ -1009,7 +1062,7 @@ async function initSubgraphFromExample( filesystem.remove(directory); this.error(`Failed to preconfigure the subgraph: ${e}`); } - }, + } ); if (!prepared) { this.exit(1); @@ -1088,9 +1141,9 @@ async function initSubgraphFromContract( deploy: string; }; addContract: boolean; - }, + } ) { - const isSubstreams = protocolInstance.name === 'substreams'; + const isSubstreams = protocolInstance.name === "substreams"; // Fail if the subgraph name is invalid if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { @@ -1127,7 +1180,7 @@ async function initSubgraphFromContract( `Create subgraph scaffold`, `Failed to create subgraph scaffold`, `Warnings while creating subgraph scaffold`, - async spinner => { + async (spinner) => { const scaffold = await generateScaffold( { protocolInstance, @@ -1141,11 +1194,11 @@ async function initSubgraphFromContract( node, spkgPath, }, - spinner, + spinner ); await writeScaffold(scaffold, directory, spinner); return true; - }, + } ); if (scaffold !== true) { process.exitCode = 1; @@ -1205,22 +1258,25 @@ async function addAnotherContract( }: { protocolInstance: Protocol; directory: string; - }, + } ) { - const addContractAnswer = await ux.prompt('Add another contract? (y/n)', { + const addContractAnswer = await ux.prompt("Add another contract? (y/n)", { required: true, - type: 'single', + type: "single", }); - const addContractConfirmation = addContractAnswer.toLowerCase() === 'y'; + const addContractConfirmation = addContractAnswer.toLowerCase() === "y"; if (addContractConfirmation) { const ProtocolContract = protocolInstance.getContract()!; - let contract = ''; + let contract = ""; for (;;) { - contract = await ux.prompt(`\nContract ${ProtocolContract.identifierName()}`, { - required: true, - }); + contract = await ux.prompt( + `\nContract ${ProtocolContract.identifierName()}`, + { + required: true, + } + ); const { valid, error } = validateContract(contract, ProtocolContract); if (valid) { break; @@ -1228,9 +1284,9 @@ async function addAnotherContract( this.log(`✖ ${error}`); } - const contractName = await ux.prompt('\nContract Name', { + const contractName = await ux.prompt("\nContract Name", { required: true, - default: 'Contract', + default: "Contract", }); // Get the cwd before process.chdir in order to switch back in the end of command execution @@ -1241,7 +1297,7 @@ async function addAnotherContract( process.chdir(directory); } - const commandLine = [contract, '--contract-name', contractName]; + const commandLine = [contract, "--contract-name", contractName]; await AddCommand.run(commandLine); } catch (e) { diff --git a/packages/cli/src/commands/local.ts b/packages/cli/src/commands/local.ts index fcacc81f4..21c0fbc42 100644 --- a/packages/cli/src/commands/local.ts +++ b/packages/cli/src/commands/local.ts @@ -1,104 +1,107 @@ -import { ChildProcess, spawn } from 'child_process'; -import http from 'http'; -import net from 'net'; -import path from 'path'; -import compose from 'docker-compose'; -import { filesystem, patching } from 'gluegun'; -import stripAnsi from 'strip-ansi'; -import tmp from 'tmp-promise'; -import { Args, Command, Flags } from '@oclif/core'; -import { step, withSpinner } from '../command-helpers/spinner'; +import { ChildProcess, spawn } from "child_process"; +import http from "http"; +import net from "net"; +import path from "path"; +import compose from "docker-compose"; +import { filesystem, patching } from "gluegun"; +import stripAnsi from "strip-ansi"; +import tmp from "tmp-promise"; +import { Args, Command, Flags } from "@oclif/core"; +import withSpinner, { step } from "../command-helpers/spinner"; // Clean up temporary files even when an uncaught exception occurs tmp.setGracefulCleanup(); export default class LocalCommand extends Command { static description = - 'Runs local tests against a Graph Node environment (using Ganache by default).'; + "Runs local tests against a Graph Node environment (using Ganache by default)."; static args = { - 'local-command': Args.string({ + "local-command": Args.string({ required: true, }), }; static flags = { help: Flags.help({ - char: 'h', + char: "h", }), - 'node-logs': Flags.boolean({ - summary: 'Print the Graph Node logs.', + "node-logs": Flags.boolean({ + summary: "Print the Graph Node logs.", }), - 'ethereum-logs': Flags.boolean({ - summary: 'Print the Ethereum logs.', + "ethereum-logs": Flags.boolean({ + summary: "Print the Ethereum logs.", }), - 'compose-file': Flags.file({ - summary: 'Custom Docker Compose file for additional services.', + "compose-file": Flags.file({ + summary: "Custom Docker Compose file for additional services.", }), - 'node-image': Flags.string({ - summary: 'Custom Graph Node image to test against.', - default: 'graphprotocol/graph-node:latest', + "node-image": Flags.string({ + summary: "Custom Graph Node image to test against.", + default: "graphprotocol/graph-node:latest", }), - 'standalone-node': Flags.string({ - summary: 'Use a standalone Graph Node outside Docker Compose.', + "standalone-node": Flags.string({ + summary: "Use a standalone Graph Node outside Docker Compose.", }), - 'standalone-node-args': Flags.string({ - summary: 'Custom arguments to be passed to the standalone Graph Node.', - dependsOn: ['standalone-node'], + "standalone-node-args": Flags.string({ + summary: "Custom arguments to be passed to the standalone Graph Node.", + dependsOn: ["standalone-node"], }), - 'skip-wait-for-ipfs': Flags.boolean({ + "skip-wait-for-ipfs": Flags.boolean({ summary: "Don't wait for IPFS to be up at localhost:15001", }), - 'skip-wait-for-ethereum': Flags.boolean({ + "skip-wait-for-ethereum": Flags.boolean({ summary: "Don't wait for Ethereum to be up at localhost:18545", }), // TODO: Remove in next major release - 'skip-wait-for-etherium': Flags.boolean({ + "skip-wait-for-etherium": Flags.boolean({ summary: "Don't wait for Ethereum to be up at localhost:18545", deprecated: { - message: 'Use --skip-wait-for-ethereum instead', + message: "Use --skip-wait-for-ethereum instead", }, }), - 'skip-wait-for-postgres': Flags.boolean({ + "skip-wait-for-postgres": Flags.boolean({ summary: "Don't wait for Postgres to be up at localhost:15432", }), timeout: Flags.integer({ - summary: 'Time to wait for service containers in milliseconds.', + summary: "Time to wait for service containers in milliseconds.", default: 120_000, }), }; async run() { const { - args: { 'local-command': testCommand }, + args: { "local-command": testCommand }, flags: { - 'compose-file': composeFileFlag, - 'ethereum-logs': ethereumLogsFlag, - 'node-image': nodeImage, - 'node-logs': nodeLogsFlag, - 'skip-wait-for-etherium': skipWaitForEthereumTypo, - 'skip-wait-for-ethereum': skipWaitForEthereumGood, - 'skip-wait-for-ipfs': skipWaitForIpfs, - 'skip-wait-for-postgres': skipWaitForPostgres, - 'standalone-node': standaloneNode, - 'standalone-node-args': standaloneNodeArgs, + "compose-file": composeFileFlag, + "ethereum-logs": ethereumLogsFlag, + "node-image": nodeImage, + "node-logs": nodeLogsFlag, + "skip-wait-for-etherium": skipWaitForEthereumTypo, + "skip-wait-for-ethereum": skipWaitForEthereumGood, + "skip-wait-for-ipfs": skipWaitForIpfs, + "skip-wait-for-postgres": skipWaitForPostgres, + "standalone-node": standaloneNode, + "standalone-node-args": standaloneNodeArgs, timeout, }, } = await this.parse(LocalCommand); - const skipWaitForEthereum = skipWaitForEthereumTypo || skipWaitForEthereumGood; + const skipWaitForEthereum = + skipWaitForEthereumTypo || skipWaitForEthereumGood; // Obtain the Docker Compose file for services that the tests run against const composeFile = composeFileFlag || path.join( __dirname, - '..', - '..', - 'resources', - 'test', - standaloneNode ? 'docker-compose-standalone-node.yml' : 'docker-compose.yml', + "..", + "..", + "resources", + "test", + standaloneNode + ? "docker-compose-standalone-node.yml" + : "docker-compose.yml" ); if (!filesystem.exists(composeFile)) { @@ -107,7 +110,7 @@ export default class LocalCommand extends Command { // Create temporary directory to operate in const { path: tempdir } = await tmp.dir({ - prefix: 'graph-test', + prefix: "graph-test", unsafeCleanup: true, }); try { @@ -143,14 +146,21 @@ export default class LocalCommand extends Command { const nodeOutputChunks: Buffer[] = []; if (standaloneNode) { try { - nodeProcess = await startGraphNode(standaloneNode, standaloneNodeArgs, nodeOutputChunks); + nodeProcess = await startGraphNode( + standaloneNode, + standaloneNodeArgs, + nodeOutputChunks + ); } catch (e) { await stopTestEnvironment(tempdir); - let errorMessage = '\n'; - errorMessage += ' Graph Node'; - errorMessage += ' ----------'; - errorMessage += indent(' ', Buffer.concat(nodeOutputChunks).toString('utf-8')); - errorMessage += '\n'; + let errorMessage = "\n"; + errorMessage += " Graph Node"; + errorMessage += " ----------"; + errorMessage += indent( + " ", + Buffer.concat(nodeOutputChunks).toString("utf-8") + ); + errorMessage += "\n"; this.error(errorMessage, { exit: 1 }); } } @@ -160,14 +170,14 @@ export default class LocalCommand extends Command { await waitForGraphNode(timeout); } catch (e) { await stopTestEnvironment(tempdir); - let errorMessage = '\n'; - errorMessage += ' Graph Node'; - errorMessage += ' ----------'; + let errorMessage = "\n"; + errorMessage += " Graph Node"; + errorMessage += " ----------"; errorMessage += indent( - ' ', - await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks), + " ", + await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks) ); - errorMessage += '\n'; + errorMessage += "\n"; this.error(errorMessage, { exit: 1 }); } @@ -184,9 +194,9 @@ export default class LocalCommand extends Command { } if (result.exitCode == 0) { - this.log('✔ Tests passed'); + this.log("✔ Tests passed"); } else { - this.log('✖ Tests failed'); + this.log("✖ Tests failed"); } // Capture logs @@ -194,7 +204,9 @@ export default class LocalCommand extends Command { nodeLogsFlag || result.exitCode !== 0 ? await collectGraphNodeLogs(tempdir, standaloneNode, nodeOutputChunks) : undefined; - const ethereumLogs = ethereumLogsFlag ? await collectEthereumLogs(tempdir) : undefined; + const ethereumLogs = ethereumLogsFlag + ? await collectEthereumLogs(tempdir) + : undefined; // Bring down the test environment try { @@ -204,27 +216,27 @@ export default class LocalCommand extends Command { } if (nodeLogs) { - this.log(''); - this.log(' Graph node'); - this.log(' ----------'); - this.log(''); - this.log(indent(' ', nodeLogs)); + this.log(""); + this.log(" Graph node"); + this.log(" ----------"); + this.log(""); + this.log(indent(" ", nodeLogs)); } if (ethereumLogs) { - this.log(''); - this.log(' Ethereum'); - this.log(' --------'); - this.log(''); - this.log(indent(' ', ethereumLogs)); + this.log(""); + this.log(" Ethereum"); + this.log(" --------"); + this.log(""); + this.log(indent(" ", ethereumLogs)); } // Always print the test output - this.log(''); - this.log(' Output'); - this.log(' ------'); - this.log(''); - this.log(indent(' ', result.output)); + this.log(""); + this.log(" Output"); + this.log(" ------"); + this.log(""); + this.log(indent(" ", result.output)); // Propagate the exit code from the test run this.exit(result.exitCode); @@ -236,29 +248,41 @@ export default class LocalCommand extends Command { */ const indent = (indentation: string, str: string) => str - .split('\n') - .map(s => `${indentation}${s}`) + .split("\n") + .map((s) => `${indentation}${s}`) // Remove whitespace from empty lines - .map(s => s.replace(/^\s+$/g, '')) - .join('\n'); + .map((s) => s.replace(/^\s+$/g, "")) + .join("\n"); -const configureTestEnvironment = async (tempdir: string, composeFile: string, nodeImage: string) => +const configureTestEnvironment = async ( + tempdir: string, + composeFile: string, + nodeImage: string +) => await withSpinner( `Configure test environment`, `Failed to configure test environment`, `Warnings configuring test environment`, async () => { // Temporary compose file - const tempComposeFile = path.join(tempdir, 'compose', 'docker-compose.yml'); + const tempComposeFile = path.join( + tempdir, + "compose", + "docker-compose.yml" + ); // Copy the compose file to the temporary directory filesystem.copy(composeFile, tempComposeFile); // Substitute the graph-node image with the custom one, if appropriate if (nodeImage) { - await patching.replace(tempComposeFile, 'graphprotocol/graph-node:latest', nodeImage); + await patching.replace( + tempComposeFile, + "graphprotocol/graph-node:latest", + nodeImage + ); } - }, + } ); const waitFor = async (timeout: number, testFn: () => void) => { @@ -288,12 +312,12 @@ const startTestEnvironment = async (tempdir: string) => `Start test environment`, `Failed to start test environment`, `Warnings starting test environment`, - async _spinner => { + async (_spinner) => { // Bring up the test environment await compose.upAll({ - cwd: path.join(tempdir, 'compose'), + cwd: path.join(tempdir, "compose"), }); - }, + } ); const waitForTestEnvironment = async ({ @@ -311,67 +335,71 @@ const waitForTestEnvironment = async ({ `Wait for test environment`, `Failed to wait for test environment`, `Warnings waiting for test environment`, - async spinner => { + async (spinner) => { // Wait 10s for IPFS (if desired) if (skipWaitForIpfs) { - step(spinner, 'Skip waiting for IPFS'); + step(spinner, "Skip waiting for IPFS"); } else { await waitFor( timeout, async () => new Promise((resolve, reject) => { http - .get('http://localhost:15001/api/v0/version', () => { + .get("http://localhost:15001/api/v0/version", () => { resolve(); }) - .on('error', e => { + .on("error", (e) => { reject(new Error(`Could not connect to IPFS: ${e}`)); }); - }), + }) ); - step(spinner, 'IPFS is up'); + step(spinner, "IPFS is up"); } // Wait 10s for Ethereum (if desired) if (skipWaitForEthereum) { - step(spinner, 'Skip waiting for Ethereum'); + step(spinner, "Skip waiting for Ethereum"); } else { await waitFor( timeout, async () => new Promise((resolve, reject) => { http - .get('http://localhost:18545', () => { + .get("http://localhost:18545", () => { resolve(); }) - .on('error', e => { + .on("error", (e) => { reject(new Error(`Could not connect to Ethereum: ${e}`)); }); - }), + }) ); - step(spinner, 'Ethereum is up'); + step(spinner, "Ethereum is up"); } // Wait 10s for Postgres (if desired) if (skipWaitForPostgres) { - step(spinner, 'Skip waiting for Postgres'); + step(spinner, "Skip waiting for Postgres"); } else { await waitFor( timeout, async () => new Promise((resolve, reject) => { try { - const socket = net.connect(15_432, 'localhost', () => resolve()); - socket.on('error', e => reject(new Error(`Could not connect to Postgres: ${e}`))); + const socket = net.connect(15_432, "localhost", () => + resolve() + ); + socket.on("error", (e) => + reject(new Error(`Could not connect to Postgres: ${e}`)) + ); socket.end(); } catch (e) { reject(new Error(`Could not connect to Postgres: ${e}`)); } - }), + }) ); - step(spinner, 'Postgres is up'); + step(spinner, "Postgres is up"); } - }, + } ); const stopTestEnvironment = async (tempdir: string) => @@ -383,50 +411,52 @@ const stopTestEnvironment = async (tempdir: string) => // Our containers do not respond quickly to the SIGTERM which `down` tries before timing out // and killing them, so speed things up by sending a SIGKILL right away. try { - await compose.kill({ cwd: path.join(tempdir, 'compose') }); + await compose.kill({ cwd: path.join(tempdir, "compose") }); } catch (e) { // Do nothing, we will just try to run 'down' // to bring down the environment } - await compose.down({ cwd: path.join(tempdir, 'compose') }); - }, + await compose.down({ cwd: path.join(tempdir, "compose") }); + } ); const startGraphNode = async ( standaloneNode: string, standaloneNodeArgs: string | undefined, - nodeOutputChunks: Buffer[], + nodeOutputChunks: Buffer[] ): Promise => await withSpinner( `Start Graph node`, `Failed to start Graph node`, `Warnings starting Graph node`, - async spinner => { + async (spinner) => { const defaultArgs = [ - '--ipfs', - 'localhost:15001', - '--postgres-url', - 'postgresql://graph:let-me-in@localhost:15432/graph', - '--ethereum-rpc', - 'test:http://localhost:18545', - '--http-port', - '18000', - '--ws-port', - '18001', - '--admin-port', - '18020', - '--index-node-port', - '18030', - '--metrics-port', - '18040', + "--ipfs", + "localhost:15001", + "--postgres-url", + "postgresql://graph:let-me-in@localhost:15432/graph", + "--ethereum-rpc", + "test:http://localhost:18545", + "--http-port", + "18000", + "--ws-port", + "18001", + "--admin-port", + "18020", + "--index-node-port", + "18030", + "--metrics-port", + "18040", ]; const defaultEnv = { - GRAPH_LOG: 'debug', - GRAPH_MAX_API_VERSION: '0.0.5', + GRAPH_LOG: "debug", + GRAPH_MAX_API_VERSION: "0.0.5", }; - const args = standaloneNodeArgs ? standaloneNodeArgs.split(' ') : defaultArgs; + const args = standaloneNodeArgs + ? standaloneNodeArgs.split(" ") + : defaultArgs; const env = { ...defaultEnv, ...process.env }; const nodeProcess = spawn(standaloneNode, args, { @@ -434,17 +464,21 @@ const startGraphNode = async ( env, }); - step(spinner, 'Graph node:', String(nodeProcess.spawnargs.join(' '))); + step(spinner, "Graph node:", String(nodeProcess.spawnargs.join(" "))); - nodeProcess.stdout.on('data', data => nodeOutputChunks.push(Buffer.from(data))); - nodeProcess.stderr.on('data', data => nodeOutputChunks.push(Buffer.from(data))); - nodeProcess.on('error', e => { - nodeOutputChunks.push(Buffer.from(String(e), 'utf-8')); + nodeProcess.stdout.on("data", (data) => + nodeOutputChunks.push(Buffer.from(data)) + ); + nodeProcess.stderr.on("data", (data) => + nodeOutputChunks.push(Buffer.from(data)) + ); + nodeProcess.on("error", (e) => { + nodeOutputChunks.push(Buffer.from(String(e), "utf-8")); }); // Return the node child process return nodeProcess; - }, + } ); const waitForGraphNode = async (timeout: number) => @@ -458,11 +492,11 @@ const waitForGraphNode = async (timeout: number) => async () => new Promise((resolve, reject) => { http - .get('http://localhost:18000', { timeout }, () => resolve()) - .on('error', e => reject(e)); - }), + .get("http://localhost:18000", { timeout }, () => resolve()) + .on("error", (e) => reject(e)); + }) ); - }, + } ); const stopGraphNode = async (nodeProcess: ChildProcess) => @@ -472,32 +506,32 @@ const stopGraphNode = async (nodeProcess: ChildProcess) => `Warnings stopping Graph node`, async () => { nodeProcess.kill(9); - }, + } ); const collectGraphNodeLogs = async ( tempdir: string, standaloneNode: string | undefined, - nodeOutputChunks: Buffer[], + nodeOutputChunks: Buffer[] ) => { if (standaloneNode) { // Pull the logs from the captured output - return stripAnsi(Buffer.concat(nodeOutputChunks).toString('utf-8')); + return stripAnsi(Buffer.concat(nodeOutputChunks).toString("utf-8")); } // Pull the logs from docker compose - const logs = await compose.logs('graph-node', { + const logs = await compose.logs("graph-node", { follow: false, - cwd: path.join(tempdir, 'compose'), + cwd: path.join(tempdir, "compose"), }); - return stripAnsi(logs.out.trim()).replace(/graph-node_1 {2}\| /g, ''); + return stripAnsi(logs.out.trim()).replace(/graph-node_1 {2}\| /g, ""); }; const collectEthereumLogs = async (tempdir: string) => { - const logs = await compose.logs('ethereum', { + const logs = await compose.logs("ethereum", { follow: false, - cwd: path.join(tempdir, 'compose'), + cwd: path.join(tempdir, "compose"), }); - return stripAnsi(logs.out.trim()).replace(/ethereum_1 {2}\| /g, ''); + return stripAnsi(logs.out.trim()).replace(/ethereum_1 {2}\| /g, ""); }; const runTests = async (testCommand: string) => @@ -506,16 +540,16 @@ const runTests = async (testCommand: string) => `Failed to run tests`, `Warnings running tests`, async () => - new Promise(resolve => { + new Promise((resolve) => { const output: Buffer[] = []; const testProcess = spawn(String(testCommand), { shell: true }); - testProcess.stdout.on('data', data => output.push(Buffer.from(data))); - testProcess.stderr.on('data', data => output.push(Buffer.from(data))); - testProcess.on('close', code => { + testProcess.stdout.on("data", (data) => output.push(Buffer.from(data))); + testProcess.stderr.on("data", (data) => output.push(Buffer.from(data))); + testProcess.on("close", (code) => { resolve({ exitCode: code, - output: Buffer.concat(output).toString('utf-8'), + output: Buffer.concat(output).toString("utf-8"), }); }); - }), + }) ); diff --git a/packages/cli/src/compiler/index.ts b/packages/cli/src/compiler/index.ts index 95ed0d8b0..e523696ef 100644 --- a/packages/cli/src/compiler/index.ts +++ b/packages/cli/src/compiler/index.ts @@ -6,7 +6,7 @@ import * as toolbox from 'gluegun'; import immutable from 'immutable'; import type { IPFSHTTPClient } from 'ipfs-http-client'; import yaml from 'js-yaml'; -import { Spinner, step, withSpinner } from '../command-helpers/spinner'; +import withSpinner, { Spinner, step } from '../command-helpers/spinner'; import debug from '../debug'; import { applyMigrations } from '../migrations'; import Protocol from '../protocols'; diff --git a/packages/cli/src/migrations.ts b/packages/cli/src/migrations.ts index 30f1f8d5f..842d9cfc5 100644 --- a/packages/cli/src/migrations.ts +++ b/packages/cli/src/migrations.ts @@ -1,33 +1,36 @@ -import { step, withSpinner } from './command-helpers/spinner'; +import withSpinner, { step } from "./command-helpers/spinner"; const MIGRATIONS = [ - import('./migrations/mapping_api_version_0_0_1'), - import('./migrations/mapping_api_version_0_0_2'), - import('./migrations/mapping_api_version_0_0_3'), - import('./migrations/mapping_api_version_0_0_4'), - import('./migrations/mapping_api_version_0_0_5'), - import('./migrations/spec_version_0_0_2'), - import('./migrations/spec_version_0_0_3'), + import("./migrations/mapping_api_version_0_0_1"), + import("./migrations/mapping_api_version_0_0_2"), + import("./migrations/mapping_api_version_0_0_3"), + import("./migrations/mapping_api_version_0_0_4"), + import("./migrations/mapping_api_version_0_0_5"), + import("./migrations/spec_version_0_0_2"), + import("./migrations/spec_version_0_0_3"), ]; -export const applyMigrations = async (options: { sourceDir: string; manifestFile: string }) => +export const applyMigrations = async (options: { + sourceDir: string; + manifestFile: string; +}) => await withSpinner( `Apply migrations`, `Failed to apply migrations`, `Warnings while applying migraitons`, - async spinner => { + async (spinner) => { await MIGRATIONS.reduce(async (previousPromise, migrationImport) => { await previousPromise; const { default: migration } = await migrationImport; const skipHint = await migration.predicate(options); - if (typeof skipHint !== 'string' && skipHint) { - step(spinner, 'Apply migration:', migration.name); + if (typeof skipHint !== "string" && skipHint) { + step(spinner, "Apply migration:", migration.name); await migration.apply(options); - } else if (typeof skipHint === 'string') { - step(spinner, 'Skip migration:', `${migration.name} (${skipHint})`); + } else if (typeof skipHint === "string") { + step(spinner, "Skip migration:", `${migration.name} (${skipHint})`); } else { - step(spinner, 'Skip migration:', String(migration.name)); + step(spinner, "Skip migration:", String(migration.name)); } }, Promise.resolve()); - }, + } ); diff --git a/packages/cli/src/migrations/util/load-manifest.ts b/packages/cli/src/migrations/util/load-manifest.ts index 8bc763830..e9dfeeb99 100644 --- a/packages/cli/src/migrations/util/load-manifest.ts +++ b/packages/cli/src/migrations/util/load-manifest.ts @@ -1,10 +1,9 @@ -import path from 'path'; -import fs from 'fs-extra'; -import yaml from 'js-yaml'; +import fs from "fs-extra"; +import { parseManifest } from "@graphprotocol/graph-cli-core"; -export async function loadManifest(manifestFile: string) { - if (manifestFile.match(/.js$/)) { - return require(path.resolve(manifestFile)); - } - return yaml.safeLoad(await fs.readFile(manifestFile, 'utf-8')); +export async function loadManifest(manifestFileName: string) { + // if (manifestFile.match(/.js$/)) { + // return require(path.resolve(manifestFile)); + // } + return parseManifest(await fs.readFile(manifestFileName, "utf-8")); } diff --git a/packages/cli/src/protocols/ethereum/abi.ts b/packages/cli/src/protocols/ethereum/abi.ts index 6208f29ee..e79162951 100644 --- a/packages/cli/src/protocols/ethereum/abi.ts +++ b/packages/cli/src/protocols/ethereum/abi.ts @@ -1,50 +1,50 @@ -import path from 'path'; -import fs from 'fs-extra'; -import immutable from 'immutable'; -import AbiCodeGenerator from './codegen/abi'; +import path from "path"; +import fs from "fs-extra"; +import immutable from "immutable"; +import AbiCodeGenerator from "./codegen/abi"; const TUPLE_ARRAY_PATTERN = /^tuple\[([0-9]*)\]$/; const TUPLE_MATRIX_PATTERN = /^tuple\[([0-9]*)\]\[([0-9]*)\]$/; const buildOldSignatureParameter = (input: immutable.Map) => { - return input.get('type') === 'tuple' + return input.get("type") === "tuple" ? `(${input - .get('components') + .get("components") .map((component: any) => buildSignatureParameter(component)) - .join(',')})` - : String(input.get('type')); + .join(",")})` + : String(input.get("type")); }; const buildSignatureParameter = (input: immutable.Map) => { - if (input.get('type') === 'tuple') { - return `(${input.get('indexed') ? 'indexed ' : ''}${input - .get('components') + if (input.get("type") === "tuple") { + return `(${input.get("indexed") ? "indexed " : ""}${input + .get("components") .map((component: any) => buildSignatureParameter(component)) - .join(',')})`; + .join(",")})`; } - if (input.get('type').match(TUPLE_ARRAY_PATTERN)) { - const length = input.get('type').match(TUPLE_ARRAY_PATTERN)[1]; - return `(${input.get('indexed') ? 'indexed ' : ''}${input - .get('components') + if (input.get("type").match(TUPLE_ARRAY_PATTERN)) { + const length = input.get("type").match(TUPLE_ARRAY_PATTERN)[1]; + return `(${input.get("indexed") ? "indexed " : ""}${input + .get("components") .map((component: any) => buildSignatureParameter(component)) - .join(',')})[${length || ''}]`; + .join(",")})[${length || ""}]`; } - if (input.get('type').match(TUPLE_MATRIX_PATTERN)) { - const length1 = input.get('type').match(TUPLE_MATRIX_PATTERN)[1]; - const length2 = input.get('type').match(TUPLE_MATRIX_PATTERN)[2]; - return `(${input.get('indexed') ? 'indexed ' : ''}${input - .get('components') + if (input.get("type").match(TUPLE_MATRIX_PATTERN)) { + const length1 = input.get("type").match(TUPLE_MATRIX_PATTERN)[1]; + const length2 = input.get("type").match(TUPLE_MATRIX_PATTERN)[2]; + return `(${input.get("indexed") ? "indexed " : ""}${input + .get("components") .map((component: any) => buildSignatureParameter(component)) - .join(',')})[${length1 || ''}][${length2 || ''}]`; + .join(",")})[${length1 || ""}][${length2 || ""}]`; } - return `${input.get('indexed') ? 'indexed ' : ''}${input.get('type')}`; + return `${input.get("indexed") ? "indexed " : ""}${input.get("type")}`; }; export default class ABI { constructor( public name: string, public file: string | undefined, - public data: immutable.Collection, + public data: immutable.Collection ) { this.name = name; this.file = file; @@ -56,17 +56,17 @@ export default class ABI { } static oldEventSignature(event: immutable.Map) { - return `${event.get('name')}(${event - .get('inputs', []) + return `${event.get("name")}(${event + .get("inputs", []) .map(buildOldSignatureParameter) - .join(',')})`; + .join(",")})`; } static eventSignature(event: immutable.Map) { - return `${event.get('name')}(${event - .get('inputs', []) + return `${event.get("name")}(${event + .get("inputs", []) .map(buildSignatureParameter) - .join(',')})`; + .join(",")})`; } /** @@ -82,48 +82,64 @@ export default class ABI { * - Multiple inputs and outputs: `example(uint256,(string,bytes32)):(bool,uint256)` */ functionSignature(fn: immutable.Map) { - const inputs = fn.get('inputs', []).map(buildSignatureParameter).join(','); - const outputs = fn.get('outputs', []).map(buildSignatureParameter).join(','); - return `${fn.get('name')}(${inputs})${outputs.length > 0 ? `:(${outputs})` : ''}`; + const inputs = fn + .get("inputs", []) + .map(buildSignatureParameter) + .join(","); + const outputs = fn + .get("outputs", []) + .map(buildSignatureParameter) + .join(","); + return `${fn.get("name")}(${inputs})${ + outputs.length > 0 ? `:(${outputs})` : "" + }`; } oldEventSignatures() { return this.data - .filter((entry: any) => entry.get('type') === 'event') + .filter((entry: any) => entry.get("type") === "event") .map(ABI.oldEventSignature); } eventSignatures() { - return this.data.filter((entry: any) => entry.get('type') === 'event').map(ABI.eventSignature); + return this.data + .filter((entry: any) => entry.get("type") === "event") + .map(ABI.eventSignature); } callFunctions() { // An entry is a function if its type is not set or if it is one of // 'constructor', 'function' or 'fallback' - const functionTypes = immutable.Set(['constructor', 'function', 'fallback']); + const functionTypes = immutable.Set([ + "constructor", + "function", + "fallback", + ]); const functions = this.data.filter( - (entry: any) => !entry.has('type') || functionTypes.includes(entry.get('type')), + (entry: any) => + !entry.has("type") || functionTypes.includes(entry.get("type")) ); // A function is a call function if it is nonpayable, payable or // not constant - const mutabilityTypes = immutable.Set(['nonpayable', 'payable']); + const mutabilityTypes = immutable.Set(["nonpayable", "payable"]); return functions.filter( (entry: any) => - mutabilityTypes.includes(entry.get('stateMutability')) || entry.get('constant') === false, + mutabilityTypes.includes(entry.get("stateMutability")) || + entry.get("constant") === false ); } callFunctionSignatures() { return this.callFunctions() - .filter((entry: any) => entry.get('type') !== 'constructor') + .filter((entry: any) => entry.get("type") !== "constructor") .map((entry: any) => { - const name = entry.get('name', ''); + const name = entry.get("name", ""); const inputs = entry - .get('inputs', immutable.List()) + .get("inputs", immutable.List()) .map((input: any) => buildSignatureParameter(input)); - return `${name}(${inputs.join(',')})`; + return `${name}(${inputs.join(",")})`; }); } @@ -141,11 +157,14 @@ export default class ABI { } static load(name: string, file: string) { + // TODO: make it async const data = JSON.parse(fs.readFileSync(file).toString()); const abi = ABI.normalized(data); if (abi === null || abi === undefined) { - throw Error(`No valid ABI in file: ${path.relative(process.cwd(), file)}`); + throw Error( + `No valid ABI in file: ${path.relative(process.cwd(), file)}` + ); } return new ABI(name, file, immutable.fromJS(abi)); diff --git a/packages/cli/src/protocols/ethereum/type-generator.ts b/packages/cli/src/protocols/ethereum/type-generator.ts index ed65bbe7b..aa85b9388 100644 --- a/packages/cli/src/protocols/ethereum/type-generator.ts +++ b/packages/cli/src/protocols/ethereum/type-generator.ts @@ -1,59 +1,74 @@ -import path from 'path'; -import fs from 'fs-extra'; -import immutable from 'immutable'; -import prettier from 'prettier'; -import { GENERATED_FILE_NOTE } from '../../codegen/typescript'; -import { displayPath } from '../../command-helpers/fs'; -import { Spinner, step, withSpinner } from '../../command-helpers/spinner'; -import { TypeGeneratorOptions } from '../../type-generator'; -import ABI from './abi'; +import path from "path"; +import fs from "fs-extra"; +import immutable from "immutable"; +import prettier from "prettier"; +import { GENERATED_FILE_NOTE } from "../../codegen/typescript"; +import { displayPath } from "../../command-helpers/fs"; +import withSpinner, { Spinner, step } from "../../command-helpers/spinner"; +import { DataSource } from "../utils"; +import ABI from "./abi"; export default class EthereumTypeGenerator { - private sourceDir: TypeGeneratorOptions['sourceDir']; - private outputDir: TypeGeneratorOptions['outputDir']; + private datasource: DataSource; - constructor(options: TypeGeneratorOptions) { - this.sourceDir = options.sourceDir; - this.outputDir = options.outputDir; + constructor(datasource: DataSource) { + this.datasource = datasource; } - async loadABIs(subgraph: immutable.Map) { + async loadABIs({ sourceDir }: { sourceDir: string }) { return await withSpinner( - 'Load contract ABIs', - 'Failed to load contract ABIs', + "Load contract ABIs", + "Failed to load contract ABIs", `Warnings while loading contract ABIs`, - async spinner => { - try { - return subgraph - .get('dataSources') - .reduce( - (abis: any[], dataSource: any) => - dataSource - .getIn(['mapping', 'abis']) - .reduce( - (abis: any[], abi: any) => - abis.push( - this._loadABI(dataSource, abi.get('name'), abi.get('file'), spinner), - ), - abis, - ), - immutable.List(), + async (spinner) => { + switch (this.datasource.kind) { + case "ethereum": + case "ethereum/contract": { + const abis = this.datasource.mapping.abis; + try { + const a = await Promise.all( + abis.map((abi) => + this._loadABI({ + name: abi.name, + file: abi.file, + spinner, + sourceDir, + }) + ) + ); + + return a; + } catch (e) { + throw Error(`Failed to load contract ABIs: ${e.message}`); + } + } + default: + throw Error( + `Cannot use 'EthereumTypeGenerator' with data source kind '${this.datasource.kind}'` ); - } catch (e) { - throw Error(`Failed to load contract ABIs: ${e.message}`); } - }, + } ); } - _loadABI(dataSource: any, name: string, maybeRelativePath: string, spinner: Spinner) { + _loadABI({ + name, + file, + spinner, + sourceDir, + }: { + name: string; + file: string; + spinner: Spinner; + sourceDir: string | undefined; + }) { try { - if (this.sourceDir) { - const absolutePath = path.resolve(this.sourceDir, maybeRelativePath); + if (sourceDir) { + const absolutePath = path.resolve(sourceDir, file); step(spinner, `Load contract ABI from`, displayPath(absolutePath)); - return { dataSource, abi: ABI.load(name, absolutePath) }; + return ABI.load(name, absolutePath); } - return { dataSource, abi: ABI.load(name, maybeRelativePath) }; + return ABI.load(name, file); } catch (e) { throw Error(`Failed to load contract ABI: ${e.message}`); } @@ -64,17 +79,22 @@ export default class EthereumTypeGenerator { `Load data source template ABIs`, `Failed to load data source template ABIs`, `Warnings while loading data source template ABIs`, - async spinner => { + async (spinner) => { const abis = []; - for (const template of subgraph.get('templates', immutable.List())) { - for (const abi of template.getIn(['mapping', 'abis'])) { + for (const template of subgraph.get("templates", immutable.List())) { + for (const abi of template.getIn(["mapping", "abis"])) { abis.push( - this._loadDataSourceTemplateABI(template, abi.get('name'), abi.get('file'), spinner), + this._loadDataSourceTemplateABI( + template, + abi.get("name"), + abi.get("file"), + spinner + ) ); } } return abis; - }, + } ); } @@ -82,12 +102,16 @@ export default class EthereumTypeGenerator { template: any, name: string, maybeRelativePath: string, - spinner: Spinner, + spinner: Spinner ) { try { if (this.sourceDir) { const absolutePath = path.resolve(this.sourceDir, maybeRelativePath); - step(spinner, `Load data source template ABI from`, displayPath(absolutePath)); + step( + spinner, + `Load data source template ABI from`, + displayPath(absolutePath) + ); return { template, abi: ABI.load(name, absolutePath) }; } return { template, abi: ABI.load(name, maybeRelativePath) }; @@ -101,11 +125,11 @@ export default class EthereumTypeGenerator { `Generate types for contract ABIs`, `Failed to generate types for contract ABIs`, `Warnings while generating types for contract ABIs`, - async spinner => { + async (spinner) => { return await Promise.all( - abis.map(async abi => await this._generateTypesForABI(abi, spinner)), + abis.map(async (abi) => await this._generateTypesForABI(abi, spinner)) ); - }, + } ); } @@ -114,7 +138,7 @@ export default class EthereumTypeGenerator { step( spinner, `Generate types for contract ABI:`, - `${abi.abi.name} (${displayPath(abi.abi.file)})`, + `${abi.abi.name} (${displayPath(abi.abi.file)})` ); const codeGenerator = abi.abi.codeGenerator(); @@ -123,16 +147,16 @@ export default class EthereumTypeGenerator { GENERATED_FILE_NOTE, ...codeGenerator.generateModuleImports(), ...codeGenerator.generateTypes(), - ].join('\n'), + ].join("\n"), { - parser: 'typescript', - }, + parser: "typescript", + } ); const outputFile = path.join( this.outputDir, - abi.dataSource.get('name'), - `${abi.abi.name}.ts`, + abi.dataSource.get("name"), + `${abi.abi.name}.ts` ); step(spinner, `Write types to`, displayPath(outputFile)); await fs.mkdirs(path.dirname(outputFile)); @@ -147,11 +171,14 @@ export default class EthereumTypeGenerator { `Generate types for data source template ABIs`, `Failed to generate types for data source template ABIs`, `Warnings while generating types for data source template ABIs`, - async spinner => { + async (spinner) => { return await Promise.all( - abis.map(async abi => await this._generateTypesForDataSourceTemplateABI(abi, spinner)), + abis.map( + async (abi) => + await this._generateTypesForDataSourceTemplateABI(abi, spinner) + ) ); - }, + } ); } @@ -160,7 +187,9 @@ export default class EthereumTypeGenerator { step( spinner, `Generate types for data source template ABI:`, - `${abi.template.get('name')} > ${abi.abi.name} (${displayPath(abi.abi.file)})`, + `${abi.template.get("name")} > ${abi.abi.name} (${displayPath( + abi.abi.file + )})` ); const codeGenerator = abi.abi.codeGenerator(); @@ -169,23 +198,25 @@ export default class EthereumTypeGenerator { GENERATED_FILE_NOTE, ...codeGenerator.generateModuleImports(), ...codeGenerator.generateTypes(), - ].join('\n'), + ].join("\n"), { - parser: 'typescript', - }, + parser: "typescript", + } ); const outputFile = path.join( this.outputDir, - 'templates', - abi.template.get('name'), - `${abi.abi.name}.ts`, + "templates", + abi.template.get("name"), + `${abi.abi.name}.ts` ); step(spinner, `Write types to`, displayPath(outputFile)); await fs.mkdirs(path.dirname(outputFile)); await fs.writeFile(outputFile, code); } catch (e) { - throw Error(`Failed to generate types for data source template ABI: ${e.message}`); + throw Error( + `Failed to generate types for data source template ABI: ${e.message}` + ); } } } diff --git a/packages/cli/src/protocols/new-protocol.ts b/packages/cli/src/protocols/new-protocol.ts new file mode 100644 index 000000000..f89ee6912 --- /dev/null +++ b/packages/cli/src/protocols/new-protocol.ts @@ -0,0 +1,60 @@ +import type { Manifest } from '@graphprotocol/graph-cli-core'; +import EthereumTypeGenerator from './ethereum/type-generator'; +import { DataSource, DataSourceKind, protocolDebugger, ProtocolName } from './utils'; + +export default class Protocol { + public datasource: DataSource; + public name: ProtocolName; + public kind: DataSourceKind; + + constructor(manifest: Manifest) { + // we only support one datasource for now + this.datasource = manifest.dataSources[0]; + protocolDebugger('Initialized protocol: %o', this.datasource); + + this.kind = this.datasource.kind; + + switch (this.kind) { + case 'arweave': + this.name = 'arweave'; + break; + case 'cosmos': + this.name = 'cosmos'; + break; + case 'ethereum': + this.name = 'ethereum'; + break; + case 'near': + this.name = 'near'; + break; + case 'substreams': + this.name = 'substreams'; + break; + default: + throw new Error(`Unsupported data source kind '${this.kind}'`); + } + } + + hasAbis() { + return 'abis' in this.datasource.mapping ? this.datasource.mapping.abis.length > 0 : false; + } + + getTypeGenerator() { + switch (this.kind) { + case 'arweave': + return null; + case 'cosmos': + return null; + case 'ethereum': + case 'ethereum/contract': + return new EthereumTypeGenerator(this.datasource); + case 'near': + return null; + case 'substreams': + return null; + break; + default: + throw new Error(`Unsupported data source kind '${this.kind}'`); + } + } +} diff --git a/packages/cli/src/protocols/utils.ts b/packages/cli/src/protocols/utils.ts new file mode 100644 index 000000000..9361c4325 --- /dev/null +++ b/packages/cli/src/protocols/utils.ts @@ -0,0 +1,8 @@ +import type { Manifest } from '@graphprotocol/graph-cli-core'; +import debug from '../debug'; + +export const protocolDebugger = debug('graph-cli:protocol'); + +export type ProtocolName = 'arweave' | 'ethereum' | 'near' | 'cosmos' | 'substreams'; +export type DataSource = Manifest['dataSources'][number]; +export type DataSourceKind = Manifest['dataSources'][number]['kind']; diff --git a/packages/cli/src/type-generator.ts b/packages/cli/src/type-generator.ts index e10083cb2..343c9233c 100644 --- a/packages/cli/src/type-generator.ts +++ b/packages/cli/src/type-generator.ts @@ -1,23 +1,23 @@ -import path from 'path'; -import fs from 'fs-extra'; -import * as toolbox from 'gluegun'; -import * as graphql from 'graphql/language'; -import immutable from 'immutable'; -import prettier from 'prettier'; +import path from "path"; +import fs from "fs-extra"; +import * as toolbox from "gluegun"; +import * as graphql from "graphql/language"; +import immutable from "immutable"; +import prettier from "prettier"; // @ts-expect-error TODO: type out if necessary -import uncrashable from '@float-capital/float-subgraph-uncrashable/src/Index.bs.js'; -import DataSourceTemplateCodeGenerator from './codegen/template'; -import { GENERATED_FILE_NOTE } from './codegen/typescript'; -import { displayPath } from './command-helpers/fs'; -import { Spinner, step, withSpinner } from './command-helpers/spinner'; -import debug from './debug'; -import { applyMigrations } from './migrations'; -import Protocol from './protocols'; -import Schema from './schema'; -import Subgraph from './subgraph'; -import Watcher from './watcher'; - -const typeGenDebug = debug('graph-cli:type-generator'); +import uncrashable from "@float-capital/float-subgraph-uncrashable/src/Index.bs.js"; +import DataSourceTemplateCodeGenerator from "./codegen/template"; +import { GENERATED_FILE_NOTE } from "./codegen/typescript"; +import { displayPath } from "./command-helpers/fs"; +import withSpinner, { Spinner, step } from "./command-helpers/spinner"; +import debug from "./debug"; +import { applyMigrations } from "./migrations"; +import Protocol from "./protocols/new-protocol"; +import Schema from "./schema"; +import Subgraph from "./subgraph"; +import Watcher from "./watcher"; + +const typeGenDebug = debug("graph-cli:type-generator"); export interface TypeGeneratorOptions { sourceDir?: string; @@ -34,32 +34,30 @@ export default class TypeGenerator { private sourceDir: string; private options: TypeGeneratorOptions; private protocol: Protocol; - private protocolTypeGenerator: any; + private protocolTypeGenerator; constructor(options: TypeGeneratorOptions) { this.options = options; this.sourceDir = this.options.sourceDir || - (this.options.subgraphManifest && path.dirname(this.options.subgraphManifest)); + (this.options.subgraphManifest && + path.dirname(this.options.subgraphManifest)); this.protocol = this.options.protocol; - this.protocolTypeGenerator = this.protocol?.getTypeGenerator?.({ - sourceDir: this.sourceDir, - outputDir: this.options.outputDir, - }); + this.protocolTypeGenerator = this.protocol?.getTypeGenerator(); - process.on('uncaughtException', e => { + process.on("uncaughtException", (e) => { toolbox.print.error(`UNCAUGHT EXCEPTION: ${e}`); }); } async generateTypes() { - if (this.protocol.name === 'substreams') { - typeGenDebug.extend('generateTypes')( - 'Subgraph uses a substream datasource. Skipping code generation.', + if (this.protocol.name === "substreams") { + typeGenDebug.extend("generateTypes")( + "Subgraph uses a substream datasource. Skipping code generation." ); toolbox.print.success( - 'Subgraph uses a substream datasource. Codegeneration is not required.', + "Subgraph uses a substream datasource. Codegeneration is not required." ); process.exit(0); return; @@ -75,30 +73,36 @@ export default class TypeGenerator { const subgraph = await this.loadSubgraph(); // Not all protocols support/have ABIs. - if (this.protocol.hasABIs()) { - typeGenDebug.extend('generateTypes')('Generating types for ABIs'); - const abis = await this.protocolTypeGenerator.loadABIs(subgraph); + if (this.protocol.hasAbis()) { + typeGenDebug.extend("generateTypes")("Generating types for ABIs"); + const abis = await this.protocolTypeGenerator?.loadABIs({ + sourceDir: this.sourceDir, + }); await this.protocolTypeGenerator.generateTypesForABIs(abis); } - typeGenDebug.extend('generateTypes')('Generating types for templates'); + typeGenDebug.extend("generateTypes")("Generating types for templates"); await this.generateTypesForDataSourceTemplates(subgraph); // Not all protocols support/have ABIs. - if (this.protocol.hasABIs()) { - const templateAbis = await this.protocolTypeGenerator.loadDataSourceTemplateABIs(subgraph); - await this.protocolTypeGenerator.generateTypesForDataSourceTemplateABIs(templateAbis); + if (this.protocol.hasAbis()) { + const templateAbis = await this.protocolTypeGenerator.loadDataSourceTemplateABIs( + subgraph + ); + await this.protocolTypeGenerator.generateTypesForDataSourceTemplateABIs( + templateAbis + ); } const schema = await this.loadSchema(subgraph); - typeGenDebug.extend('generateTypes')('Generating types for schema'); + typeGenDebug.extend("generateTypes")("Generating types for schema"); await this.generateTypesForSchema(schema); - toolbox.print.success('\nTypes generated successfully\n'); + toolbox.print.success("\nTypes generated successfully\n"); if (this.options.uncrashable && this.options.uncrashableConfig) { await this.generateUncrashableEntities(schema); - toolbox.print.success('\nUncrashable Helpers generated successfully\n'); + toolbox.print.success("\nUncrashable Helpers generated successfully\n"); } return true; } catch (e) { @@ -108,16 +112,23 @@ export default class TypeGenerator { async generateUncrashableEntities(graphSchema: any) { const ast = graphql.parse(graphSchema.document); - const entityDefinitions = ast['definitions']; + const entityDefinitions = ast["definitions"]; return await withSpinner( `Generate Uncrashable Entity Helpers`, `Failed to generate Uncrashable Entity Helpers`, `Warnings while generating Uncrashable Entity Helpers`, - async spinner => { - uncrashable.run(entityDefinitions, this.options.uncrashableConfig, this.options.outputDir); - const outputFile = path.join(this.options.outputDir, 'UncrashableEntityHelpers.ts'); - step(spinner, 'Save uncrashable entities to', displayPath(outputFile)); - }, + async (spinner) => { + uncrashable.run( + entityDefinitions, + this.options.uncrashableConfig, + this.options.outputDir + ); + const outputFile = path.join( + this.options.outputDir, + "UncrashableEntityHelpers.ts" + ); + step(spinner, "Save uncrashable entities to", displayPath(outputFile)); + } ); } @@ -130,7 +141,12 @@ export default class TypeGenerator { if (quiet) { return ( this.options.subgraph || - (await Subgraph.load(this.options.subgraphManifest, subgraphLoadOptions)).result + ( + await Subgraph.load( + this.options.subgraphManifest, + subgraphLoadOptions + ) + ).result ); } const manifestPath = displayPath(this.options.subgraphManifest); @@ -139,25 +155,26 @@ export default class TypeGenerator { `Load subgraph from ${manifestPath}`, `Failed to load subgraph from ${manifestPath}`, `Warnings while loading subgraph from ${manifestPath}`, - async _spinner => { + async (_spinner) => { return ( - this.options.subgraph || Subgraph.load(this.options.subgraphManifest, subgraphLoadOptions) + this.options.subgraph || + Subgraph.load(this.options.subgraphManifest, subgraphLoadOptions) ); - }, + } ); } async loadSchema(subgraph: immutable.Map) { - const maybeRelativePath = subgraph.getIn(['schema', 'file']) as string; + const maybeRelativePath = subgraph.getIn(["schema", "file"]) as string; const absolutePath = path.resolve(this.sourceDir, maybeRelativePath); return await withSpinner( `Load GraphQL schema from ${displayPath(absolutePath)}`, `Failed to load GraphQL schema from ${displayPath(absolutePath)}`, `Warnings while loading GraphQL schema from ${displayPath(absolutePath)}`, - async _spinner => { + async (_spinner) => { const absolutePath = path.resolve(this.sourceDir, maybeRelativePath); return Schema.load(absolutePath); - }, + } ); } @@ -166,7 +183,7 @@ export default class TypeGenerator { `Generate types for GraphQL schema`, `Failed to generate types for GraphQL schema`, `Warnings while generating types for GraphQL schema`, - async spinner => { + async (spinner) => { // Generate TypeScript module from schema const codeGenerator = schema.codeGenerator(); const code = prettier.format( @@ -175,17 +192,17 @@ export default class TypeGenerator { ...codeGenerator.generateModuleImports(), ...codeGenerator.generateTypes(), ...codeGenerator.generateDerivedLoaders(), - ].join('\n'), + ].join("\n"), { - parser: 'typescript', - }, + parser: "typescript", + } ); - const outputFile = path.join(this.options.outputDir, 'schema.ts'); - step(spinner, 'Write types to', displayPath(outputFile)); + const outputFile = path.join(this.options.outputDir, "schema.ts"); + step(spinner, "Write types to", displayPath(outputFile)); await fs.mkdirs(path.dirname(outputFile)); await fs.writeFile(outputFile, code); - }, + } ); } @@ -194,35 +211,51 @@ export default class TypeGenerator { `Generate types for data source templates`, `Failed to generate types for data source templates`, `Warnings while generating types for data source templates`, - async spinner => { + async (spinner) => { // Combine the generated code for all templates const codeSegments = subgraph - .get('templates', immutable.List()) + .get("templates", immutable.List()) .reduce((codeSegments: any, template: any) => { - step(spinner, 'Generate types for data source template', String(template.get('name'))); + step( + spinner, + "Generate types for data source template", + String(template.get("name")) + ); - const codeGenerator = new DataSourceTemplateCodeGenerator(template, this.protocol); + const codeGenerator = new DataSourceTemplateCodeGenerator( + template, + this.protocol + ); // Only generate module imports once, because they are identical for // all types generated for data source templates. if (codeSegments.isEmpty()) { - codeSegments = codeSegments.concat(codeGenerator.generateModuleImports()); + codeSegments = codeSegments.concat( + codeGenerator.generateModuleImports() + ); } return codeSegments.concat(codeGenerator.generateTypes()); }, immutable.List()); if (!codeSegments.isEmpty()) { - const code = prettier.format([GENERATED_FILE_NOTE, ...codeSegments].join('\n'), { - parser: 'typescript', - }); - - const outputFile = path.join(this.options.outputDir, 'templates.ts'); - step(spinner, `Write types for templates to`, displayPath(outputFile)); + const code = prettier.format( + [GENERATED_FILE_NOTE, ...codeSegments].join("\n"), + { + parser: "typescript", + } + ); + + const outputFile = path.join(this.options.outputDir, "templates.ts"); + step( + spinner, + `Write types for templates to`, + displayPath(outputFile) + ); await fs.mkdirs(path.dirname(outputFile)); await fs.writeFile(outputFile, code); } - }, + } ); } @@ -235,17 +268,17 @@ export default class TypeGenerator { files.push(this.options.subgraphManifest); // Add the GraphQL schema to the watched files - files.push(subgraph.getIn(['schema', 'file'])); + files.push(subgraph.getIn(["schema", "file"])); // Add all file paths specified in manifest - subgraph.get('dataSources').map((dataSource: any) => { - dataSource.getIn(['mapping', 'abis']).map((abi: any) => { - files.push(abi.get('file')); + subgraph.get("dataSources").map((dataSource: any) => { + dataSource.getIn(["mapping", "abis"]).map((abi: any) => { + files.push(abi.get("file")); }); }); // Make paths absolute - return files.map(file => path.resolve(file)); + return files.map((file) => path.resolve(file)); } catch (e) { throw Error(`Failed to load subgraph: ${e.message}`); } @@ -257,8 +290,8 @@ export default class TypeGenerator { // Create watcher and generate types once and then on every change to a watched file const watcher = new Watcher({ - onReady: () => (spinner = toolbox.print.spin('Watching subgraph files')), - onTrigger: async changedFile => { + onReady: () => (spinner = toolbox.print.spin("Watching subgraph files")), + onTrigger: async (changedFile) => { if (changedFile !== undefined) { spinner.info(`File change detected: ${displayPath(changedFile)}\n`); } @@ -266,7 +299,7 @@ export default class TypeGenerator { spinner.start(); }, onCollectFiles: async () => await generator.getFilesToWatch(), - onError: error => { + onError: (error) => { spinner.stop(); toolbox.print.error(`${error}\n`); spinner.start(); @@ -274,7 +307,7 @@ export default class TypeGenerator { }); // Catch keyboard interrupt: close watcher and exit process - process.on('SIGINT', () => { + process.on("SIGINT", () => { watcher.close(); process.exit(); }); diff --git a/packages/core/package.json b/packages/core/package.json index ed87d3508..31a0c08c0 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -1,10 +1,11 @@ { "name": "@graphprotocol/graph-cli-core", "version": "0.0.1", - "description": "", + "description": "Core helpers for the Graph CLI", "author": "Saihajpreet Singh (https://saihaj.dev)", "license": "MIT", "private": true, + "main": "src/index.ts", "scripts": { "test": "vitest" }, diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts new file mode 100644 index 000000000..4a1442566 --- /dev/null +++ b/packages/core/src/index.ts @@ -0,0 +1 @@ +export { parseManifest, type Manifest } from './manifest'; diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index ac21ed674..794226fe1 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -203,7 +203,7 @@ importers: version: link:../../packages/ts '@nomicfoundation/hardhat-toolbox': specifier: ^2.0.2 - version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.4)(@types/mocha@10.0.1)(@types/node@20.6.2)(chai@4.3.7)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4) + version: 2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.10)(@types/mocha@10.0.1)(@types/node@20.6.2)(chai@4.3.10)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4) hardhat: specifier: ^2.13.1 version: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) @@ -256,6 +256,9 @@ importers: '@float-capital/float-subgraph-uncrashable': specifier: ^0.0.0-alpha.4 version: 0.0.0-alpha.6 + '@graphprotocol/graph-cli-core': + specifier: workspace:* + version: link:../core '@oclif/core': specifier: 2.8.6 version: 2.8.6(@types/node@20.6.2)(typescript@5.0.2) @@ -3198,6 +3201,25 @@ packages: - supports-color - utf-8-validate + /@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3)(chai@4.3.10)(ethers@5.7.2)(hardhat@2.13.1): + resolution: {integrity: sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ==} + peerDependencies: + '@nomiclabs/hardhat-ethers': ^2.0.0 + chai: ^4.2.0 + ethers: ^5.0.0 + hardhat: ^2.9.4 + dependencies: + '@ethersproject/abi': 5.7.0 + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.13.1) + '@types/chai-as-promised': 7.1.5 + chai: 4.3.10 + chai-as-promised: 7.1.1(chai@4.3.10) + deep-eql: 4.1.3 + ethers: 5.7.2 + hardhat: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) + ordinal: 1.0.3 + dev: true + /@nomicfoundation/hardhat-chai-matchers@1.0.6(@nomiclabs/hardhat-ethers@2.2.3)(chai@4.3.7)(ethers@5.7.2)(hardhat@2.13.1): resolution: {integrity: sha512-f5ZMNmabZeZegEfuxn/0kW+mm7+yV7VNDxLpMOMGXWFJ2l/Ct3QShujzDRF9cOkK9Ui/hbDeOWGZqyQALDXVCQ==} peerDependencies: @@ -3224,6 +3246,50 @@ packages: ethereumjs-util: 7.1.5 hardhat: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) + /@nomicfoundation/hardhat-toolbox@2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.10)(@types/mocha@10.0.1)(@types/node@20.6.2)(chai@4.3.10)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4): + resolution: {integrity: sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==} + peerDependencies: + '@ethersproject/abi': ^5.4.7 + '@ethersproject/providers': ^5.4.7 + '@nomicfoundation/hardhat-chai-matchers': ^1.0.0 + '@nomicfoundation/hardhat-network-helpers': ^1.0.0 + '@nomiclabs/hardhat-ethers': ^2.0.0 + '@nomiclabs/hardhat-etherscan': ^3.0.0 + '@typechain/ethers-v5': ^10.1.0 + '@typechain/hardhat': ^6.1.2 + '@types/chai': ^4.2.0 + '@types/mocha': '>=9.1.0' + '@types/node': '>=12.0.0' + chai: ^4.2.0 + ethers: ^5.4.7 + hardhat: ^2.11.0 + hardhat-gas-reporter: ^1.0.8 + solidity-coverage: ^0.8.1 + ts-node: '>=8.0.0' + typechain: ^8.1.0 + typescript: '>=4.5.0' + dependencies: + '@ethersproject/abi': 5.7.0 + '@ethersproject/providers': 5.7.2 + '@nomicfoundation/hardhat-chai-matchers': 1.0.6(@nomiclabs/hardhat-ethers@2.2.3)(chai@4.3.10)(ethers@5.7.2)(hardhat@2.13.1) + '@nomicfoundation/hardhat-network-helpers': 1.0.8(hardhat@2.13.1) + '@nomiclabs/hardhat-ethers': 2.2.3(ethers@5.7.2)(hardhat@2.13.1) + '@nomiclabs/hardhat-etherscan': 3.1.7(hardhat@2.13.1) + '@typechain/ethers-v5': 10.2.0(@ethersproject/abi@5.7.0)(@ethersproject/bytes@5.7.0)(@ethersproject/providers@5.7.2)(ethers@5.7.2)(typechain@8.1.1)(typescript@5.0.4) + '@typechain/hardhat': 6.1.5(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@typechain/ethers-v5@10.2.0)(ethers@5.7.2)(hardhat@2.13.1)(typechain@8.1.1) + '@types/chai': 4.3.10 + '@types/mocha': 10.0.1 + '@types/node': 20.6.2 + chai: 4.3.10 + ethers: 5.7.2 + hardhat: 2.13.1(ts-node@10.9.1)(typescript@5.0.4) + hardhat-gas-reporter: 1.0.9(hardhat@2.13.1) + solidity-coverage: 0.8.2(hardhat@2.13.1) + ts-node: 10.9.1(@types/node@20.6.2)(typescript@5.0.4) + typechain: 8.1.1(jest@29.7.0)(typescript@5.0.4) + typescript: 5.0.4 + dev: true + /@nomicfoundation/hardhat-toolbox@2.0.2(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(@nomicfoundation/hardhat-chai-matchers@1.0.6)(@nomicfoundation/hardhat-network-helpers@1.0.8)(@nomiclabs/hardhat-ethers@2.2.3)(@nomiclabs/hardhat-etherscan@3.1.7)(@typechain/ethers-v5@10.2.0)(@typechain/hardhat@6.1.5)(@types/chai@4.3.4)(@types/mocha@10.0.1)(@types/node@20.6.2)(chai@4.3.7)(ethers@5.7.2)(hardhat-gas-reporter@1.0.9)(hardhat@2.13.1)(solidity-coverage@0.8.2)(ts-node@10.9.1)(typechain@8.1.1)(typescript@5.0.4): resolution: {integrity: sha512-vnN1AzxbvpSx9pfdRHbUzTRIXpMLPXnUlkW855VaDk6N1pwRaQ2gNzEmFAABk4lWf11E00PKwFd/q27HuwYrYg==} peerDependencies: @@ -5844,6 +5910,15 @@ packages: resolution: {integrity: sha512-eyrF0jiFpY+3drT6383f1qhkbGsLSifNAjA61IUjZjmLCWjItY6LB9ft9YhoDgwfmclB2zhu51Lc7+95b8NRAg==} dev: true + /chai-as-promised@7.1.1(chai@4.3.10): + resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==} + peerDependencies: + chai: '>= 2.1.2 < 5' + dependencies: + chai: 4.3.10 + check-error: 1.0.2 + dev: true + /chai-as-promised@7.1.1(chai@4.3.7): resolution: {integrity: sha512-azL6xMoi+uxu6z4rhWQ1jbdUhOMhis2PvscD/xjLqNMkv3BPPp2JyyuTHOrf9BOosGpNQ11v6BKv/g57RXbiaA==} peerDependencies: From 8f2358004d6374c97ee319baa87a8d57b6ccc21e Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 8 Jul 2024 14:52:00 -0400 Subject: [PATCH 4/7] fix formatting --- packages/cli/src/commands/init.ts | 434 +++++++++++++----------------- 1 file changed, 191 insertions(+), 243 deletions(-) diff --git a/packages/cli/src/commands/init.ts b/packages/cli/src/commands/init.ts index a123c8376..2712d78db 100644 --- a/packages/cli/src/commands/init.ts +++ b/packages/cli/src/commands/init.ts @@ -71,10 +71,10 @@ const AVAILABLE_NETWORKS = async () => { } }; -const DEFAULT_EXAMPLE_SUBGRAPH = "ethereum-gravatar"; +const DEFAULT_EXAMPLE_SUBGRAPH = 'ethereum-gravatar'; export default class InitCommand extends Command { - static description = "Creates a new subgraph with basic scaffolding."; + static description = 'Creates a new subgraph with basic scaffolding.'; static args = { subgraphName: Args.string(), @@ -83,7 +83,7 @@ export default class InitCommand extends Command { static flags = { help: Flags.help({ - char: "h", + char: 'h', }), protocol: Flags.string({ @@ -106,11 +106,11 @@ export default class InitCommand extends Command { }, }), node: Flags.string({ - summary: "Graph node for which to initialize.", - char: "g", + summary: 'Graph node for which to initialize.', + char: 'g', }), - "allow-simple-name": Flags.boolean({ - description: "Use a subgraph name without a prefix.", + 'allow-simple-name': Flags.boolean({ + description: 'Use a subgraph name without a prefix.', default: false, deprecated: { message: @@ -118,29 +118,29 @@ export default class InitCommand extends Command { }, }), - "from-contract": Flags.string({ - description: "Creates a scaffold based on an existing contract.", - exclusive: ["from-example"], + 'from-contract': Flags.string({ + description: 'Creates a scaffold based on an existing contract.', + exclusive: ['from-example'], }), - "from-example": Flags.string({ - description: "Creates a scaffold based on an example subgraph.", + 'from-example': Flags.string({ + description: 'Creates a scaffold based on an example subgraph.', // TODO: using a default sets the value and therefore requires not to have --from-contract // default: 'Contract', - exclusive: ["from-contract"], + exclusive: ['from-contract'], }), - "contract-name": Flags.string({ - helpGroup: "Scaffold from contract", - description: "Name of the contract.", - dependsOn: ["from-contract"], + 'contract-name': Flags.string({ + helpGroup: 'Scaffold from contract', + description: 'Name of the contract.', + dependsOn: ['from-contract'], }), - "index-events": Flags.boolean({ - helpGroup: "Scaffold from contract", - description: "Index contract events as entities.", - dependsOn: ["from-contract"], + 'index-events': Flags.boolean({ + helpGroup: 'Scaffold from contract', + description: 'Index contract events as entities.', + dependsOn: ['from-contract'], }), - "skip-install": Flags.boolean({ - summary: "Skip installing dependencies.", + 'skip-install': Flags.boolean({ + summary: 'Skip installing dependencies.', default: false, }), 'skip-git': Flags.boolean({ @@ -156,17 +156,17 @@ export default class InitCommand extends Command { description: 'Block number to start indexing from.', // TODO: using a default sets the value and therefore requires --from-contract // default: '0', - dependsOn: ["from-contract"], + dependsOn: ['from-contract'], }), abi: Flags.string({ - summary: "Path to the contract ABI", + summary: 'Path to the contract ABI', // TODO: using a default sets the value and therefore requires --from-contract // default: '*Download from Etherscan*', - dependsOn: ["from-contract"], + dependsOn: ['from-contract'], }), spkg: Flags.string({ - summary: "Path to the SPKG file", + summary: 'Path to the SPKG file', }), network: Flags.string({ summary: 'Network the contract is deployed to.', @@ -215,44 +215,33 @@ export default class InitCommand extends Command { }); if (fromContract && fromExample) { - this.error( - 'Only one of "--from-example" and "--from-contract" can be used at a time.', - { - exit: 1, - } - ); + this.error('Only one of "--from-example" and "--from-contract" can be used at a time.', { + exit: 1, + }); } // Detect git - const git = system.which("git"); + const git = system.which('git'); if (!git) { - this.error( - 'Git was not found on your system. Please install "git" so it is in $PATH.', - { - exit: 1, - } - ); + this.error('Git was not found on your system. Please install "git" so it is in $PATH.', { + exit: 1, + }); } // Detect Yarn and/or NPM - const yarn = system.which("yarn"); - const npm = system.which("npm"); + const yarn = system.which('yarn'); + const npm = system.which('npm'); if (!yarn && !npm) { - this.error( - `Neither Yarn nor NPM were found on your system. Please install one of them.`, - { - exit: 1, - } - ); + this.error(`Neither Yarn nor NPM were found on your system. Please install one of them.`, { + exit: 1, + }); } const commands = { - link: yarn - ? "yarn link @graphprotocol/graph-cli" - : "npm link @graphprotocol/graph-cli", - install: yarn ? "yarn" : "npm install", - codegen: yarn ? "yarn codegen" : "npm run codegen", - deploy: yarn ? "yarn deploy" : "npm run deploy", + link: yarn ? 'yarn link @graphprotocol/graph-cli' : 'npm link @graphprotocol/graph-cli', + install: yarn ? 'yarn' : 'npm install', + codegen: yarn ? 'yarn codegen' : 'npm run codegen', + deploy: yarn ? 'yarn deploy' : 'npm run deploy', }; // If all parameters are provided from the command-line, @@ -267,7 +256,7 @@ export default class InitCommand extends Command { skipInstall, skipGit, }, - { commands } + { commands }, ); // Exit with success return this.exit(0); @@ -278,20 +267,13 @@ export default class InitCommand extends Command { // If all parameters are provided from the command-line, // go straight to creating the subgraph from an existing contract - if ( - fromContract && - protocol && - subgraphName && - directory && - network && - node - ) { + if (fromContract && protocol && subgraphName && directory && network && node) { if (!protocolChoices.includes(protocol as ProtocolName)) { this.error( `Protocol '${protocol}' is not supported, choose from these options: ${protocolChoices.join( - ", " + ', ', )}`, - { exit: 1 } + { exit: 1 }, ); } @@ -307,7 +289,7 @@ export default class InitCommand extends Command { } } else { try { - if (network === "poa-core") { + if (network === 'poa-core') { abi = await loadAbiFromBlockScout(ABI, network, fromContract); } else { abi = await loadAbiFromEtherscan(ABI, network, fromContract); @@ -336,7 +318,7 @@ export default class InitCommand extends Command { skipInstall, skipGit, }, - { commands, addContract: false } + { commands, addContract: false }, ); // Exit with success return this.exit(0); @@ -363,7 +345,7 @@ export default class InitCommand extends Command { skipInstall, skipGit, }, - { commands } + { commands }, ); } else { // Otherwise, take the user through the interactive form @@ -413,7 +395,7 @@ export default class InitCommand extends Command { skipInstall, skipGit, }, - { commands, addContract: true } + { commands, addContract: true }, ); } // Exit with success @@ -431,23 +413,23 @@ async function processFromExampleInitForm( directory?: string; subgraphName?: string; allowSimpleName: boolean | undefined; - } + }, ): Promise< | { - subgraphName: string; - directory: string; - } + subgraphName: string; + directory: string; + } | undefined > { try { const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { - type: "input", - name: "subgraphName", + type: 'input', + name: 'subgraphName', // TODO: is defaulting to studio ok? - message: () => "Subgraph slug", + message: () => 'Subgraph slug', initial: initSubgraphName, - validate: (name) => { + validate: name => { try { validateSubgraphName(name, { allowSimpleName: initAllowSimpleName, @@ -467,15 +449,13 @@ async function processFromExampleInitForm( const { directory } = await prompt.ask<{ directory: string }>([ { - type: "input", - name: "directory", - message: "Directory to create the subgraph in", + type: 'input', + name: 'directory', + message: 'Directory to create the subgraph in', initial: () => initDirectory || getSubgraphBasename(subgraphName), - validate: (value) => - filesystem.exists( - value || initDirectory || getSubgraphBasename(subgraphName) - ) - ? "Directory already exists" + validate: value => + filesystem.exists(value || initDirectory || getSubgraphBasename(subgraphName)) + ? 'Directory already exists' : true, }, ]); @@ -489,17 +469,15 @@ async function processFromExampleInitForm( } } -async function retryWithPrompt( - func: () => Promise -): Promise { - for (; ;) { +async function retryWithPrompt(func: () => Promise): Promise { + for (;;) { try { return await func(); } catch (_) { const { retry } = await toolbox.prompt.ask({ - type: "confirm", - name: "retry", - message: "Do you want to retry?", + type: 'confirm', + name: 'retry', + message: 'Do you want to retry?', initial: true, }); @@ -547,32 +525,32 @@ async function processInitForm( contractName?: string; startBlock?: string; spkgPath?: string; - } + }, ): Promise< | { - abi: EthereumABI; - protocolInstance: Protocol; - subgraphName: string; - directory: string; - studio: boolean; - product: string; - network: string; - contract: string; - indexEvents: boolean; - contractName: string; - startBlock: string; - fromExample: boolean; - spkgPath: string | undefined; - } + abi: EthereumABI; + protocolInstance: Protocol; + subgraphName: string; + directory: string; + studio: boolean; + product: string; + network: string; + contract: string; + indexEvents: boolean; + contractName: string; + startBlock: string; + fromExample: boolean; + spkgPath: string | undefined; + } | undefined > { let abiFromEtherscan: EthereumABI | undefined = undefined; try { const { protocol } = await prompt.ask<{ protocol: ProtocolName }>({ - type: "select", - name: "protocol", - message: "Protocol", + type: 'select', + name: 'protocol', + message: 'Protocol', choices: protocolChoices, skip: protocolChoices.includes(String(initProtocol) as ProtocolName), result: value => { @@ -590,30 +568,30 @@ async function processInitForm( initDebugger.extend('processInitForm')('isSubstreams: %O', isSubstreams); const { product } = await prompt.ask<{ - product: "subgraph-studio" | "hosted-service"; + product: 'subgraph-studio' | 'hosted-service'; }>([ { - type: "select", - name: "product", - message: "Product for which to initialize", - choices: ["subgraph-studio", "hosted-service"], + type: 'select', + name: 'product', + message: 'Product for which to initialize', + choices: ['subgraph-studio', 'hosted-service'], skip: - protocol === "arweave" || - protocol === "cosmos" || - protocol === "near" || - initProduct === "subgraph-studio" || - initProduct === "hosted-service" || + protocol === 'arweave' || + protocol === 'cosmos' || + protocol === 'near' || + initProduct === 'subgraph-studio' || + initProduct === 'hosted-service' || initStudio !== undefined || initNode !== undefined, - result: (value) => { + result: value => { if (initProduct) return initProduct; - if (initStudio) return "subgraph-studio"; + if (initStudio) return 'subgraph-studio'; // For now we only support NEAR subgraphs in the Hosted Service - if (protocol === "near") { - return "hosted-service"; + if (protocol === 'near') { + return 'hosted-service'; } - if (value == "subgraph-studio") { + if (value == 'subgraph-studio') { initAllowSimpleName = true; } @@ -628,12 +606,11 @@ async function processInitForm( const { subgraphName } = await prompt.ask<{ subgraphName: string }>([ { - type: "input", - name: "subgraphName", - message: () => - product == "subgraph-studio" ? "Subgraph slug" : "Subgraph name", + type: 'input', + name: 'subgraphName', + message: () => (product == 'subgraph-studio' ? 'Subgraph slug' : 'Subgraph name'), initial: initSubgraphName, - validate: (name) => { + validate: name => { try { validateSubgraphName(name, { allowSimpleName: initAllowSimpleName, @@ -653,15 +630,13 @@ async function processInitForm( const { directory } = await prompt.ask<{ directory: string }>([ { - type: "input", - name: "directory", - message: "Directory to create the subgraph in", + type: 'input', + name: 'directory', + message: 'Directory to create the subgraph in', initial: () => initDirectory || getSubgraphBasename(subgraphName), - validate: (value) => - filesystem.exists( - value || initDirectory || getSubgraphBasename(subgraphName) - ) - ? "Directory already exists" + validate: value => + filesystem.exists(value || initDirectory || getSubgraphBasename(subgraphName)) + ? 'Directory already exists' : true, }, ]); @@ -681,8 +656,8 @@ async function processInitForm( const { network } = await prompt.ask<{ network: string }>([ { - type: "select", - name: "network", + type: 'select', + name: 'network', message: () => `${protocolInstance.displayName()} network`, choices, skip: initNetwork !== undefined, @@ -703,25 +678,20 @@ async function processInitForm( // - arweave // - cosmos { - type: "input", - name: "contract", + type: 'input', + name: 'contract', message: `Contract ${protocolInstance.getContract()?.identifierName()}`, skip: () => - initFromExample !== undefined || - !protocolInstance.hasContract() || - isSubstreams, + initFromExample !== undefined || !protocolInstance.hasContract() || isSubstreams, initial: initContract, validate: async (value: string) => { - if ( - initFromExample !== undefined || - !protocolInstance.hasContract() - ) { + if (initFromExample !== undefined || !protocolInstance.hasContract()) { return true; } const protocolContract = protocolInstance.getContract(); if (!protocolContract) { - return "Contract not found."; + return 'Contract not found.'; } // Validate whether the contract is valid const { valid, error } = validateContract(value, protocolContract); @@ -737,13 +707,13 @@ async function processInitForm( // Try loading the ABI from Etherscan, if none was provided if (protocolInstance.hasABIs() && !initAbi) { - if (network === "poa-core") { + if (network === 'poa-core') { abiFromEtherscan = await retryWithPrompt(() => - loadAbiFromBlockScout(ABI, network, value) + loadAbiFromBlockScout(ABI, network, value), ); } else { abiFromEtherscan = await retryWithPrompt(() => - loadAbiFromEtherscan(ABI, network, value) + loadAbiFromEtherscan(ABI, network, value), ); } } @@ -751,7 +721,7 @@ async function processInitForm( if (!initStartBlock) { // Load startBlock for this contract const startBlock = await retryWithPrompt(() => - loadStartBlockForContract(network, value) + loadStartBlockForContract(network, value), ); if (startBlock) { initStartBlock = Number(startBlock).toString(); @@ -776,23 +746,21 @@ async function processInitForm( const { spkg } = await prompt.ask<{ spkg: string }>([ { - type: "input", - name: "spkg", - message: "SPKG file (path)", + type: 'input', + name: 'spkg', + message: 'SPKG file (path)', initial: () => initSpkgPath, skip: () => !isSubstreams || !!initSpkgPath, - validate: (value) => - filesystem.exists(initSpkgPath || value) - ? true - : "SPKG file does not exist", + validate: value => + filesystem.exists(initSpkgPath || value) ? true : 'SPKG file does not exist', }, ]); const { abi: abiFromFile } = await prompt.ask<{ abi: EthereumABI }>([ { - type: "input", - name: "abi", - message: "ABI file (path)", + type: 'input', + name: 'abi', + message: 'ABI file (path)', initial: initAbi, skip: () => !protocolInstance.hasABIs() || @@ -801,11 +769,7 @@ async function processInitForm( isSubstreams || !!initAbiPath, validate: async (value: string) => { - if ( - initFromExample || - abiFromEtherscan || - !protocolInstance.hasABIs() - ) { + if (initFromExample || abiFromEtherscan || !protocolInstance.hasABIs()) { return true; } @@ -827,11 +791,7 @@ async function processInitForm( } }, result: async (value: string) => { - if ( - initFromExample || - abiFromEtherscan || - !protocolInstance.hasABIs() - ) { + if (initFromExample || abiFromEtherscan || !protocolInstance.hasABIs()) { return null; } const ABI = protocolInstance.getABI(); @@ -854,12 +814,12 @@ async function processInitForm( const { startBlock } = await prompt.ask<{ startBlock: string }>([ { - type: "input", - name: "startBlock", - message: "Start Block", - initial: initStartBlock || "0", + type: 'input', + name: 'startBlock', + message: 'Start Block', + initial: initStartBlock || '0', skip: () => initFromExample !== undefined || isSubstreams, - validate: (value) => parseInt(value) >= 0, + validate: value => parseInt(value) >= 0, result(value) { if (initStartBlock) return initStartBlock; return value; @@ -884,9 +844,9 @@ async function processInitForm( const { indexEvents } = await prompt.ask<{ indexEvents: boolean }>([ { - type: "confirm", - name: "indexEvents", - message: "Index contract events as entities", + type: 'confirm', + name: 'indexEvents', + message: 'Index contract events as entities', initial: true, skip: () => !!initIndexEvents || isSubstreams, }, @@ -897,7 +857,7 @@ async function processInitForm( protocolInstance, subgraphName, directory, - studio: product === "subgraph-studio", + studio: product === 'subgraph-studio', startBlock, fromExample: !!initFromExample, product, @@ -916,20 +876,20 @@ const loadAbiFromFile = (ABI: typeof EthereumABI, filename: string) => { const exists = filesystem.exists(filename); if (!exists) { - throw Error("File does not exist."); - } else if (exists === "dir") { - throw Error("Path points to a directory, not a file."); - } else if (exists === "other") { - throw Error("Not sure what this path points to."); + throw Error('File does not exist.'); + } else if (exists === 'dir') { + throw Error('Path points to a directory, not a file.'); + } else if (exists === 'other') { + throw Error('Not sure what this path points to.'); } else { - return ABI.load("Contract", filename); + return ABI.load('Contract', filename); } }; function revalidateSubgraphName( this: InitCommand, subgraphName: string, - { allowSimpleName }: { allowSimpleName: boolean | undefined } + { allowSimpleName }: { allowSimpleName: boolean | undefined }, ) { // Fail if the subgraph name is invalid try { @@ -948,13 +908,13 @@ function revalidateSubgraphName( // Inspired from: https://github.com/graphprotocol/graph-tooling/issues/1450#issuecomment-1713992618 async function isInRepo() { try { - const result = await system.run("git rev-parse --is-inside-work-tree"); + const result = await system.run('git rev-parse --is-inside-work-tree'); // It seems like we are returning "true\n" instead of "true". // Don't think it is great idea to check for new line character here. // So best to just check if the result includes "true". - return result.includes("true"); + return result.includes('true'); } catch (err) { - if (err.stderr.includes("not a git repository")) { + if (err.stderr.includes('not a git repository')) { return false; } throw Error(err.stderr); @@ -969,24 +929,24 @@ const initRepository = async (directory: string) => async () => { // Remove .git dir in --from-example mode; in --from-contract, we're // starting from an empty directory - const gitDir = path.join(directory, ".git"); + const gitDir = path.join(directory, '.git'); if (filesystem.exists(gitDir)) { filesystem.remove(gitDir); } if (await isInRepo()) { - await system.run("git add --all", { cwd: directory }); + await system.run('git add --all', { cwd: directory }); await system.run('git commit -m "Initialize subgraph"', { cwd: directory, }); } else { - await system.run("git init", { cwd: directory }); - await system.run("git add --all", { cwd: directory }); + await system.run('git init', { cwd: directory }); + await system.run('git add --all', { cwd: directory }); await system.run('git commit -m "Initial commit"', { cwd: directory, }); } return true; - } + }, ); const installDependencies = async ( @@ -994,7 +954,7 @@ const installDependencies = async ( commands: { link: string; install: string; - } + }, ) => await withSpinner( `Install dependencies with ${commands.install}`, @@ -1008,7 +968,7 @@ const installDependencies = async ( await system.run(commands.install, { cwd: directory }); return true; - } + }, ); const runCodegen = async (directory: string, codegenCommand: string) => @@ -1019,7 +979,7 @@ const runCodegen = async (directory: string, codegenCommand: string) => async () => { await system.run(codegenCommand, { cwd: directory }); return true; - } + }, ); function printNextSteps( @@ -1033,7 +993,7 @@ function printNextSteps( codegen: string; deploy: string; }; - } + }, ) { const relativeDir = path.relative(process.cwd(), directory); @@ -1041,7 +1001,7 @@ function printNextSteps( this.log( ` Subgraph ${subgraphName} created in ${relativeDir} -` +`, ); this.log(`Next steps: @@ -1080,7 +1040,7 @@ async function initSubgraphFromExample( codegen: string; deploy: string; }; - } + }, ) { // Fail if the subgraph name is invalid if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { @@ -1100,28 +1060,22 @@ async function initSubgraphFromExample( `Warnings while cloning example subgraph`, async () => { // Create a temporary directory - const prefix = path.join(os.tmpdir(), "example-subgraph-"); + const prefix = path.join(os.tmpdir(), 'example-subgraph-'); const tmpDir = fs.mkdtempSync(prefix); try { - await system.run( - `git clone https://github.com/graphprotocol/graph-tooling ${tmpDir}` - ); + await system.run(`git clone https://github.com/graphprotocol/graph-tooling ${tmpDir}`); // If an example is not specified, use the default one if (fromExample === undefined || fromExample === true) { fromExample = DEFAULT_EXAMPLE_SUBGRAPH; } // Legacy purposes when everything existed in examples repo - if (fromExample === "ethereum/gravatar") { + if (fromExample === 'ethereum/gravatar') { fromExample = DEFAULT_EXAMPLE_SUBGRAPH; } - const exampleSubgraphPath = path.join( - tmpDir, - "examples", - String(fromExample) - ); + const exampleSubgraphPath = path.join(tmpDir, 'examples', String(fromExample)); if (!filesystem.exists(exampleSubgraphPath)) { return { result: false, error: `Example not found: ${fromExample}` }; } @@ -1131,14 +1085,14 @@ async function initSubgraphFromExample( } finally { filesystem.remove(tmpDir); } - } + }, ); if (!cloned) { this.exit(1); return; } - const networkConf = await initNetworksConfig(directory, "address"); + const networkConf = await initNetworksConfig(directory, 'address'); if (networkConf !== true) { this.exit(1); return; @@ -1152,22 +1106,19 @@ async function initSubgraphFromExample( async () => { try { // Load package.json - const pkgJsonFilename = filesystem.path(directory, "package.json"); - const pkgJson = await filesystem.read(pkgJsonFilename, "json"); + const pkgJsonFilename = filesystem.path(directory, 'package.json'); + const pkgJson = await filesystem.read(pkgJsonFilename, 'json'); pkgJson.name = getSubgraphBasename(subgraphName); for (const name of Object.keys(pkgJson.scripts)) { - pkgJson.scripts[name] = pkgJson.scripts[name].replace( - "example", - subgraphName - ); + pkgJson.scripts[name] = pkgJson.scripts[name].replace('example', subgraphName); } - delete pkgJson["license"]; - delete pkgJson["repository"]; + delete pkgJson['license']; + delete pkgJson['repository']; // Remove example's cli in favor of the local one (added via `npm link`) if (process.env.GRAPH_CLI_TESTS) { - delete pkgJson["devDependencies"]["@graphprotocol/graph-cli"]; + delete pkgJson['devDependencies']['@graphprotocol/graph-cli']; } // Write package.json @@ -1177,7 +1128,7 @@ async function initSubgraphFromExample( filesystem.remove(directory); this.error(`Failed to preconfigure the subgraph: ${e}`); } - } + }, ); if (!prepared) { this.exit(1); @@ -1256,9 +1207,9 @@ async function initSubgraphFromContract( deploy: string; }; addContract: boolean; - } + }, ) { - const isSubstreams = protocolInstance.name === "substreams"; + const isSubstreams = protocolInstance.name === 'substreams'; // Fail if the subgraph name is invalid if (!revalidateSubgraphName.bind(this)(subgraphName, { allowSimpleName })) { @@ -1286,7 +1237,7 @@ async function initSubgraphFromContract( `Create subgraph scaffold`, `Failed to create subgraph scaffold`, `Warnings while creating subgraph scaffold`, - async (spinner) => { + async spinner => { const scaffold = await generateScaffold( { protocolInstance, @@ -1300,11 +1251,11 @@ async function initSubgraphFromContract( node, spkgPath, }, - spinner + spinner, ); await writeScaffold(scaffold, directory, spinner); return true; - } + }, ); if (scaffold !== true) { process.exitCode = 1; @@ -1366,25 +1317,22 @@ async function addAnotherContract( }: { protocolInstance: Protocol; directory: string; - } + }, ) { - const addContractAnswer = await ux.prompt("Add another contract? (y/n)", { + const addContractAnswer = await ux.prompt('Add another contract? (y/n)', { required: true, - type: "single", + type: 'single', }); - const addContractConfirmation = addContractAnswer.toLowerCase() === "y"; + const addContractConfirmation = addContractAnswer.toLowerCase() === 'y'; if (addContractConfirmation) { const ProtocolContract = protocolInstance.getContract()!; - let contract = ""; - for (; ;) { - contract = await ux.prompt( - `\nContract ${ProtocolContract.identifierName()}`, - { - required: true, - } - ); + let contract = ''; + for (;;) { + contract = await ux.prompt(`\nContract ${ProtocolContract.identifierName()}`, { + required: true, + }); const { valid, error } = validateContract(contract, ProtocolContract); if (valid) { break; From 2651d056604d165db96a8d2d302137a1567b877c Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 8 Jul 2024 14:55:27 -0400 Subject: [PATCH 5/7] fix import --- packages/cli/src/command-helpers/network.ts | 2 +- packages/cli/src/commands/add.ts | 2 +- packages/cli/src/commands/local.ts | 2 +- packages/cli/src/compiler/index.ts | 2 +- packages/cli/src/migrations.ts | 2 +- packages/cli/src/protocols/ethereum/type-generator.ts | 2 +- 6 files changed, 6 insertions(+), 6 deletions(-) diff --git a/packages/cli/src/command-helpers/network.ts b/packages/cli/src/command-helpers/network.ts index f9feac44f..7f36e64ff 100644 --- a/packages/cli/src/command-helpers/network.ts +++ b/packages/cli/src/command-helpers/network.ts @@ -1,7 +1,7 @@ import path from 'path'; import { filesystem, patching } from 'gluegun'; import yaml from 'yaml'; -import withSpinner, { step } from './spinner'; +import { step, withSpinner } from './spinner'; export const updateSubgraphNetwork = async ( manifest: any, diff --git a/packages/cli/src/commands/add.ts b/packages/cli/src/commands/add.ts index 5d34a4290..2bbd5f9cd 100644 --- a/packages/cli/src/commands/add.ts +++ b/packages/cli/src/commands/add.ts @@ -17,7 +17,7 @@ import { writeSchema, writeTestsFiles, } from '../command-helpers/scaffold'; -import withSpinner from '../command-helpers/spinner'; +import { withSpinner } from '../command-helpers/spinner'; import Protocol from '../protocols'; import EthereumABI from '../protocols/ethereum/abi'; import Subgraph from '../subgraph'; diff --git a/packages/cli/src/commands/local.ts b/packages/cli/src/commands/local.ts index 10e1e632e..fcacc81f4 100644 --- a/packages/cli/src/commands/local.ts +++ b/packages/cli/src/commands/local.ts @@ -7,7 +7,7 @@ import { filesystem, patching } from 'gluegun'; import stripAnsi from 'strip-ansi'; import tmp from 'tmp-promise'; import { Args, Command, Flags } from '@oclif/core'; -import withSpinner, { step } from '../command-helpers/spinner'; +import { step, withSpinner } from '../command-helpers/spinner'; // Clean up temporary files even when an uncaught exception occurs tmp.setGracefulCleanup(); diff --git a/packages/cli/src/compiler/index.ts b/packages/cli/src/compiler/index.ts index 591c6dbd4..d2512dcc2 100644 --- a/packages/cli/src/compiler/index.ts +++ b/packages/cli/src/compiler/index.ts @@ -6,7 +6,7 @@ import * as toolbox from 'gluegun'; import immutable from 'immutable'; import type { IPFSHTTPClient } from 'ipfs-http-client'; import yaml from 'js-yaml'; -import withSpinner, { Spinner, step } from '../command-helpers/spinner'; +import { Spinner, step, withSpinner } from '../command-helpers/spinner'; import debug from '../debug'; import { applyMigrations } from '../migrations'; import Protocol from '../protocols'; diff --git a/packages/cli/src/migrations.ts b/packages/cli/src/migrations.ts index 28aead74c..30f1f8d5f 100644 --- a/packages/cli/src/migrations.ts +++ b/packages/cli/src/migrations.ts @@ -1,4 +1,4 @@ -import withSpinner, { step } from './command-helpers/spinner'; +import { step, withSpinner } from './command-helpers/spinner'; const MIGRATIONS = [ import('./migrations/mapping_api_version_0_0_1'), diff --git a/packages/cli/src/protocols/ethereum/type-generator.ts b/packages/cli/src/protocols/ethereum/type-generator.ts index 773462fc9..2458addcd 100644 --- a/packages/cli/src/protocols/ethereum/type-generator.ts +++ b/packages/cli/src/protocols/ethereum/type-generator.ts @@ -4,7 +4,7 @@ import immutable from 'immutable'; import prettier from 'prettier'; import { GENERATED_FILE_NOTE } from '../../codegen/typescript'; import { displayPath } from '../../command-helpers/fs'; -import withSpinner, { Spinner, step } from '../../command-helpers/spinner'; +import { Spinner, step, withSpinner } from '../../command-helpers/spinner'; import { DataSource } from '../utils'; import ABI from './abi'; From f8c7c21750e6cd751d1d5c994c0a7c08364a7884 Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 8 Jul 2024 14:58:53 -0400 Subject: [PATCH 6/7] update deps --- packages/core/package.json | 4 +- pnpm-lock.yaml | 165 +------------------------------------ 2 files changed, 5 insertions(+), 164 deletions(-) diff --git a/packages/core/package.json b/packages/core/package.json index 1fcbfd516..79a82227b 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -15,11 +15,11 @@ }, "dependencies": { "js-yaml": "3.14.1", - "zod": "^3.21.4" + "zod": "^3.23.8" }, "devDependencies": { "@types/js-yaml": "^3.12.7", "prettier": "3.0.3", - "vitest": "^0.34.1" + "vitest": "^1.0.2" } } diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 52ad2a659..df0642b28 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -394,7 +394,7 @@ importers: specifier: 3.14.1 version: 3.14.1 zod: - specifier: ^3.21.4 + specifier: ^3.23.8 version: 3.23.8 devDependencies: '@types/js-yaml': @@ -404,8 +404,8 @@ importers: specifier: 3.0.3 version: 3.0.3 vitest: - specifier: ^0.34.1 - version: 0.34.6(terser@5.31.0) + specifier: ^1.0.2 + version: 1.0.2(@types/node@20.12.11)(terser@5.31.0) packages/ts: dependencies: @@ -3465,9 +3465,6 @@ packages: '@types/chai-as-promised@7.1.5': resolution: {integrity: sha512-jStwss93SITGBwt/niYrkf2C+/1KTeZCZl1LaeezTlqppAKeoQC7jxyqYuP72sxBGKCIbw7oHgbYssIRzT5FCQ==} - '@types/chai-subset@1.3.5': - resolution: {integrity: sha512-c2mPnw+xHtXDoHmdtcCXGwyLMiauiAyxWMzhGpqHC4nqI/Y5G2XhTampslK2rb59kpcuHon03UH8W6iYUzw88A==} - '@types/chai@4.3.14': resolution: {integrity: sha512-Wj71sXE4Q4AkGdG9Tvq1u/fquNz9EdG4LIJMwVVII7ashjD/8cf8fyIfJAjRr6YcsXnSE8cOGQPq1gqeR8z+3w==} @@ -3791,33 +3788,18 @@ packages: peerDependencies: vite: ^4.2.0 || ^5.0.0 - '@vitest/expect@0.34.6': - resolution: {integrity: sha512-QUzKpUQRc1qC7qdGo7rMK3AkETI7w18gTCUrsNnyjjJKYiuUB9+TQK3QnR1unhCnWRC0AbKv2omLGQDF/mIjOw==} - '@vitest/expect@1.0.2': resolution: {integrity: sha512-mAIo/8uddSWkjQMLFcjqZP3WmkwvvN0OtlyZIu33jFnwme3vZds8m8EDMxtj+Uzni2DwtPfHNjJcTM8zTV1f4A==} - '@vitest/runner@0.34.6': - resolution: {integrity: sha512-1CUQgtJSLF47NnhN+F9X2ycxUP0kLHQ/JWvNHbeBfwW8CzEGgeskzNnHDyv1ieKTltuR6sdIHV+nmR6kPxQqzQ==} - '@vitest/runner@1.0.2': resolution: {integrity: sha512-ZcHJXPT2kg/9Hc4fNkCbItlsgZSs3m4vQbxB8LCSdzpbG85bExCmSvu6K9lWpMNdoKfAr1Jn0BwS9SWUcGnbTQ==} - '@vitest/snapshot@0.34.6': - resolution: {integrity: sha512-B3OZqYn6k4VaN011D+ve+AA4whM4QkcwcrwaKwAbyyvS/NB1hCWjFIBQxAQQSQir9/RtyAAGuq+4RJmbn2dH4w==} - '@vitest/snapshot@1.0.2': resolution: {integrity: sha512-9ClDz2/aV5TfWA4reV7XR9p+hE0e7bifhwxlURugj3Fw0YXeTFzHmKCNEHd6wOIFMfthbGGwhlq7TOJ2jDO4/g==} - '@vitest/spy@0.34.6': - resolution: {integrity: sha512-xaCvneSaeBw/cz8ySmF7ZwGvL0lBjfvqc1LpQ/vcdHEvpLn3Ff1vAvjw+CoGn0802l++5L/pxb7whwcWAw+DUQ==} - '@vitest/spy@1.0.2': resolution: {integrity: sha512-YlnHmDntp+zNV3QoTVFI5EVHV0AXpiThd7+xnDEbWnD6fw0TH/J4/+3GFPClLimR39h6nA5m0W4Bjm5Edg4A/A==} - '@vitest/utils@0.34.6': - resolution: {integrity: sha512-IG5aDD8S6zlvloDsnzHw0Ut5xczlF+kv2BOTo+iXfPr54Yhi5qbVOgGB1hZaVq4iJ4C/MZ2J0y15IlsV/ZcI0A==} - '@vitest/utils@1.0.2': resolution: {integrity: sha512-GPQkGHAnFAP/+seSbB9pCsj339yRrMgILoI5H2sPevTLCYgBq0VRjF8QSllmnQyvf0EontF6KUIt2t5s2SmqoQ==} @@ -7570,10 +7552,6 @@ packages: resolution: {integrity: sha512-OfCBkGEw4nN6JLtgRidPX6QxjBQGQf72q3si2uvqyFEMbycSFFHwAZeXx6cJgFM9wmLrf9zBwCP3Ivqa+LLZPw==} engines: {node: '>=6'} - local-pkg@0.4.3: - resolution: {integrity: sha512-SFppqq5p42fe2qcZQqqEOiVRXl+WCP1MdT6k7BDEW1j++sp5fIY+/fdRQitvKgB5BrBcmrs5m/L0v2FrU5MY1g==} - engines: {node: '>=14'} - local-pkg@0.5.0: resolution: {integrity: sha512-ok6z3qlYyCDS4ZEU27HaU6x/xZa9Whf8jD4ptH5UZTQYZVYeb9bnZ3ojVhiJNLiXK1Hfc0GNbLXcmZ5plLDDBg==} engines: {node: '>=14'} @@ -8638,10 +8616,6 @@ packages: resolution: {integrity: sha512-TYOanM3wGwNGsZN2cVTYPArw454xnXj5qmWF1bEoAc4+cU/ol7GVh7odevjp1FNHduHc3KZMcFduxU5Xc6uJRQ==} engines: {node: '>=10'} - p-limit@4.0.0: - resolution: {integrity: sha512-5b0R4txpzjPWVw/cXXUResoD4hb6U/x9BH08L7nw+GN1sezDzPdxeRvpc9c433fZhBan/wusjbCsqwqm4EIBIQ==} - engines: {node: ^12.20.0 || ^14.13.1 || >=16.0.0} - p-limit@5.0.0: resolution: {integrity: sha512-/Eaoq+QyLSiXQ4lyYV23f14mZRQcXnxfHrN0vCai+ak9G0pp9iEQukIIZq5NccEvwRB8PUnZT0KsOoDCINS1qQ==} engines: {node: '>=18'} @@ -10300,10 +10274,6 @@ packages: tinybench@2.5.1: resolution: {integrity: sha512-65NKvSuAVDP/n4CqH+a9w2kTlLReS9vhsAP06MWx+/89nMinJyB2icyl58RIcqCmIggpojIGeuJGhjU1aGMBSg==} - tinypool@0.7.0: - resolution: {integrity: sha512-zSYNUlYSMhJ6Zdou4cJwo/p7w5nmAH17GRfU/ui3ctvjXFErXXkruT4MWW6poDeXgCaIBlGLrfU6TbTXxyGMww==} - engines: {node: '>=14.0.0'} - tinypool@0.8.1: resolution: {integrity: sha512-zBTCK0cCgRROxvs9c0CGK838sPkeokNGdQVUUwHAbynHFlmyJYj825f/oRs528HaIJ97lo0pLIlDUzwN+IorWg==} engines: {node: '>=14.0.0'} @@ -10897,11 +10867,6 @@ packages: resolution: {integrity: sha512-LII3bXRFBZLlezoG5FfZVcXflZgWP/4dCwKtxd5ky9+LOtM4CS3bIRQsmR1KMnMW07jpE8fqR2lcxPZ+8sJIcw==} engines: {node: '>= 0.10'} - vite-node@0.34.6: - resolution: {integrity: sha512-nlBMJ9x6n7/Amaz6F3zJ97EBwR2FkzhBRxF5e+jE6LA3yi6Wtc2lyTij1OnDMIr34v5g/tVQtsVAzhT0jc5ygA==} - engines: {node: '>=v14.18.0'} - hasBin: true - vite-node@1.0.2: resolution: {integrity: sha512-h7BbMJf46fLvFW/9Ygo3snkIBEHFh6fHpB4lge98H5quYrDhPFeI3S0LREz328uqPWSnii2yeJXktQ+Pmqk5BQ==} engines: {node: ^18.0.0 || >=20.0.0} @@ -10968,37 +10933,6 @@ packages: terser: optional: true - vitest@0.34.6: - resolution: {integrity: sha512-+5CALsOvbNKnS+ZHMXtuUC7nL8/7F1F2DnHGjSsszX8zCjWSSviphCb/NuS9Nzf4Q03KyyDRBAXhF/8lffME4Q==} - engines: {node: '>=v14.18.0'} - hasBin: true - peerDependencies: - '@edge-runtime/vm': '*' - '@vitest/browser': '*' - '@vitest/ui': '*' - happy-dom: '*' - jsdom: '*' - playwright: '*' - safaridriver: '*' - webdriverio: '*' - peerDependenciesMeta: - '@edge-runtime/vm': - optional: true - '@vitest/browser': - optional: true - '@vitest/ui': - optional: true - happy-dom: - optional: true - jsdom: - optional: true - playwright: - optional: true - safaridriver: - optional: true - webdriverio: - optional: true - vitest@1.0.2: resolution: {integrity: sha512-F3NVwwpXfRSDnJmyv+ALPwSRVt0zDkRRE18pwUHSUPXAlWQ47rY1dc99ziMW5bBHyqwK2ERjMisLNoef64qk9w==} engines: {node: ^18.0.0 || >=20.0.0} @@ -15538,10 +15472,6 @@ snapshots: dependencies: '@types/chai': 4.3.14 - '@types/chai-subset@1.3.5': - dependencies: - '@types/chai': 4.3.14 - '@types/chai@4.3.14': {} '@types/chai@4.3.4': {} @@ -15913,56 +15843,28 @@ snapshots: transitivePeerDependencies: - supports-color - '@vitest/expect@0.34.6': - dependencies: - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - chai: 4.4.1 - '@vitest/expect@1.0.2': dependencies: '@vitest/spy': 1.0.2 '@vitest/utils': 1.0.2 chai: 4.4.1 - '@vitest/runner@0.34.6': - dependencies: - '@vitest/utils': 0.34.6 - p-limit: 4.0.0 - pathe: 1.1.2 - '@vitest/runner@1.0.2': dependencies: '@vitest/utils': 1.0.2 p-limit: 5.0.0 pathe: 1.1.2 - '@vitest/snapshot@0.34.6': - dependencies: - magic-string: 0.30.5 - pathe: 1.1.2 - pretty-format: 29.7.0 - '@vitest/snapshot@1.0.2': dependencies: magic-string: 0.30.5 pathe: 1.1.2 pretty-format: 29.7.0 - '@vitest/spy@0.34.6': - dependencies: - tinyspy: 2.2.0 - '@vitest/spy@1.0.2': dependencies: tinyspy: 2.2.0 - '@vitest/utils@0.34.6': - dependencies: - diff-sequences: 29.6.3 - loupe: 2.3.7 - pretty-format: 29.7.0 - '@vitest/utils@1.0.2': dependencies: diff-sequences: 29.6.3 @@ -20973,8 +20875,6 @@ snapshots: pify: 4.0.1 strip-bom: 3.0.0 - local-pkg@0.4.3: {} - local-pkg@0.5.0: dependencies: mlly: 1.7.0 @@ -22496,10 +22396,6 @@ snapshots: dependencies: yocto-queue: 0.1.0 - p-limit@4.0.0: - dependencies: - yocto-queue: 1.0.0 - p-limit@5.0.0: dependencies: yocto-queue: 1.0.0 @@ -24463,8 +24359,6 @@ snapshots: tinybench@2.5.1: {} - tinypool@0.7.0: {} - tinypool@0.8.1: {} tinyspy@2.2.0: {} @@ -25146,24 +25040,6 @@ snapshots: remove-trailing-separator: 1.1.0 replace-ext: 1.0.1 - vite-node@0.34.6(@types/node@20.12.11)(terser@5.31.0): - dependencies: - cac: 6.7.14 - debug: 4.3.4(supports-color@5.5.0) - mlly: 1.7.0 - pathe: 1.1.2 - picocolors: 1.0.0 - vite: 5.2.11(@types/node@20.12.11)(terser@5.31.0) - transitivePeerDependencies: - - '@types/node' - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - vite-node@1.0.2(@types/node@20.12.11)(terser@5.31.0): dependencies: cac: 6.7.14 @@ -25209,41 +25085,6 @@ snapshots: fsevents: 2.3.3 terser: 5.31.0 - vitest@0.34.6(terser@5.31.0): - dependencies: - '@types/chai': 4.3.14 - '@types/chai-subset': 1.3.5 - '@types/node': 20.12.11 - '@vitest/expect': 0.34.6 - '@vitest/runner': 0.34.6 - '@vitest/snapshot': 0.34.6 - '@vitest/spy': 0.34.6 - '@vitest/utils': 0.34.6 - acorn: 8.11.3 - acorn-walk: 8.3.2 - cac: 6.7.14 - chai: 4.4.1 - debug: 4.3.4(supports-color@5.5.0) - local-pkg: 0.4.3 - magic-string: 0.30.5 - pathe: 1.1.2 - picocolors: 1.0.0 - std-env: 3.7.0 - strip-literal: 1.3.0 - tinybench: 2.5.1 - tinypool: 0.7.0 - vite: 5.2.11(@types/node@20.12.11)(terser@5.31.0) - vite-node: 0.34.6(@types/node@20.12.11)(terser@5.31.0) - why-is-node-running: 2.2.2 - transitivePeerDependencies: - - less - - lightningcss - - sass - - stylus - - sugarss - - supports-color - - terser - vitest@1.0.2(@types/node@20.12.11)(terser@5.31.0): dependencies: '@vitest/expect': 1.0.2 From fc4be7014a2dd86b79793afe2261d2d9dfb0a1ec Mon Sep 17 00:00:00 2001 From: Saihajpreet Singh Date: Mon, 8 Jul 2024 15:02:00 -0400 Subject: [PATCH 7/7] Revert expect error comment --- packages/cli/src/type-generator.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/packages/cli/src/type-generator.ts b/packages/cli/src/type-generator.ts index f0d221fb7..df8dc3bb2 100644 --- a/packages/cli/src/type-generator.ts +++ b/packages/cli/src/type-generator.ts @@ -4,6 +4,7 @@ import * as toolbox from 'gluegun'; import * as graphql from 'graphql/language'; import immutable from 'immutable'; import prettier from 'prettier'; +// @ts-expect-error TODO: type out if necessary import uncrashable from '@float-capital/float-subgraph-uncrashable/src/Index.bs.js'; import DataSourceTemplateCodeGenerator from './codegen/template'; import { GENERATED_FILE_NOTE, ModuleImports } from './codegen/typescript';