diff --git a/packages/core/src/index.ts b/packages/core/src/index.ts index fc63334b9..7e5dc48e1 100644 --- a/packages/core/src/index.ts +++ b/packages/core/src/index.ts @@ -2,3 +2,4 @@ export * from './constants.js'; export * from './helpers/index.js'; export * from './types.js'; export * from './versions/index.js'; +export * from './utils/index.js'; diff --git a/packages/core/src/utils/index.ts b/packages/core/src/utils/index.ts index 1ee757bcb..a90dfae66 100644 --- a/packages/core/src/utils/index.ts +++ b/packages/core/src/utils/index.ts @@ -1,9 +1,9 @@ -/** @internal */ +/** Make sure to use all possible options in a switch statement */ export const assertExhaustiveSwitch = (x: never, msg: string): never => { throw new Error(`${msg}: ${x}`); }; -/** @internal */ +/** Use this to filter empty undefinied values from arrays in a type-safe way */ export const nonNullable = (value: T): value is NonNullable => { return value !== null && value !== undefined; }; diff --git a/packages/events/src/ColonyEventManager.ts b/packages/events/src/ColonyEventManager.ts index 25a9d05ff..0d2bdab3f 100644 --- a/packages/events/src/ColonyEventManager.ts +++ b/packages/events/src/ColonyEventManager.ts @@ -7,7 +7,12 @@ import { type EventFilter, type BaseContract, } from 'ethers'; -import { addressesAreEqual, type SignerOrProvider } from '@colony/core'; + +import { + addressesAreEqual, + nonNullable, + type SignerOrProvider, +} from '@colony/core'; import { type IpfsAdapter, @@ -16,8 +21,9 @@ import { IpfsMetadata, type MetadataType, } from './ipfs/index.js'; + import type { Ethers6Filter } from './types.js'; -import { getLogs, nonNullable } from './utils.js'; +import { getLogs } from './utils.js'; /** * A valid eventsource (currently just an ethers.js {@link BaseContract}) diff --git a/packages/events/src/utils.ts b/packages/events/src/utils.ts index f93ddcc8c..f8a5226a2 100644 --- a/packages/events/src/utils.ts +++ b/packages/events/src/utils.ts @@ -14,8 +14,3 @@ export const getLogs = async ( const usedFilter = await filter; return provider.send('eth_getLogs', [usedFilter]); }; - -/** Use this to filter empty undefinied values from arrays in a type-safe way */ -export const nonNullable = (value: T): value is NonNullable => { - return value !== null && value !== undefined; -}; diff --git a/packages/wagmi-plugin/package.json b/packages/wagmi-plugin/package.json new file mode 100644 index 000000000..5bb0b91ee --- /dev/null +++ b/packages/wagmi-plugin/package.json @@ -0,0 +1,57 @@ +{ + "name": "@colony/wagmi-plugin", + "version": "0.1", + "description": "A plugin to generate wagmi/abitype compatible ABIs", + "scripts": { + "clean": "rimraf ./dist", + "build": "npm run clean && tsc --declaration --outDir dist", + "lint": "eslint --ext .ts src", + "typecheck": "tsc --noEmit" + }, + "exports": { + ".": { + "import": "./dist/index.js", + "types": "./dist/index.d.ts", + "default": "./dist/index.js" + } + }, + "module": "./dist/esm/index.js", + "types": "./dist/types/index.d.ts", + "files": [ + "./dist", + "README.md" + ], + "type": "module", + "engines": { + "node": "^18 || ^20 || ^22", + "pnpm": "^8" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/JoinColony/colonyJS.git" + }, + "author": "Christian Maniewski ", + "license": "GPL-3.0-only", + "bugs": { + "url": "https://github.com/JoinColony/colonyJS/issues" + }, + "homepage": "https://docs.colony.io/develop", + "dependencies": { + "@colony/abis": "^1.8.1", + "@colony/core": "^2.3.0", + "@typechain/ethers-v5": "^11.1.0", + "@typechain/ethers-v6": "^0.4.2", + "@types/mkdirp": "^1.0.2", + "@types/yargs": "^17.0.24", + "abitype": "^1.0.6", + "cross-fetch": "^4.0.0", + "mkdirp": "^1.0.4", + "rimraf": "^5.0.0", + "typechain": "8.3.0", + "yargs": "^17.7.1" + }, + "devDependencies": { + "@types/node": "^18.15.11", + "ts-node": "^10.9.1" + } +} diff --git a/packages/wagmi-plugin/src/index.ts b/packages/wagmi-plugin/src/index.ts new file mode 100644 index 000000000..3a9c2cb59 --- /dev/null +++ b/packages/wagmi-plugin/src/index.ts @@ -0,0 +1,68 @@ +import { createRequire } from 'node:module'; +import { join as joinPath } from 'node:path'; +import { readFile } from 'node:fs/promises'; +import { fetch } from 'cross-fetch'; +import { type Address } from 'abitype'; +import { joinAbis, type NodeType } from '@colony/abis/utils'; +import { nonNullable } from '@colony/core'; + +interface ContractConfig { + name: string; + path: string; + address?: Address; + merge?: NodeType; +} + +interface ColonyConfig { + baseUrl: string; + contracts: ContractConfig[]; +} + +// Capitalize the first letter of a string +const capitalize = (s: string) => s.charAt(0).toUpperCase() + s.slice(1); + +const resolveAbiFile = async (path: string) => { + const require = createRequire(import.meta.url); + return require.resolve(path); +}; + +const colony = (config: ColonyConfig) => { + const { baseUrl, contracts } = config; + return { + name: 'Colony', + async contracts() { + const promises = contracts.map(async ({ name, path, address, merge }) => { + try { + if (merge) { + const typeSuffix = capitalize(merge); + const eventsAbiFile = await resolveAbiFile( + `@colony/abis/${merge}/${name}${typeSuffix}`, + ); + const contents = await readFile(eventsAbiFile); + const { abi: historicalAbi } = JSON.parse(contents.toString()); + const res = await fetch(joinPath(baseUrl, path)); + const { abi } = await res.json(); + // Join historical and current ABIs + return { + abi: joinAbis(historicalAbi, abi, merge), + name: `${name}${typeSuffix}`, + }; + } + const res = await fetch(joinPath(baseUrl, path)); + const { abi } = await res.json(); + return { abi, address, name }; + } catch (e) { + console.error( + `Could not read contract file ${name}:`, + (e as Error).message, + ); + return undefined; + } + }); + const results = await Promise.all(promises); + return results.filter(nonNullable); + }, + }; +}; + +export default colony; diff --git a/packages/wagmi-plugin/tsconfig.json b/packages/wagmi-plugin/tsconfig.json new file mode 100644 index 000000000..4d467e465 --- /dev/null +++ b/packages/wagmi-plugin/tsconfig.json @@ -0,0 +1,22 @@ +{ + "compilerOptions": { + "lib": [ + "es2022" + ], + "module": "nodenext", + "moduleResolution": "nodenext", + "noImplicitAny": true, + "resolveJsonModule": true, + "esModuleInterop": true, + "strict": true, + "target": "es2022", + "types": [ + "node" + ], + "rootDir": "./src", + "skipLibCheck": true + }, + "include": [ + "./src" + ] +} diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index b5e61005d..4b689136e 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -301,6 +301,52 @@ importers: specifier: ^5.7.2 version: 5.7.2 + packages/wagmi-plugin: + dependencies: + '@colony/abis': + specifier: ^1.8.1 + version: 1.8.1(typescript@5.1.6) + '@colony/core': + specifier: ^2.3.0 + version: link:../core + '@typechain/ethers-v5': + specifier: ^11.1.0 + version: 11.1.0(@ethersproject/abi@5.7.0)(@ethersproject/providers@5.7.2)(typechain@8.3.0)(typescript@5.1.6) + '@typechain/ethers-v6': + specifier: ^0.4.2 + version: 0.4.2(typechain@8.3.0)(typescript@5.1.6) + '@types/mkdirp': + specifier: ^1.0.2 + version: 1.0.2 + '@types/yargs': + specifier: ^17.0.24 + version: 17.0.24 + abitype: + specifier: ^1.0.6 + version: 1.0.6(typescript@5.1.6) + cross-fetch: + specifier: ^4.0.0 + version: 4.0.0 + mkdirp: + specifier: ^1.0.4 + version: 1.0.4 + rimraf: + specifier: ^5.0.0 + version: 5.0.0 + typechain: + specifier: 8.3.0 + version: 8.3.0(typescript@5.1.6) + yargs: + specifier: ^17.7.1 + version: 17.7.1 + devDependencies: + '@types/node': + specifier: ^18.15.11 + version: 18.15.11 + ts-node: + specifier: ^10.9.1 + version: 10.9.1(@types/node@18.15.11)(typescript@5.1.6) + packages: /@ampproject/remapping@2.2.1: @@ -836,6 +882,17 @@ packages: engines: {node: ^16 || ^18 || ^20, npm: ^8 || ^9 || ^10} dev: false + /@colony/abis@1.8.1(typescript@5.1.6): + resolution: {integrity: sha512-DNL5HnhVyptXusgahK4QJK4Djq8p0tWVdMZ23Gv310uPFDFsdn/Da/Ziq4+GGG7oV/+bguta/JykOg91Wme83A==} + engines: {node: ^18 || ^20 || ^22, npm: ^10} + dependencies: + abitype: 1.0.7(typescript@5.1.6) + lodash: 4.17.21 + transitivePeerDependencies: + - typescript + - zod + dev: false + /@colony/eslint-config-colony@9.2.0(eslint-config-airbnb-base@15.0.0)(eslint-config-prettier@8.8.0)(eslint-plugin-eslint-comments@3.2.0)(eslint-plugin-import@2.27.5)(eslint-plugin-prettier@4.2.1)(eslint@8.39.0)(prettier@2.8.8): resolution: {integrity: sha512-Z0Ss2S7HAbdxBibfInJ8Hu7xBGCf30nAmvkHAM+JUsxSPgj35xEx3NsoXyUogDoYbuCtnaRZKcvB5tCSonltBQ==} peerDependencies: @@ -2324,6 +2381,20 @@ packages: typescript: 5.1.6 dev: false + /abitype@1.0.7(typescript@5.1.6): + resolution: {integrity: sha512-ZfYYSktDQUwc2eduYu8C4wOs+RDPmnRYMh7zNfzeMtGGgb0U+6tLGjixUic6mXf5xKKCcgT5Qp6cv39tOARVFw==} + peerDependencies: + typescript: '>=5.0.4' + zod: ^3 >=3.22.0 + peerDependenciesMeta: + typescript: + optional: true + zod: + optional: true + dependencies: + typescript: 5.1.6 + dev: false + /acorn-jsx@5.3.2(acorn@8.8.2): resolution: {integrity: sha512-rq9s+JNhf0IChjtDXxllJ7g41oZk5SlXtp0LHwyA5cejwn7vKmKp4pPri6YEePv2PU65sAsegbXtIinmDFDXgQ==} peerDependencies: @@ -2929,7 +3000,6 @@ packages: node-fetch: 2.7.0 transitivePeerDependencies: - encoding - dev: true /cross-spawn@5.1.0: resolution: {integrity: sha512-pTgQJ5KC0d2hcY8eyL1IzlBPYjTkyH72XRZPnLyKus2mBfNjQs3klqbJU2VILqZryAZUt9JOb3h/mWMy23/f5A==} @@ -5328,7 +5398,6 @@ packages: optional: true dependencies: whatwg-url: 5.0.0 - dev: true /node-int64@0.4.0: resolution: {integrity: sha512-O5lz91xSOeoXP6DulyHfllpq+Eg00MWitZIbtPfoSEvqIHdl5gfcY6hYzDWnj0qD5tz52PI08u9qUvSVeUBeHw==} @@ -5824,6 +5893,7 @@ packages: /rimraf@3.0.2: resolution: {integrity: sha512-JZkJMZkAGFFPP2YqXZXPbMlMBgsxzE8ILs4lMIX/2o0L9UBw9O/Y3o6wFw/i9YLapcUJWwqbi3kdxIPdC62TIA==} + deprecated: Rimraf versions prior to v4 are no longer supported hasBin: true dependencies: glob: 7.2.3 @@ -6270,7 +6340,6 @@ packages: /tr46@0.0.3: resolution: {integrity: sha512-N3WMsuqV66lT30CrXNbEjx4GEwlow3v6rr4mCcv6prnfwhS01rkgyFdjPNBYd9br7LpXV1+Emh01fHnq2Gdgrw==} - dev: true /tr46@1.0.1: resolution: {integrity: sha512-dTpowEjclQ7Kgx5SdBkqRzVhERQXov8/l9Ft9dVM9fmg0W0KQSVaXX9T4i6twCPNtYiZM53lpSSUAwJbFPOHxA==} @@ -6696,7 +6765,6 @@ packages: /webidl-conversions@3.0.1: resolution: {integrity: sha512-2JAn3z8AR6rjK8Sm8orRC0h/bcl/DqL7tRPdGZ4I1CjdF+EaMLmYxBHyXuKL849eucPFhvBoxMsflfOb8kxaeQ==} - dev: true /webidl-conversions@4.0.2: resolution: {integrity: sha512-YQ+BmxuTgd6UXZW3+ICGfyqRyHXVlD5GtQr5+qjiNW7bF0cqrzX500HVXPBOvgXb5YnzDd+h0zqyv61KUD7+Sg==} @@ -6707,7 +6775,6 @@ packages: dependencies: tr46: 0.0.3 webidl-conversions: 3.0.1 - dev: true /whatwg-url@7.1.0: resolution: {integrity: sha512-WUu7Rg1DroM7oQvGWfOiAK21n74Gg+T4elXEQYkOhtyLeWiJFoOGLXPKI/9gzIie9CtwVLm8wtw6YJdKyxSjeg==}