From 4103396ac1f717a437a33dd67c6c7cc8bc71dff4 Mon Sep 17 00:00:00 2001 From: Ioannis Tourkogiorgis Date: Mon, 5 Aug 2024 14:46:20 +0200 Subject: [PATCH] refactor: replace typechain with abitype --- .env.template | 4 +- package-lock.json | 442 ++------------ package.json | 5 +- src/abi/AddressDriver.json | 13 - src/abi/RepoDriver.json | 14 - src/appSettings.ts | 24 +- src/application/Auth.ts | 20 +- src/application/ReceiverMapper.ts | 86 ++- src/application/SafeAdapter.ts | 6 +- .../contractClients/AddressDriverAbi.ts | 396 ++++++++++++ .../contractClients/RepoDriverAbi.ts | 575 ++++++++++++++++++ .../contractClients/contractClients.ts | 90 +++ src/application/network.ts | 50 ++ src/application/networkConfig.ts | 57 ++ src/application/networks.ts | 12 - src/environment.d.ts | 4 +- src/infrastructure/AppDataSource.ts | 2 +- src/infrastructure/logger.ts | 2 +- src/main.ts | 17 +- tests/application/Auth.test.ts | 16 +- 20 files changed, 1307 insertions(+), 528 deletions(-) delete mode 100644 src/abi/AddressDriver.json delete mode 100644 src/abi/RepoDriver.json create mode 100644 src/application/contractClients/AddressDriverAbi.ts create mode 100644 src/application/contractClients/RepoDriverAbi.ts create mode 100644 src/application/contractClients/contractClients.ts create mode 100644 src/application/network.ts create mode 100644 src/application/networkConfig.ts delete mode 100644 src/application/networks.ts diff --git a/.env.template b/.env.template index a5a4a5b..ca520e6 100644 --- a/.env.template +++ b/.env.template @@ -1,5 +1,5 @@ PORT=number # Port number for the API server. Defaults to 5001. -NETWORK=string # 'goerli' or 'mainnet'. Defaults to 'goerli'. This will also be the database schema name. +CHAIN_ID=string # Chain ID of the blockchain. See `SUPPORTED_CHAIN_IDS` in `src/network.ts` for supported chain IDs. DB_HOST=string DB_PORT=number DB_USER=string @@ -9,7 +9,5 @@ LOG_LEVEL=string # 'debug', 'info', 'warn' or 'error'. Defaults to 'info'. GRAPHQL_URL=string # URL of the GraphQL server. GRAPHQL_TOKEN=string # Token for the GraphQL server. RPC_URL=string # The RPC URL to connect to. -ADDRESS_DRIVER_ADDRESS=string # The `AddressDriver` contract address on the `NETWORK`. -REPO_DRIVER_ADDRESS=string # The `RepoDriver` contract address on the `NETWORK`. AUTH_STRATEGY=string # 'signature' or 'dev'. Defaults to 'signature'. API_KEY=string # API key for the API server. \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index bdf0b7b..ee0c653 100644 --- a/package-lock.json +++ b/package-lock.json @@ -13,7 +13,7 @@ "@safe-global/protocol-kit": "^3.0.1", "@safe-global/safe-core-sdk-types": "^4.0.1", "dotenv": "^16.4.5", - "ethers": "^6.11.1", + "ethers": "^6.13.2", "express": "^4.18.2", "express-validator": "^7.0.1", "graphql-request": "^6.1.0", @@ -25,12 +25,12 @@ "devDependencies": { "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", - "@typechain/ethers-v6": "^0.5.1", "@types/express": "^4.17.21", "@types/jest": "^29.5.12", "@types/node": "^20.11.19", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", + "abitype": "^1.0.5", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", @@ -44,7 +44,6 @@ "prettier": "^3.2.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typechain": "^8.3.2", "typescript": "^5.3.3" } }, @@ -2900,21 +2899,6 @@ "integrity": "sha512-vxhUy4J8lyeyinH7Azl1pdd43GJhZH/tP2weN8TntQblOY+A0XbT8DJk1/oCPuOOyg/Ja757rG0CgHcWC8OfMA==", "devOptional": true }, - "node_modules/@typechain/ethers-v6": { - "version": "0.5.1", - "resolved": "https://registry.npmjs.org/@typechain/ethers-v6/-/ethers-v6-0.5.1.tgz", - "integrity": "sha512-F+GklO8jBWlsaVV+9oHaPh5NJdd6rAKN4tklGfInX1Q7h0xPgVLP39Jl3eCulPB5qexI71ZFHwbljx4ZXNfouA==", - "dev": true, - "dependencies": { - "lodash": "^4.17.15", - "ts-essentials": "^7.0.1" - }, - "peerDependencies": { - "ethers": "6.x", - "typechain": "^8.3.2", - "typescript": ">=4.7.0" - } - }, "node_modules/@types/babel__core": { "version": "7.20.5", "resolved": "https://registry.npmjs.org/@types/babel__core/-/babel__core-7.20.5.tgz", @@ -3126,12 +3110,6 @@ "@types/node": "*" } }, - "node_modules/@types/prettier": { - "version": "2.7.3", - "resolved": "https://registry.npmjs.org/@types/prettier/-/prettier-2.7.3.tgz", - "integrity": "sha512-+68kP9yzs4LMp7VNh8gdzMSPZFL44MLGqiHWvttYJe+6qnuVr4Ek9wSBQoveqY/r+LwjCcU29kNVkidwim+kYA==", - "dev": true - }, "node_modules/@types/qs": { "version": "6.9.11", "resolved": "https://registry.npmjs.org/@types/qs/-/qs-6.9.11.tgz", @@ -3415,6 +3393,28 @@ "integrity": "sha512-nne9/IiQ/hzIhY6pdDnbBtz7DjPTKrY00P/zvPSm5pOFkl6xuGrGnXn/VtTNNfNtAfZ9/1RtehkszU9qcTii0Q==", "dev": true }, + "node_modules/abitype": { + "version": "1.0.5", + "resolved": "https://registry.npmjs.org/abitype/-/abitype-1.0.5.tgz", + "integrity": "sha512-YzDhti7cjlfaBhHutMaboYB21Ha3rXR9QTkNJFzYC4kC8YclaiwPBBBJY8ejFdu2wnJeZCVZSMlQJ7fi8S6hsw==", + "dev": true, + "license": "MIT", + "funding": { + "url": "https://github.com/sponsors/wevm" + }, + "peerDependencies": { + "typescript": ">=5.0.4", + "zod": "^3 >=3.22.0" + }, + "peerDependenciesMeta": { + "typescript": { + "optional": true + }, + "zod": { + "optional": true + } + } + }, "node_modules/abortcontroller-polyfill": { "version": "1.7.5", "resolved": "https://registry.npmjs.org/abortcontroller-polyfill/-/abortcontroller-polyfill-1.7.5.tgz", @@ -3568,15 +3568,6 @@ "integrity": "sha512-8+9WqebbFzpX9OR+Wa6O29asIogeRMzcGtAINdpMHHyAg10f05aSFVBbcEqGf/PXw1EjAZ+q2/bEBg3DvurK3Q==", "dev": true }, - "node_modules/array-back": { - "version": "3.1.0", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-3.1.0.tgz", - "integrity": "sha512-TkuxA4UCOvxuDK6NZYXCalszEzj+TLszyASooky+i742l9TqsOdYCMJJupxRic61hwquNtppB3hgcuq9SVSH1Q==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/array-buffer-byte-length": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/array-buffer-byte-length/-/array-buffer-byte-length-1.0.1.tgz", @@ -4873,54 +4864,6 @@ "node": ">= 0.8" } }, - "node_modules/command-line-args": { - "version": "5.2.1", - "resolved": "https://registry.npmjs.org/command-line-args/-/command-line-args-5.2.1.tgz", - "integrity": "sha512-H4UfQhZyakIjC74I9d34fGYDwk3XpSr17QhEd0Q3I9Xq1CETHo4Hcuo87WyWHpAF1aSLjLRf5lD9ZGX2qStUvg==", - "dev": true, - "dependencies": { - "array-back": "^3.1.0", - "find-replace": "^3.0.0", - "lodash.camelcase": "^4.3.0", - "typical": "^4.0.0" - }, - "engines": { - "node": ">=4.0.0" - } - }, - "node_modules/command-line-usage": { - "version": "6.1.3", - "resolved": "https://registry.npmjs.org/command-line-usage/-/command-line-usage-6.1.3.tgz", - "integrity": "sha512-sH5ZSPr+7UStsloltmDh7Ce5fb8XPlHyoPzTpyyMuYCtervL65+ubVZ6Q61cFtFl62UyJlc8/JwERRbAFPUqgw==", - "dev": true, - "dependencies": { - "array-back": "^4.0.2", - "chalk": "^2.4.2", - "table-layout": "^1.0.2", - "typical": "^5.2.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/command-line-usage/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/command-line-usage/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/commander": { "version": "11.1.0", "resolved": "https://registry.npmjs.org/commander/-/commander-11.1.0.tgz", @@ -5438,15 +5381,6 @@ "integrity": "sha512-Q6fKUPqnAHAyhiUgFU7BUzLiv0kd8saH9al7tnu5Q/okj6dnupxyTgFIBjVzJATdfIAm9NAsvXNzjaKa+bxVyA==", "dev": true }, - "node_modules/deep-extend": { - "version": "0.6.0", - "resolved": "https://registry.npmjs.org/deep-extend/-/deep-extend-0.6.0.tgz", - "integrity": "sha512-LOHxIOaPYdHlJRtCQfDIVZtfw/ufM8+rVj649RIHzcm/vGwQRXFt6OPqIFWsm2XEMrNIEtWR64sY1LEKD2vAOA==", - "dev": true, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/deep-is": { "version": "0.1.4", "resolved": "https://registry.npmjs.org/deep-is/-/deep-is-0.1.4.tgz", @@ -6492,9 +6426,9 @@ } }, "node_modules/ethers": { - "version": "6.11.1", - "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.11.1.tgz", - "integrity": "sha512-mxTAE6wqJQAbp5QAe/+o+rXOID7Nw91OZXvgpjDa1r4fAbq2Nu314oEZSbjoRLacuCzs7kUC3clEvkCQowffGg==", + "version": "6.13.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-6.13.2.tgz", + "integrity": "sha512-9VkriTTed+/27BGuY1s0hf441kqwHJ1wtN2edksEtiRvXx+soxRX3iSXTfFqq2+YwrOqbDoTHjIhQnjJRlzKmg==", "funding": [ { "type": "individual", @@ -6505,6 +6439,7 @@ "url": "https://www.buymeacoffee.com/ricmoo" } ], + "license": "MIT", "dependencies": { "@adraffy/ens-normalize": "1.10.1", "@noble/curves": "1.2.0", @@ -6512,7 +6447,7 @@ "@types/node": "18.15.13", "aes-js": "4.0.0-beta.5", "tslib": "2.4.0", - "ws": "8.5.0" + "ws": "8.17.1" }, "engines": { "node": ">=14.0.0" @@ -6877,18 +6812,6 @@ "merge": "^2.1.1" } }, - "node_modules/find-replace": { - "version": "3.0.0", - "resolved": "https://registry.npmjs.org/find-replace/-/find-replace-3.0.0.tgz", - "integrity": "sha512-6Tb2myMioCAgv5kfvP5/PkZZ/ntTpVK39fHY7WkWBgvbeE+VHd/tZuZ4mrC+bxh4cfOZeYKVPaJIZtZXV7GNCQ==", - "dev": true, - "dependencies": { - "array-back": "^3.0.1" - }, - "engines": { - "node": ">=4.0.0" - } - }, "node_modules/find-root": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/find-root/-/find-root-1.1.0.tgz", @@ -12700,15 +12623,6 @@ "node": ">=8" } }, - "node_modules/reduce-flatten": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/reduce-flatten/-/reduce-flatten-2.0.0.tgz", - "integrity": "sha512-EJ4UNY/U1t2P/2k6oqotuX2Cc3T6nxJwsM0N0asT7dhrtH1ltUxDn4NalSYmPE2rCkVpcf/X6R0wDwcFpzhd4w==", - "dev": true, - "engines": { - "node": ">=6" - } - }, "node_modules/reflect-metadata": { "version": "0.2.1", "resolved": "https://registry.npmjs.org/reflect-metadata/-/reflect-metadata-0.2.1.tgz", @@ -13524,12 +13438,6 @@ "node": ">=0.6.19" } }, - "node_modules/string-format": { - "version": "2.0.0", - "resolved": "https://registry.npmjs.org/string-format/-/string-format-2.0.0.tgz", - "integrity": "sha512-bbEs3scLeYNXLecRRuk6uJxdXUSj6le/8rNPHChIJTn2V79aXVTR1EH2OH5zLKKoz0V02fOUKZZcw01pLUShZA==", - "dev": true - }, "node_modules/string-length": { "version": "4.0.2", "resolved": "https://registry.npmjs.org/string-length/-/string-length-4.0.2.tgz", @@ -13842,39 +13750,6 @@ "node": ">= 4.0.0" } }, - "node_modules/table-layout": { - "version": "1.0.2", - "resolved": "https://registry.npmjs.org/table-layout/-/table-layout-1.0.2.tgz", - "integrity": "sha512-qd/R7n5rQTRFi+Zf2sk5XVVd9UQl6ZkduPFC3S7WEGJAmetDTjY3qPN50eSKzwuzEyQKy5TN2TiZdkIjos2L6A==", - "dev": true, - "dependencies": { - "array-back": "^4.0.1", - "deep-extend": "~0.6.0", - "typical": "^5.2.0", - "wordwrapjs": "^4.0.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/table-layout/node_modules/array-back": { - "version": "4.0.2", - "resolved": "https://registry.npmjs.org/array-back/-/array-back-4.0.2.tgz", - "integrity": "sha512-NbdMezxqf94cnNfWLL7V/im0Ub+Anbb0IoZhvzie8+4HJ4nMQuzHuy49FkGYCJK2yAloZ3meiB6AVMClbrI1vg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/table-layout/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/tar": { "version": "4.4.19", "resolved": "https://registry.npmjs.org/tar/-/tar-4.4.19.tgz", @@ -14123,100 +13998,6 @@ "typescript": ">=4.2.0" } }, - "node_modules/ts-command-line-args": { - "version": "2.5.1", - "resolved": "https://registry.npmjs.org/ts-command-line-args/-/ts-command-line-args-2.5.1.tgz", - "integrity": "sha512-H69ZwTw3rFHb5WYpQya40YAX2/w7Ut75uUECbgBIsLmM+BNuYnxsltfyyLMxy6sEeKxgijLTnQtLd0nKd6+IYw==", - "dev": true, - "dependencies": { - "chalk": "^4.1.0", - "command-line-args": "^5.1.1", - "command-line-usage": "^6.1.0", - "string-format": "^2.0.0" - }, - "bin": { - "write-markdown": "dist/write-markdown.js" - } - }, - "node_modules/ts-command-line-args/node_modules/ansi-styles": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/ansi-styles/-/ansi-styles-4.3.0.tgz", - "integrity": "sha512-zbB9rCJAT1rbjiVDb2hqKFHNYLxgtk8NURxZ3IZwD3F6NtxbXZQCnnSi1Lkx+IDohdPlFp222wVALIheZJQSEg==", - "dev": true, - "dependencies": { - "color-convert": "^2.0.1" - }, - "engines": { - "node": ">=8" - }, - "funding": { - "url": "https://github.com/chalk/ansi-styles?sponsor=1" - } - }, - "node_modules/ts-command-line-args/node_modules/chalk": { - "version": "4.1.2", - "resolved": "https://registry.npmjs.org/chalk/-/chalk-4.1.2.tgz", - "integrity": "sha512-oKnbhFyRIXpUuez8iBMmyEa4nbj4IOQyuhc/wy9kY7/WVPcwIO9VA668Pu8RkO7+0G76SLROeyw9CpQ061i4mA==", - "dev": true, - "dependencies": { - "ansi-styles": "^4.1.0", - "supports-color": "^7.1.0" - }, - "engines": { - "node": ">=10" - }, - "funding": { - "url": "https://github.com/chalk/chalk?sponsor=1" - } - }, - "node_modules/ts-command-line-args/node_modules/color-convert": { - "version": "2.0.1", - "resolved": "https://registry.npmjs.org/color-convert/-/color-convert-2.0.1.tgz", - "integrity": "sha512-RRECPsj7iu/xb5oKYcsFHSppFNnsj/52OVTRKb4zP5onXwVF3zVmmToNcOfGC+CRDpfK/U584fMg38ZHCaElKQ==", - "dev": true, - "dependencies": { - "color-name": "~1.1.4" - }, - "engines": { - "node": ">=7.0.0" - } - }, - "node_modules/ts-command-line-args/node_modules/color-name": { - "version": "1.1.4", - "resolved": "https://registry.npmjs.org/color-name/-/color-name-1.1.4.tgz", - "integrity": "sha512-dOy+3AuW3a2wNbZHIuMZpTcgjGuLU/uBL/ubcZF9OXbDo8ff4O8yVp5Bf0efS8uEoYo5q4Fx7dY9OgQGXgAsQA==", - "dev": true - }, - "node_modules/ts-command-line-args/node_modules/has-flag": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/has-flag/-/has-flag-4.0.0.tgz", - "integrity": "sha512-EykJT/Q1KjTWctppgIAgfSO0tKVuZUjhgMr17kqTumMl6Afv3EISleU7qZUzoXDFTAHTDC4NOoG/ZxU3EvlMPQ==", - "dev": true, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-command-line-args/node_modules/supports-color": { - "version": "7.2.0", - "resolved": "https://registry.npmjs.org/supports-color/-/supports-color-7.2.0.tgz", - "integrity": "sha512-qpCAvRl9stuOHveKsn7HncJRvv501qIacKzQlO/+Lwxc9+0q2wLyv4Dfvt80/DPn2pqOBsJdDiogXGR9+OvwRw==", - "dev": true, - "dependencies": { - "has-flag": "^4.0.0" - }, - "engines": { - "node": ">=8" - } - }, - "node_modules/ts-essentials": { - "version": "7.0.3", - "resolved": "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz", - "integrity": "sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ==", - "dev": true, - "peerDependencies": { - "typescript": ">=3.7.0" - } - }, "node_modules/ts-jest": { "version": "29.1.2", "resolved": "https://registry.npmjs.org/ts-jest/-/ts-jest-29.1.2.tgz", @@ -14407,131 +14188,6 @@ "node": ">= 0.6" } }, - "node_modules/typechain": { - "version": "8.3.2", - "resolved": "https://registry.npmjs.org/typechain/-/typechain-8.3.2.tgz", - "integrity": "sha512-x/sQYr5w9K7yv3es7jo4KTX05CLxOf7TRWwoHlrjRh8H82G64g+k7VuWPJlgMo6qrjfCulOdfBjiaDtmhFYD/Q==", - "dev": true, - "dependencies": { - "@types/prettier": "^2.1.1", - "debug": "^4.3.1", - "fs-extra": "^7.0.0", - "glob": "7.1.7", - "js-sha3": "^0.8.0", - "lodash": "^4.17.15", - "mkdirp": "^1.0.4", - "prettier": "^2.3.1", - "ts-command-line-args": "^2.2.0", - "ts-essentials": "^7.0.1" - }, - "bin": { - "typechain": "dist/cli/cli.js" - }, - "peerDependencies": { - "typescript": ">=4.3.0" - } - }, - "node_modules/typechain/node_modules/brace-expansion": { - "version": "1.1.11", - "resolved": "https://registry.npmjs.org/brace-expansion/-/brace-expansion-1.1.11.tgz", - "integrity": "sha512-iCuPHDFgrHX7H2vEI/5xpz07zSHB00TpugqhmYtVmMO6518mCuRMoOYFldEBl0g187ufozdaHgWKcYFb61qGiA==", - "dev": true, - "dependencies": { - "balanced-match": "^1.0.0", - "concat-map": "0.0.1" - } - }, - "node_modules/typechain/node_modules/fs-extra": { - "version": "7.0.1", - "resolved": "https://registry.npmjs.org/fs-extra/-/fs-extra-7.0.1.tgz", - "integrity": "sha512-YJDaCJZEnBmcbw13fvdAM9AwNOJwOzrE4pqMqBq5nFiEqXUqHwlK4B+3pUw6JNvfSPtX05xFHtYy/1ni01eGCw==", - "dev": true, - "dependencies": { - "graceful-fs": "^4.1.2", - "jsonfile": "^4.0.0", - "universalify": "^0.1.0" - }, - "engines": { - "node": ">=6 <7 || >=8" - } - }, - "node_modules/typechain/node_modules/glob": { - "version": "7.1.7", - "resolved": "https://registry.npmjs.org/glob/-/glob-7.1.7.tgz", - "integrity": "sha512-OvD9ENzPLbegENnYP5UUfJIirTg4+XwMWGaQfQTY0JenxNvvIKP3U3/tAQSPIu/lHxXYSZmpXlUHeqAIdKzBLQ==", - "dev": true, - "dependencies": { - "fs.realpath": "^1.0.0", - "inflight": "^1.0.4", - "inherits": "2", - "minimatch": "^3.0.4", - "once": "^1.3.0", - "path-is-absolute": "^1.0.0" - }, - "engines": { - "node": "*" - }, - "funding": { - "url": "https://github.com/sponsors/isaacs" - } - }, - "node_modules/typechain/node_modules/jsonfile": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/jsonfile/-/jsonfile-4.0.0.tgz", - "integrity": "sha512-m6F1R3z8jjlf2imQHS2Qez5sjKWQzbuuhuJ/FKYFRZvPE3PuHcSMVZzfsLhGVOkfd20obL5SWEBew5ShlquNxg==", - "dev": true, - "optionalDependencies": { - "graceful-fs": "^4.1.6" - } - }, - "node_modules/typechain/node_modules/minimatch": { - "version": "3.1.2", - "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", - "integrity": "sha512-J7p63hRiAjw1NDEww1W7i37+ByIrOWO5XQQAzZ3VOcL0PNybwpfmV/N05zFAzwQ9USyEcX6t3UO+K5aqBQOIHw==", - "dev": true, - "dependencies": { - "brace-expansion": "^1.1.7" - }, - "engines": { - "node": "*" - } - }, - "node_modules/typechain/node_modules/mkdirp": { - "version": "1.0.4", - "resolved": "https://registry.npmjs.org/mkdirp/-/mkdirp-1.0.4.tgz", - "integrity": "sha512-vVqVZQyf3WLx2Shd0qJ9xuvqgAyKPLAiqITEtqW0oIUjzo3PePDd6fW9iFz30ef7Ysp/oiWqbhszeGWW2T6Gzw==", - "dev": true, - "bin": { - "mkdirp": "bin/cmd.js" - }, - "engines": { - "node": ">=10" - } - }, - "node_modules/typechain/node_modules/prettier": { - "version": "2.8.8", - "resolved": "https://registry.npmjs.org/prettier/-/prettier-2.8.8.tgz", - "integrity": "sha512-tdN8qQGvNjw4CHbY+XXk0JgCXn9QiF21a55rBe5LJAU+kDyC4WQn4+awm2Xfk2lQMk5fKup9XgzTZtGkjBdP9Q==", - "dev": true, - "bin": { - "prettier": "bin-prettier.js" - }, - "engines": { - "node": ">=10.13.0" - }, - "funding": { - "url": "https://github.com/prettier/prettier?sponsor=1" - } - }, - "node_modules/typechain/node_modules/universalify": { - "version": "0.1.2", - "resolved": "https://registry.npmjs.org/universalify/-/universalify-0.1.2.tgz", - "integrity": "sha512-rBJeI5CXAlmy1pV+617WB9J63U6XcazHHF2f2dbJix4XzpUF0RS3Zbj0FGIOCAva5P/d/GBOYaACQ1w+0azUkg==", - "dev": true, - "engines": { - "node": ">= 4.0.0" - } - }, "node_modules/typed-array-buffer": { "version": "1.0.1", "resolved": "https://registry.npmjs.org/typed-array-buffer/-/typed-array-buffer-1.0.1.tgz", @@ -14844,15 +14500,6 @@ "node": ">=14.17" } }, - "node_modules/typical": { - "version": "4.0.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-4.0.0.tgz", - "integrity": "sha512-VAH4IvQ7BDFYglMd7BPRDfLgxZZX4O4TFcRDA6EN5X7erNJJq+McIEp8np9aVtxrCJ6qx4GTYVfOWNjcqwZgRw==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/ultron": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/ultron/-/ultron-1.1.1.tgz", @@ -15636,28 +15283,6 @@ "node": ">=0.10.0" } }, - "node_modules/wordwrapjs": { - "version": "4.0.1", - "resolved": "https://registry.npmjs.org/wordwrapjs/-/wordwrapjs-4.0.1.tgz", - "integrity": "sha512-kKlNACbvHrkpIw6oPeYDSmdCTu2hdMHoyXLTcUKala++lx5Y+wjJ/e474Jqv5abnVmwxw08DiTuHmw69lJGksA==", - "dev": true, - "dependencies": { - "reduce-flatten": "^2.0.0", - "typical": "^5.2.0" - }, - "engines": { - "node": ">=8.0.0" - } - }, - "node_modules/wordwrapjs/node_modules/typical": { - "version": "5.2.0", - "resolved": "https://registry.npmjs.org/typical/-/typical-5.2.0.tgz", - "integrity": "sha512-dvdQgNDNJo+8B2uBQoqdb11eUCE1JQXhvjC/CZtgvZseVd5TYMXnq0+vuUemXbd/Se29cTaUuPX3YIc2xgbvIg==", - "dev": true, - "engines": { - "node": ">=8" - } - }, "node_modules/wrap-ansi": { "version": "7.0.0", "resolved": "https://registry.npmjs.org/wrap-ansi/-/wrap-ansi-7.0.0.tgz", @@ -15770,15 +15395,16 @@ } }, "node_modules/ws": { - "version": "8.5.0", - "resolved": "https://registry.npmjs.org/ws/-/ws-8.5.0.tgz", - "integrity": "sha512-BWX0SWVgLPzYwF8lTzEy1egjhS4S4OEAHfsO8o65WOVsrnSRGaSiUaa9e0ggGlkMTtBlmOpEXiie9RUcBO86qg==", + "version": "8.17.1", + "resolved": "https://registry.npmjs.org/ws/-/ws-8.17.1.tgz", + "integrity": "sha512-6XQFvXTkbfUOZOKKILFG1PDK2NDQs4azKQl26T0YS5CxqWLgXajbPZ+h4gZekJyRqFU8pvnbAbbs/3TgRPy+GQ==", + "license": "MIT", "engines": { "node": ">=10.0.0" }, "peerDependencies": { "bufferutil": "^4.0.1", - "utf-8-validate": "^5.0.2" + "utf-8-validate": ">=5.0.2" }, "peerDependenciesMeta": { "bufferutil": { diff --git a/package.json b/package.json index 71de670..1c3ceff 100644 --- a/package.json +++ b/package.json @@ -29,12 +29,12 @@ "devDependencies": { "@commitlint/cli": "^18.6.1", "@commitlint/config-conventional": "^18.6.2", - "@typechain/ethers-v6": "^0.5.1", "@types/express": "^4.17.21", "@types/jest": "^29.5.12", "@types/node": "^20.11.19", "@typescript-eslint/eslint-plugin": "^6.21.0", "@typescript-eslint/parser": "^6.21.0", + "abitype": "^1.0.5", "cz-conventional-changelog": "^3.3.0", "eslint": "^8.56.0", "eslint-config-airbnb-base": "^15.0.0", @@ -48,7 +48,6 @@ "prettier": "^3.2.5", "ts-jest": "^29.1.2", "ts-node": "^10.9.2", - "typechain": "^8.3.2", "typescript": "^5.3.3" }, "dependencies": { @@ -56,7 +55,7 @@ "@safe-global/protocol-kit": "^3.0.1", "@safe-global/safe-core-sdk-types": "^4.0.1", "dotenv": "^16.4.5", - "ethers": "^6.11.1", + "ethers": "^6.13.2", "express": "^4.18.2", "express-validator": "^7.0.1", "graphql-request": "^6.1.0", diff --git a/src/abi/AddressDriver.json b/src/abi/AddressDriver.json deleted file mode 100644 index 5c26065..0000000 --- a/src/abi/AddressDriver.json +++ /dev/null @@ -1,13 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "address", "name": "addr", "type": "address" } - ], - "name": "calcAccountId", - "outputs": [ - { "internalType": "uint256", "name": "accountId", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/src/abi/RepoDriver.json b/src/abi/RepoDriver.json deleted file mode 100644 index fe2d6a9..0000000 --- a/src/abi/RepoDriver.json +++ /dev/null @@ -1,14 +0,0 @@ -[ - { - "inputs": [ - { "internalType": "enum Forge", "name": "forge", "type": "uint8" }, - { "internalType": "bytes", "name": "name", "type": "bytes" } - ], - "name": "calcAccountId", - "outputs": [ - { "internalType": "uint256", "name": "accountId", "type": "uint256" } - ], - "stateMutability": "view", - "type": "function" - } -] diff --git a/src/appSettings.ts b/src/appSettings.ts index b1d2fd1..e789012 100644 --- a/src/appSettings.ts +++ b/src/appSettings.ts @@ -1,13 +1,14 @@ import dotenv from 'dotenv'; import shouldNeverHappen from './application/shouldNeverHappen'; -import { networks } from './application/networks'; +import type { ChainId } from './application/network'; +import { getNetwork } from './application/network'; +import { getNetworkConfig } from './application/networkConfig'; dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); const appSettings = { port: parseInt(process.env.PORT || '5001', 10), nodeEnv: process.env.NODE_ENV, - network: process.env.NETWORK, dbHost: process.env.DB_HOST, dbPort: parseInt( process.env.DB_PORT || shouldNeverHappen('Missing database port.'), @@ -31,13 +32,18 @@ const appSettings = { })(), graphQlToken: process.env.GRAPHQL_TOKEN, rpcUrl: process.env.RPC_URL, - chainId: networks[process.env.NETWORK as keyof typeof networks], - addressDriverAddress: - process.env.ADDRESS_DRIVER_ADDRESS || - shouldNeverHappen(`Missing 'AddressDriver' address.`), - repoDriverAddress: - process.env.REPO_DRIVER_ADDRESS || - shouldNeverHappen(`Missing RepoDriver address.`), + network: getNetwork( + parseInt( + process.env.CHAIN_ID || shouldNeverHappen('Missing CHAIN_ID'), + 10, + ) as ChainId, + ), + networkConfig: getNetworkConfig( + parseInt( + process.env.CHAIN_ID || shouldNeverHappen('Missing CHAIN_ID'), + 10, + ) as ChainId, + ), authStrategy: process.env.AUTH_STRATEGY || 'signature', apiKey: process.env.API_KEY, }; diff --git a/src/application/Auth.ts b/src/application/Auth.ts index 9ff2edb..8f82168 100644 --- a/src/application/Auth.ts +++ b/src/application/Auth.ts @@ -83,7 +83,7 @@ export class Auth implements IAuthStrategy { `Verifying reconstructed message '${message}' with signature '${signature}' for signer '${signerAddress}'...`, ); - const isEoa = !(await isSafe(appSettings.chainId, signerAddress)); + const isEoa = !(await isSafe(appSettings.network.chainId, signerAddress)); if (isEoa) { this._logger.info(`Signer '${signerAddress}' is EOA.`); @@ -197,7 +197,7 @@ export const REVEAL_VOTES_MESSAGE_TEMPLATE = ( votingRoundId: UUID, currentTime: Date, ) => - `Reveal the votes for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`; + `Reveal the votes for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`; export const SET_NOMINATION_STATUS_MESSAGE_TEMPLATE = ( publisherAddress: Address, @@ -205,7 +205,7 @@ export const SET_NOMINATION_STATUS_MESSAGE_TEMPLATE = ( currentTime: Date, nominations: { accountId: AccountId; status: NominationStatus }[], ) => - `Setting nominations statuses for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The statuses are: ${JSON.stringify( + `Setting nominations statuses for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The statuses are: ${JSON.stringify( nominations, )}.`; @@ -215,14 +215,14 @@ export const NOMINATE_MESSAGE_TEMPLATE = ( currentTime: Date, nomination: NominationReceiver, ) => - `Nominating receiver for voting round with ID ${votingRoundId}, nominated by ${nominatedBy}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The nomination is: ${JSON.stringify(nomination)})`; + `Nominating receiver for voting round with ID ${votingRoundId}, nominated by ${nominatedBy}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The nomination is: ${JSON.stringify(nomination)})`; export const REVEAL_RESULT_MESSAGE_TEMPLATE = ( publisherAddress: Address, votingRoundId: UUID, currentTime: Date, ) => - `Reveal the result for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`; + `Reveal the result for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`; export const VOTE_MESSAGE_TEMPLATE = ( currentTime: Date, @@ -249,7 +249,7 @@ export const VOTE_MESSAGE_TEMPLATE = ( }) .sort(); - return `Submit the vote for address ${voterAddress}, for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The receivers for this vote are: ${JSON.stringify(sortedReceivers)}`; + return `Submit the vote for address ${voterAddress}, for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The receivers for this vote are: ${JSON.stringify(sortedReceivers)}`; }; export const DELETE_VOTING_ROUND_MESSAGE_TEMPLATE = ( @@ -257,7 +257,7 @@ export const DELETE_VOTING_ROUND_MESSAGE_TEMPLATE = ( publisherAddress: Address, votingRoundId: string, ) => - `Delete the voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`; + `Delete the voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`; export const START_VOTING_ROUND_MESSAGE_TEMPLATE = ( currentTime: Date, @@ -267,7 +267,7 @@ export const START_VOTING_ROUND_MESSAGE_TEMPLATE = ( ) => { const sortedCollaborators = collaborators.sort(); - return `Create a new voting round for the Drip List with ID ${dripListId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The voters for this round are: ${JSON.stringify(sortedCollaborators)}`; + return `Create a new voting round for the Drip List with ID ${dripListId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The voters for this round are: ${JSON.stringify(sortedCollaborators)}`; }; export const CREATE_COLLABORATIVE_LIST_MESSAGE_TEMPLATE = ( @@ -277,8 +277,8 @@ export const CREATE_COLLABORATIVE_LIST_MESSAGE_TEMPLATE = ( ) => { const sortedCollaborators = collaborators.sort(); - return `Create a new collaborative Drip List owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The voters for this list are: ${JSON.stringify(sortedCollaborators)}`; + return `Create a new collaborative Drip List owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The voters for this list are: ${JSON.stringify(sortedCollaborators)}`; }; export const REVEAL_VOTE = (votingRoundId: UUID, currentTime: Date) => - `Reveal my vote for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`; + `Reveal my vote for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`; diff --git a/src/application/ReceiverMapper.ts b/src/application/ReceiverMapper.ts index 9d59f34..e84a832 100644 --- a/src/application/ReceiverMapper.ts +++ b/src/application/ReceiverMapper.ts @@ -12,7 +12,6 @@ import type { ProjectReceiver, Receiver, } from '../domain/votingRoundAggregate/Vote'; -import type { AddressDriver, RepoDriver } from '../generated/contracts'; import type { AddressNominationReceiver, DripListNominationReceiver, @@ -30,12 +29,20 @@ import type { ReceiverDto, } from './dtos'; import type { AllowedReceiverData } from '../domain/allowedReceiver/AllowedReceiver'; +import type { + AddressDriverRead, + OxString, + RepoDriverRead, +} from './contractClients/contractClients'; export default class ReceiverMapper implements IReceiverMapper { - private readonly _repoDriver: RepoDriver; - private readonly _addressDriver: AddressDriver; + private readonly _repoDriver: RepoDriverRead; + private readonly _addressDriver: AddressDriverRead; - public constructor(repoDriver: RepoDriver, addressDriver: AddressDriver) { + public constructor( + repoDriver: RepoDriverRead, + addressDriver: AddressDriverRead, + ) { this._repoDriver = repoDriver; this._addressDriver = addressDriver; } @@ -44,24 +51,29 @@ export default class ReceiverMapper implements IReceiverMapper { receiverDto: AllowedReceiverDto, ): Promise { if ('address' in receiverDto) { + const accountId = this._addressDriver({ + functionName: 'calcAccountId', + args: [receiverDto.address as OxString], + }).toString() as AddressDriverId; + return { ...receiverDto, - accountId: ( - await this._addressDriver.calcAccountId(receiverDto.address) - ).toString() as AddressDriverId, + accountId, }; } if ('url' in receiverDto) { const { username, repoName } = parseGitHubUrl(receiverDto.url); const projectName = `${username}/${repoName}`; + const accountId = ( + await this._repoDriver({ + functionName: 'calcAccountId', + args: [0, hexlify(toUtf8Bytes(`${projectName}`)) as OxString], + }) + ).toString() as ProjectId; + return { ...receiverDto, - accountId: ( - await this._repoDriver.calcAccountId( - 0, - hexlify(toUtf8Bytes(`${projectName}`)), - ) - ).toString() as ProjectId, + accountId, }; } @@ -74,24 +86,30 @@ export default class ReceiverMapper implements IReceiverMapper { public async mapToReceiver(receiverDto: ReceiverDto): Promise { if ('address' in receiverDto) { + const accountId = this._addressDriver({ + functionName: 'calcAccountId', + args: [receiverDto.address as OxString], + }).toString() as AddressDriverId; + return { ...receiverDto, - accountId: ( - await this._addressDriver.calcAccountId(receiverDto.address) - ).toString() as AddressDriverId, + accountId, } satisfies AddressReceiver; } if ('url' in receiverDto) { const { username, repoName } = parseGitHubUrl(receiverDto.url); const projectName = `${username}/${repoName}`; + + const accountId = ( + await this._repoDriver({ + functionName: 'calcAccountId', + args: [0, hexlify(toUtf8Bytes(`${projectName}`)) as OxString], + }) + ).toString() as ProjectId; + return { ...receiverDto, - accountId: ( - await this._repoDriver.calcAccountId( - 0, - hexlify(toUtf8Bytes(`${projectName}`)), - ) - ).toString() as ProjectId, + accountId, } satisfies ProjectReceiver; } @@ -103,24 +121,30 @@ export default class ReceiverMapper implements IReceiverMapper { receiverDto: NominationDto, ): Promise { if ('address' in receiverDto) { + const accountId = this._addressDriver({ + functionName: 'calcAccountId', + args: [receiverDto.address as OxString], + }).toString() as AddressDriverId; + return { ...receiverDto, - accountId: ( - await this._addressDriver.calcAccountId(receiverDto.address) - ).toString() as AddressDriverId, + accountId, } satisfies AddressNominationReceiver; } if ('url' in receiverDto) { const { username, repoName } = parseGitHubUrl(receiverDto.url); const projectName = `${username}/${repoName}`; + + const accountId = ( + await this._repoDriver({ + functionName: 'calcAccountId', + args: [0, hexlify(toUtf8Bytes(`${projectName}`)) as OxString], + }) + ).toString() as ProjectId; + return { ...receiverDto, - accountId: ( - await this._repoDriver.calcAccountId( - 0, - hexlify(toUtf8Bytes(`${projectName}`)), - ) - ).toString() as ProjectId, + accountId, } satisfies ProjectNominationReceiver; } diff --git a/src/application/SafeAdapter.ts b/src/application/SafeAdapter.ts index 401fd4f..f3d7b4f 100644 --- a/src/application/SafeAdapter.ts +++ b/src/application/SafeAdapter.ts @@ -30,7 +30,7 @@ export class SafeAdapter implements ISafeAdapter { safeTransactionHash: string, ): Promise { const safeApiKit = new SafeApiKit({ - chainId: BigInt(appSettings.chainId), + chainId: BigInt(appSettings.network.chainId), }); return safeApiKit.getTransaction(safeTransactionHash); @@ -40,13 +40,13 @@ export class SafeAdapter implements ISafeAdapter { export class UnsupportedSafeOperationsAdapter implements ISafeAdapter { isValidSignature(): Promise { throw new Error( - `Safe operations are not supported on chain ${appSettings.chainId}`, + `Safe operations are not supported on chain ${appSettings.network.chainId}`, ); } getTransaction(): Promise { throw new Error( - `Safe operations are not supported on chain ${appSettings.chainId}`, + `Safe operations are not supported on chain ${appSettings.network.chainId}`, ); } } diff --git a/src/application/contractClients/AddressDriverAbi.ts b/src/application/contractClients/AddressDriverAbi.ts new file mode 100644 index 0000000..ed5f2ed --- /dev/null +++ b/src/application/contractClients/AddressDriverAbi.ts @@ -0,0 +1,396 @@ +export const addressDriverAbi = [ + { + inputs: [ + { internalType: 'contract Drips', name: 'drips_', type: 'address' }, + { internalType: 'address', name: 'forwarder', type: 'address' }, + { internalType: 'uint32', name: 'driverId_', type: 'uint32' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'currentAdmin', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'NewAdminProposed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Unpaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'acceptAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'admin', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'allPausers', + outputs: [ + { + internalType: 'address[]', + name: 'pausersList', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'addr', type: 'address' }], + name: 'calcAccountId', + outputs: [{ internalType: 'uint256', name: 'accountId', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { internalType: 'address', name: 'transferTo', type: 'address' }, + ], + name: 'collect', + outputs: [{ internalType: 'uint128', name: 'amt', type: 'uint128' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'drips', + outputs: [{ internalType: 'contract Drips', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'driverId', + outputs: [{ internalType: 'uint32', name: '', type: 'uint32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'bytes32', name: 'key', type: 'bytes32' }, + { internalType: 'bytes', name: 'value', type: 'bytes' }, + ], + internalType: 'struct AccountMetadata[]', + name: 'accountMetadata', + type: 'tuple[]', + }, + ], + name: 'emitAccountMetadata', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'receiver', type: 'uint256' }, + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { internalType: 'uint128', name: 'amt', type: 'uint128' }, + ], + name: 'give', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'grantPauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'implementation', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'isPauser', + outputs: [{ internalType: 'bool', name: 'isAddrPauser', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'forwarder', type: 'address' }], + name: 'isTrustedForwarder', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'newAdmin', type: 'address' }], + name: 'proposeNewAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'proposedAdmin', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'revokePauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { internalType: 'uint32', name: 'weight', type: 'uint32' }, + ], + internalType: 'struct SplitsReceiver[]', + name: 'receivers', + type: 'tuple[]', + }, + ], + name: 'setSplits', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + internalType: 'StreamConfig', + name: 'config', + type: 'uint256', + }, + ], + internalType: 'struct StreamReceiver[]', + name: 'currReceivers', + type: 'tuple[]', + }, + { internalType: 'int128', name: 'balanceDelta', type: 'int128' }, + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + internalType: 'StreamConfig', + name: 'config', + type: 'uint256', + }, + ], + internalType: 'struct StreamReceiver[]', + name: 'newReceivers', + type: 'tuple[]', + }, + { internalType: 'uint32', name: 'maxEndHint1', type: 'uint32' }, + { internalType: 'uint32', name: 'maxEndHint2', type: 'uint32' }, + { internalType: 'address', name: 'transferTo', type: 'address' }, + ], + name: 'setStreams', + outputs: [ + { internalType: 'int128', name: 'realBalanceDelta', type: 'int128' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unpause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, +] as const; + +export type AddressDriverAbi = typeof addressDriverAbi; diff --git a/src/application/contractClients/RepoDriverAbi.ts b/src/application/contractClients/RepoDriverAbi.ts new file mode 100644 index 0000000..f743674 --- /dev/null +++ b/src/application/contractClients/RepoDriverAbi.ts @@ -0,0 +1,575 @@ +export const repoDriverAbi = [ + { + inputs: [ + { internalType: 'contract Drips', name: 'drips_', type: 'address' }, + { internalType: 'address', name: 'forwarder', type: 'address' }, + { internalType: 'uint32', name: 'driverId_', type: 'uint32' }, + ], + stateMutability: 'nonpayable', + type: 'constructor', + }, + { inputs: [], name: 'InvalidShortString', type: 'error' }, + { + inputs: [{ internalType: 'string', name: 'str', type: 'string' }], + name: 'StringTooLong', + type: 'error', + }, + { + anonymous: false, + inputs: [ + { + indexed: false, + internalType: 'address', + name: 'previousAdmin', + type: 'address', + }, + { + indexed: false, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'AdminChanged', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'contract OperatorInterface', + name: 'operator', + type: 'address', + }, + { + indexed: true, + internalType: 'bytes32', + name: 'jobId', + type: 'bytes32', + }, + { + indexed: false, + internalType: 'uint96', + name: 'defaultFee', + type: 'uint96', + }, + ], + name: 'AnyApiOperatorUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'beacon', + type: 'address', + }, + ], + name: 'BeaconUpgraded', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'currentAdmin', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'newAdmin', + type: 'address', + }, + ], + name: 'NewAdminProposed', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'accountId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'enum Forge', + name: 'forge', + type: 'uint8', + }, + { + indexed: false, + internalType: 'bytes', + name: 'name', + type: 'bytes', + }, + ], + name: 'OwnerUpdateRequested', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'uint256', + name: 'accountId', + type: 'uint256', + }, + { + indexed: false, + internalType: 'address', + name: 'owner', + type: 'address', + }, + ], + name: 'OwnerUpdated', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Paused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserGranted', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + { + indexed: true, + internalType: 'address', + name: 'admin', + type: 'address', + }, + ], + name: 'PauserRevoked', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'pauser', + type: 'address', + }, + ], + name: 'Unpaused', + type: 'event', + }, + { + anonymous: false, + inputs: [ + { + indexed: true, + internalType: 'address', + name: 'implementation', + type: 'address', + }, + ], + name: 'Upgraded', + type: 'event', + }, + { + inputs: [], + name: 'acceptAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'admin', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'allPausers', + outputs: [ + { + internalType: 'address[]', + name: 'pausersList', + type: 'address[]', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'anyApiOperator', + outputs: [ + { + internalType: 'contract OperatorInterface', + name: 'operator', + type: 'address', + }, + { internalType: 'bytes32', name: 'jobId', type: 'bytes32' }, + { internalType: 'uint96', name: 'defaultFee', type: 'uint96' }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'enum Forge', name: 'forge', type: 'uint8' }, + { internalType: 'bytes', name: 'name', type: 'bytes' }, + ], + name: 'calcAccountId', + outputs: [{ internalType: 'uint256', name: 'accountId', type: 'uint256' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { internalType: 'address', name: 'transferTo', type: 'address' }, + ], + name: 'collect', + outputs: [{ internalType: 'uint128', name: 'amt', type: 'uint128' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'drips', + outputs: [{ internalType: 'contract Drips', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'driverId', + outputs: [{ internalType: 'uint32', name: '', type: 'uint32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + components: [ + { internalType: 'bytes32', name: 'key', type: 'bytes32' }, + { internalType: 'bytes', name: 'value', type: 'bytes' }, + ], + internalType: 'struct AccountMetadata[]', + name: 'accountMetadata', + type: 'tuple[]', + }, + ], + name: 'emitAccountMetadata', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { internalType: 'uint256', name: 'receiver', type: 'uint256' }, + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { internalType: 'uint128', name: 'amt', type: 'uint128' }, + ], + name: 'give', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'grantPauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'implementation', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract OperatorInterface', + name: 'operator', + type: 'address', + }, + { internalType: 'bytes32', name: 'jobId', type: 'bytes32' }, + { internalType: 'uint96', name: 'defaultFee', type: 'uint96' }, + ], + name: 'initializeAnyApiOperator', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'isPaused', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'isPauser', + outputs: [{ internalType: 'bool', name: 'isAddrPauser', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'forwarder', type: 'address' }], + name: 'isTrustedForwarder', + outputs: [{ internalType: 'bool', name: '', type: 'bool' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'linkToken', + outputs: [ + { + internalType: 'contract LinkTokenInterface', + name: '', + type: 'address', + }, + ], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [ + { internalType: 'address', name: '', type: 'address' }, + { internalType: 'uint256', name: 'amount', type: 'uint256' }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'onTokenTransfer', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'uint256', name: 'accountId', type: 'uint256' }], + name: 'ownerOf', + outputs: [{ internalType: 'address', name: 'owner', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'pause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'newAdmin', type: 'address' }], + name: 'proposeNewAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'proposedAdmin', + outputs: [{ internalType: 'address', name: '', type: 'address' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'proxiableUUID', + outputs: [{ internalType: 'bytes32', name: '', type: 'bytes32' }], + stateMutability: 'view', + type: 'function', + }, + { + inputs: [], + name: 'renounceAdmin', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'enum Forge', name: 'forge', type: 'uint8' }, + { internalType: 'bytes', name: 'name', type: 'bytes' }, + ], + name: 'requestUpdateOwner', + outputs: [{ internalType: 'uint256', name: 'accountId', type: 'uint256' }], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [{ internalType: 'address', name: 'pauser', type: 'address' }], + name: 'revokePauser', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { internalType: 'uint32', name: 'weight', type: 'uint32' }, + ], + internalType: 'struct SplitsReceiver[]', + name: 'receivers', + type: 'tuple[]', + }, + ], + name: 'setSplits', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { internalType: 'contract IERC20', name: 'erc20', type: 'address' }, + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + internalType: 'StreamConfig', + name: 'config', + type: 'uint256', + }, + ], + internalType: 'struct StreamReceiver[]', + name: 'currReceivers', + type: 'tuple[]', + }, + { internalType: 'int128', name: 'balanceDelta', type: 'int128' }, + { + components: [ + { internalType: 'uint256', name: 'accountId', type: 'uint256' }, + { + internalType: 'StreamConfig', + name: 'config', + type: 'uint256', + }, + ], + internalType: 'struct StreamReceiver[]', + name: 'newReceivers', + type: 'tuple[]', + }, + { internalType: 'uint32', name: 'maxEndHint1', type: 'uint32' }, + { internalType: 'uint32', name: 'maxEndHint2', type: 'uint32' }, + { internalType: 'address', name: 'transferTo', type: 'address' }, + ], + name: 'setStreams', + outputs: [ + { internalType: 'int128', name: 'realBalanceDelta', type: 'int128' }, + ], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [], + name: 'unpause', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'contract OperatorInterface', + name: 'operator', + type: 'address', + }, + { internalType: 'bytes32', name: 'jobId', type: 'bytes32' }, + { internalType: 'uint96', name: 'defaultFee', type: 'uint96' }, + ], + name: 'updateAnyApiOperator', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { internalType: 'bytes32', name: 'requestId', type: 'bytes32' }, + { internalType: 'bytes', name: 'ownerRaw', type: 'bytes' }, + ], + name: 'updateOwnerByAnyApi', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + ], + name: 'upgradeTo', + outputs: [], + stateMutability: 'nonpayable', + type: 'function', + }, + { + inputs: [ + { + internalType: 'address', + name: 'newImplementation', + type: 'address', + }, + { internalType: 'bytes', name: 'data', type: 'bytes' }, + ], + name: 'upgradeToAndCall', + outputs: [], + stateMutability: 'payable', + type: 'function', + }, +] as const; + +export type RepoDriverAbi = typeof repoDriverAbi; diff --git a/src/application/contractClients/contractClients.ts b/src/application/contractClients/contractClients.ts new file mode 100644 index 0000000..61675dc --- /dev/null +++ b/src/application/contractClients/contractClients.ts @@ -0,0 +1,90 @@ +import type { + AbiFunction, + AbiParametersToPrimitiveTypes, + ExtractAbiFunction, + ExtractAbiFunctionNames, +} from 'abitype'; +import { Contract } from 'ethers'; +import type { AddressDriverAbi } from './AddressDriverAbi'; +import { addressDriverAbi } from './AddressDriverAbi'; +import provider from '../provider'; +import { getNetworkConfig } from '../networkConfig'; +import type { RepoDriverAbi } from './RepoDriverAbi'; +import { repoDriverAbi } from './RepoDriverAbi'; + +export type OxString = `0x${string}`; + +export type UnwrappedEthersResult = T extends [infer U] + ? U + : T extends readonly [infer U] + ? U + : T; + +export default function unwrapEthersResult( + result: T | T[], +): UnwrappedEthersResult | UnwrappedEthersResult { + if (Array.isArray(result) && result.length === 1) { + return result[0] as UnwrappedEthersResult; + } + + return result as UnwrappedEthersResult; +} + +export type AddressDriverRead = typeof executeAddressDriverReadMethod; + +export async function executeAddressDriverReadMethod< + functionName extends ExtractAbiFunctionNames< + AddressDriverAbi, + 'pure' | 'view' + >, + abiFunction extends AbiFunction = ExtractAbiFunction< + AddressDriverAbi, + functionName + >, +>(config: { + functionName: + | functionName + | ExtractAbiFunctionNames; + args: AbiParametersToPrimitiveTypes; +}): Promise< + UnwrappedEthersResult< + AbiParametersToPrimitiveTypes + > +> { + const { functionName: func, args } = config; + + const addressDriverAddress = getNetworkConfig().ADDRESS_DRIVER; + const addressDriver = new Contract( + addressDriverAddress, + addressDriverAbi, + provider, + ); + + return unwrapEthersResult(await addressDriver[func](...(args as any))); +} + +export type RepoDriverRead = typeof executeRepoDriverReadMethod; + +export async function executeRepoDriverReadMethod< + functionName extends ExtractAbiFunctionNames, + abiFunction extends AbiFunction = ExtractAbiFunction< + RepoDriverAbi, + functionName + >, +>(config: { + functionName: + | functionName + | ExtractAbiFunctionNames; + args: AbiParametersToPrimitiveTypes; +}): Promise< + UnwrappedEthersResult< + AbiParametersToPrimitiveTypes + > +> { + const { functionName: func, args } = config; + + const repoDriverAddress = getNetworkConfig().REPO_DRIVER; + const repoDriver = new Contract(repoDriverAddress, repoDriverAbi, provider); + + return unwrapEthersResult(await repoDriver[func](...(args as any))); +} diff --git a/src/application/network.ts b/src/application/network.ts new file mode 100644 index 0000000..815ddf0 --- /dev/null +++ b/src/application/network.ts @@ -0,0 +1,50 @@ +import dotenv from 'dotenv'; +import { assert } from './assert'; + +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); + +export type Network = { + chainId: ChainId; + name: string; +}; + +const SUPPORTED_CHAIN_IDS = [1, 80002, 11155420, 11155111, 84532] as const; +export type ChainId = (typeof SUPPORTED_CHAIN_IDS)[number]; + +export type ValueForEachSupportedChain = Record< + (typeof SUPPORTED_CHAIN_IDS)[number], + T +>; + +const NETWORK_NAMES: ValueForEachSupportedChain = { + 1: 'mainnet', + 80002: 'polygon_amoy', + 11155420: 'optimism_sepolia', + 11155111: 'sepolia', + 84532: 'base_sepolia', +}; + +export function isSupportedChainId(chainId: number): chainId is ChainId { + return SUPPORTED_CHAIN_IDS.includes(chainId as ChainId); +} + +export function isSafeUnsupportedNetwork(chainId: number): chainId is ChainId { + return [11155420, 80002].includes(chainId as ChainId); +} + +const configuredChainId = Number(process.env.CHAIN_ID); +assert( + isSupportedChainId(configuredChainId), + 'Missing or invalid CHAIN_ID env variable.', +); + +export function getNetwork(chainId: ChainId): Network { + assert(isSupportedChainId(chainId), 'Unsupported chain id'); + + return { + chainId, + name: NETWORK_NAMES[chainId], + }; +} + +export default getNetwork(configuredChainId); diff --git a/src/application/networkConfig.ts b/src/application/networkConfig.ts new file mode 100644 index 0000000..e6b7b29 --- /dev/null +++ b/src/application/networkConfig.ts @@ -0,0 +1,57 @@ +import dotenv from 'dotenv'; +import { assert } from './assert'; +import type { ChainId } from './network'; +import shouldNeverHappen from './shouldNeverHappen'; + +dotenv.config({ path: `.env.${process.env.NODE_ENV}` }); + +export type NetworkConfig = { + CHAIN: string; + ADDRESS_DRIVER: string; + REPO_DRIVER: string; +}; + +export const networkConfigs = { + // Mainnet + 1: { + CHAIN: 'mainnet', + ADDRESS_DRIVER: '0x1455d9bD6B98f95dd8FEB2b3D60ed825fcef0610', + REPO_DRIVER: '0x770023d55D09A9C110694827F1a6B32D5c2b373E', + }, + // Sepolia + 11155111: { + CHAIN: 'sepolia', + ADDRESS_DRIVER: '0x70E1E1437AeFe8024B6780C94490662b45C3B567', + REPO_DRIVER: '0xa71bdf410D48d4AA9aE1517A69D7E1Ef0c179b2B', + }, + // Polygon Amoy + 80002: { + CHAIN: 'amoy', + ADDRESS_DRIVER: '0x004310a6d47893Dd6e443cbE471c24aDA1e6c619', + REPO_DRIVER: '0x54372850Db72915Fd9C5EC745683EB607b4a8642', + }, + // Optimism Sepolia + 11155420: { + CHAIN: 'optimism-sepolia', + ADDRESS_DRIVER: '0x70E1E1437AeFe8024B6780C94490662b45C3B567', + REPO_DRIVER: '0xa71bdf410D48d4AA9aE1517A69D7E1Ef0c179b2B', + }, + // Base Sepolia + 84532: { + CHAIN: 'base-sepolia', + ADDRESS_DRIVER: '0x004310a6d47893Dd6e443cbE471c24aDA1e6c619', + REPO_DRIVER: '0x54372850Db72915Fd9C5EC745683EB607b4a8642', + }, +} as const; + +export function getNetworkConfig( + chainId: ChainId = parseInt( + process.env.CHAIN_ID || shouldNeverHappen('Missing CHAIN_ID'), + 10, + ) as ChainId, +): NetworkConfig { + const config = networkConfigs[chainId]; + assert(config, `No network config found for chainId ${chainId}`); + + return config; +} diff --git a/src/application/networks.ts b/src/application/networks.ts deleted file mode 100644 index eceeb19..0000000 --- a/src/application/networks.ts +++ /dev/null @@ -1,12 +0,0 @@ -export const networks = { - mainnet: 1, - goerli: 5, - sepolia: 11155111, - 'optimism-sepolia': 11155420, - 'polygon-amoy': 80002, -} as const; - -export const safeUnsupportedNetworks = [ - 'optimism-sepolia', - 'polygon-amoy', -] as const; diff --git a/src/environment.d.ts b/src/environment.d.ts index 6e82609..749c908 100644 --- a/src/environment.d.ts +++ b/src/environment.d.ts @@ -3,7 +3,7 @@ declare global { interface ProcessEnv { NODE_ENV: 'local' | 'goerli' | 'mainnet'; PORT: string; - NETWORK: 'sepolia' | 'mainnet' | 'optimism-sepolia'; + CHAIN_ID: '1' | '80002' | '11155420' | '11155111' | '84532'; DB_HOST: string; DB_PORT: string; DB_USER: string; @@ -13,8 +13,6 @@ declare global { GRAPHQL_URL: string; GRAPHQL_TOKEN: string; RPC_URL: string; - ADDRESS_DRIVER_ADDRESS: string; - REPO_DRIVER_ADDRESS: string; AUTH_STRATEGY: 'signature' | 'dev'; API_KEY: string; } diff --git a/src/infrastructure/AppDataSource.ts b/src/infrastructure/AppDataSource.ts index 8a0f7fb..8a8d968 100644 --- a/src/infrastructure/AppDataSource.ts +++ b/src/infrastructure/AppDataSource.ts @@ -31,7 +31,7 @@ export async function initializeAppDataSource() { ], synchronize: true, logging: false, - schema: network, + schema: network.name, }); await AppDataSource.initialize(); diff --git a/src/infrastructure/logger.ts b/src/infrastructure/logger.ts index 6aceb59..417e381 100644 --- a/src/infrastructure/logger.ts +++ b/src/infrastructure/logger.ts @@ -30,6 +30,6 @@ const productionLogger = winston.createLogger({ }); const logger = - appSettings.network === 'mainnet' ? productionLogger : developmentLogger; + appSettings.network.name === 'mainnet' ? productionLogger : developmentLogger; export default logger; diff --git a/src/main.ts b/src/main.ts index eaaf379..79d78bf 100644 --- a/src/main.ts +++ b/src/main.ts @@ -26,27 +26,26 @@ import GetVotingRoundVotesEndpoint from './features/getVotingRoundVotes/GetVotin import GetVotingRoundVotesUseCase from './features/getVotingRoundVotes/GetVotingRoundVotesUseCase'; import type { IAuthStrategy } from './application/Auth'; import { Auth, DevAuth } from './application/Auth'; -import { - AddressDriver__factory, - RepoDriver__factory, -} from './generated/contracts'; import ReceiverMapper from './application/ReceiverMapper'; import NominateEndpoint from './features/nominate/NominateEndpoint'; import NominateUseCase from './features/nominate/NominateUseCase'; import GetCollaboratorByAddressEndpoint from './features/getCollaboratorByAddress/GetCollaboratorByAddressEndpoint'; import GetCollaboratorByAddressUseCase from './features/getCollaboratorByAddress/GetCollaboratorByAddressUseCase'; -import provider from './application/provider'; import SetNominationsStatusesEndpoint from './features/setNominationsStatuses/SetNominationsStatusesEndpoint'; import SetNominationsStatusesUseCase from './features/setNominationsStatuses/SetNominationsStatusesUseCase'; import VotingRoundMapper from './application/VotingRoundMapper'; import SafeService from './application/SafeService'; -import { safeUnsupportedNetworks } from './application/networks'; import { SafeAdapter, UnsupportedSafeOperationsAdapter, } from './application/SafeAdapter'; import type ISafeAdapter from './application/interfaces/ISafeAdapter'; import AllowedReceiversRepository from './infrastructure/repositories/AllowedReceiversRepository'; +import { isSafeUnsupportedNetwork } from './application/network'; +import { + executeAddressDriverReadMethod, + executeRepoDriverReadMethod, +} from './application/contractClients/contractClients'; export async function main(): Promise { logger.info('Starting the application...'); @@ -75,7 +74,7 @@ export async function main(): Promise { }); let safeAdapter: ISafeAdapter; - if (safeUnsupportedNetworks.includes(appSettings.network as any)) { + if (isSafeUnsupportedNetwork(appSettings.network.chainId)) { safeAdapter = new UnsupportedSafeOperationsAdapter(); } else { safeAdapter = new SafeAdapter(); @@ -102,8 +101,8 @@ export async function main(): Promise { ); const receiverMapper = new ReceiverMapper( - RepoDriver__factory.connect(appSettings.repoDriverAddress, provider), - AddressDriver__factory.connect(appSettings.addressDriverAddress, provider), + executeRepoDriverReadMethod, + executeAddressDriverReadMethod, ); const votingRoundMapper = new VotingRoundMapper(receiverMapper); diff --git a/tests/application/Auth.test.ts b/tests/application/Auth.test.ts index f874ad6..cea88ea 100644 --- a/tests/application/Auth.test.ts +++ b/tests/application/Auth.test.ts @@ -338,7 +338,7 @@ describe('Auth', () => { // Assert expect(message).toBe( - `Reveal the votes for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`, + `Reveal the votes for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`, ); }); }); @@ -366,7 +366,7 @@ describe('Auth', () => { // Assert expect(message).toBe( - `Setting nominations statuses for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The statuses are: ${JSON.stringify( + `Setting nominations statuses for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The statuses are: ${JSON.stringify( nominations, )}.`, ); @@ -395,7 +395,7 @@ describe('Auth', () => { // Assert expect(message).toBe( - `Nominating receiver for voting round with ID ${votingRoundId}, nominated by ${nominatedBy}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The nomination is: ${JSON.stringify(nomination)})`, + `Nominating receiver for voting round with ID ${votingRoundId}, nominated by ${nominatedBy}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The nomination is: ${JSON.stringify(nomination)})`, ); }); }); @@ -416,7 +416,7 @@ describe('Auth', () => { // Assert expect(message).toBe( - `Reveal the result for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`, + `Reveal the result for voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`, ); }); }); @@ -440,7 +440,7 @@ describe('Auth', () => { ['project', 'c', 2], ]; - const expectedMessage = `Submit the vote for address ${voterAddress}, for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The receivers for this vote are: ${JSON.stringify(expectedSortedReceivers)}`; + const expectedMessage = `Submit the vote for address ${voterAddress}, for the voting round with ID ${votingRoundId}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The receivers for this vote are: ${JSON.stringify(expectedSortedReceivers)}`; // Act const message = VOTE_MESSAGE_TEMPLATE( @@ -471,7 +471,7 @@ describe('Auth', () => { // Assert expect(message).toBe( - `Delete the voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}.`, + `Delete the voting round with ID ${votingRoundId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}.`, ); }); }); @@ -486,7 +486,7 @@ describe('Auth', () => { const sortedCollaborators = ['a', 'b', 'c']; - const expectedMessage = `Create a new voting round for the Drip List with ID ${dripListId}, owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The voters for this round are: ${JSON.stringify(sortedCollaborators)}`; + const expectedMessage = `Create a new voting round for the Drip List with ID ${dripListId}, owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The voters for this round are: ${JSON.stringify(sortedCollaborators)}`; // Act const message = START_VOTING_ROUND_MESSAGE_TEMPLATE( @@ -510,7 +510,7 @@ describe('Auth', () => { const sortedCollaborators = ['a', 'b', 'c']; - const expectedMessage = `Create a new collaborative Drip List owned by ${publisherAddress}, on chain ID ${appSettings.chainId}. The current time is ${currentTime.toISOString()}. The voters for this list are: ${JSON.stringify(sortedCollaborators)}`; + const expectedMessage = `Create a new collaborative Drip List owned by ${publisherAddress}, on chain ID ${appSettings.network.chainId}. The current time is ${currentTime.toISOString()}. The voters for this list are: ${JSON.stringify(sortedCollaborators)}`; // Act const message = CREATE_COLLABORATIVE_LIST_MESSAGE_TEMPLATE(