From 0e3f0008e180b3daf0a6e61eaa962c2257c3dd7a Mon Sep 17 00:00:00 2001 From: vignesha22 <82584664+vignesha22@users.noreply.github.com> Date: Mon, 4 Mar 2024 22:32:21 +0530 Subject: [PATCH] PRO-2207-Admin Frontend Changes (#67) * added Admin Config changes --- admin_frontend/.editorconfig | 12 + admin_frontend/Dockerfile | 1 + admin_frontend/config-overrides.js | 23 + admin_frontend/package-lock.json | 885 +++++++++++++++++- admin_frontend/package.json | 5 +- admin_frontend/src/App.js | 27 +- admin_frontend/src/components/ApiKeys.jsx | 628 +++++++------ admin_frontend/src/components/Dashboard.jsx | 712 ++++++++------ admin_frontend/src/components/Header.jsx | 62 +- admin_frontend/src/components/NotFound.jsx | 1 - .../src/components/ProtectedRoute.js | 24 + admin_frontend/src/constants/common.js | 4 +- admin_frontend/src/constants/constants.js | 8 + .../src/constants/defaultNetworks.json | 170 ++++ admin_frontend/src/context/AuthContext.js | 86 ++ .../src/modals/AddERC20Paymaster.jsx | 257 +++++ .../src/modals/AddSupportedNetworksModal.jsx | 211 +++++ admin_frontend/src/modals/CoingeckoId.jsx | 216 +++++ .../src/modals/DeployedPaymasters.jsx | 217 +++++ .../src/modals/ViewERC20Paymaster.jsx | 91 ++ .../src/modals/ViewSupportedNetworksModal.jsx | 77 ++ backend/package.json | 2 +- backend/src/constants/ErrorMessage.ts | 1 + backend/src/migrations/001.default.sql | 2 +- backend/src/plugins/config.ts | 2 + backend/src/routes/admin.ts | 14 + backend/src/server.ts | 8 +- docker-compose.yml | 5 +- frontend/Dockerfile | 1 + frontend/demo.env | 1 + frontend/package-lock.json | 4 +- frontend/package.json | 2 +- 32 files changed, 3144 insertions(+), 615 deletions(-) create mode 100644 admin_frontend/.editorconfig create mode 100644 admin_frontend/config-overrides.js create mode 100644 admin_frontend/src/components/ProtectedRoute.js create mode 100644 admin_frontend/src/constants/constants.js create mode 100644 admin_frontend/src/constants/defaultNetworks.json create mode 100644 admin_frontend/src/context/AuthContext.js create mode 100644 admin_frontend/src/modals/AddERC20Paymaster.jsx create mode 100644 admin_frontend/src/modals/AddSupportedNetworksModal.jsx create mode 100644 admin_frontend/src/modals/CoingeckoId.jsx create mode 100644 admin_frontend/src/modals/DeployedPaymasters.jsx create mode 100644 admin_frontend/src/modals/ViewERC20Paymaster.jsx create mode 100644 admin_frontend/src/modals/ViewSupportedNetworksModal.jsx create mode 100644 frontend/demo.env diff --git a/admin_frontend/.editorconfig b/admin_frontend/.editorconfig new file mode 100644 index 0000000..64d3d6a --- /dev/null +++ b/admin_frontend/.editorconfig @@ -0,0 +1,12 @@ +# EditorConfig is awesome: https://EditorConfig.org + +# top-most EditorConfig file +root = true + +[*] +indent_style = space +indent_size = 2 +end_of_line = lf +charset = utf-8 +trim_trailing_whitespace = true +insert_final_newline = true \ No newline at end of file diff --git a/admin_frontend/Dockerfile b/admin_frontend/Dockerfile index 076083c..801c351 100644 --- a/admin_frontend/Dockerfile +++ b/admin_frontend/Dockerfile @@ -5,5 +5,6 @@ COPY src/ /admin_frontend/src COPY package.json /admin_frontend/ COPY postcss.config.js /admin_frontend/ COPY tailwind.config.js /admin_frontend/ +COPY demo.env /frontend/.env RUN npm install CMD ["npm", "start"] diff --git a/admin_frontend/config-overrides.js b/admin_frontend/config-overrides.js new file mode 100644 index 0000000..ed199c9 --- /dev/null +++ b/admin_frontend/config-overrides.js @@ -0,0 +1,23 @@ +const webpack = require("webpack"); + +module.exports = function override(config) { + const fallback = config.resolve.fallback || {}; + Object.assign(fallback, {}); + config.resolve.fallback = fallback; + config.plugins = (config.plugins || []).concat([ + new webpack.ProvidePlugin({ + process: "process/browser", + Buffer: ["buffer", "Buffer"], + }), + ]); + config.ignoreWarnings = [/Failed to parse source map/]; + config.module.rules.push({ + test: /\.(js|mjs|jsx)$/, + enforce: "pre", + loader: require.resolve("source-map-loader"), + resolve: { + fullySpecified: false, + }, + }); + return config; +}; diff --git a/admin_frontend/package-lock.json b/admin_frontend/package-lock.json index 1a3039e..736fa53 100644 --- a/admin_frontend/package-lock.json +++ b/admin_frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "admin_frontend", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "admin_frontend", - "version": "1.0.0", + "version": "1.0.2", "dependencies": { "@emotion/react": "11.11.3", "@emotion/styled": "11.11.0", @@ -16,6 +16,8 @@ "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "13.4.0", "@testing-library/user-event": "13.5.0", + "buffer": "6.0.3", + "ethers": "5.7.2", "react": "18.2.0", "react-dom": "18.2.0", "react-hot-toast": "2.4.1", @@ -26,6 +28,7 @@ }, "devDependencies": { "autoprefixer": "10.4.16", + "json-loader": "0.5.7", "postcss": "8.4.33", "tailwindcss": "3.4.1" } @@ -2546,6 +2549,697 @@ "node": "^12.22.0 || ^14.17.0 || >=16.0.0" } }, + "node_modules/@ethersproject/abi": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abi/-/abi-5.7.0.tgz", + "integrity": "sha512-351ktp42TiRcYB3H1OP8yajPeAQstMW/yCFokj/AthP9bLHzQFPlOrxOcwYEDkUAICmOHljvN4K39OMTMUa9RA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-provider": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-provider/-/abstract-provider-5.7.0.tgz", + "integrity": "sha512-R41c9UkchKCpAqStMYUpdunjo3pkEvZC3FAwZn5S5MGbXoMQOHIdHItezTETxAO5bevtMApSyEhn9+CHcDsWBw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0" + } + }, + "node_modules/@ethersproject/abstract-signer": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/abstract-signer/-/abstract-signer-5.7.0.tgz", + "integrity": "sha512-a16V8bq1/Cz+TGCkE2OPMTOUDLS3grCpdjoJCYNnVBbdYEMSgKrU0+B90s8b6H+ByYTBZN7a3g76jdIJi7UfKQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/address": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/address/-/address-5.7.0.tgz", + "integrity": "sha512-9wYhYt7aghVGo758POM5nqcOMaE168Q6aRLJZwUmiqSrAungkG74gSSeKEIR7ukixesdRZGPgVqme6vmxs1fkA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/rlp": "^5.7.0" + } + }, + "node_modules/@ethersproject/base64": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/base64/-/base64-5.7.0.tgz", + "integrity": "sha512-Dr8tcHt2mEbsZr/mwTPIQAf3Ai0Bks/7gTw9dSqk1mQvhW3XvRlmDJr/4n+wg1JmCl16NZue17CDh8xb/vZ0sQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0" + } + }, + "node_modules/@ethersproject/basex": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/basex/-/basex-5.7.0.tgz", + "integrity": "sha512-ywlh43GwZLv2Voc2gQVTKBoVQ1mti3d8HK5aMxsfu/nRDnMmNqaSJ3r3n85HBByT8OpoY96SXM1FogC533T4zw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/properties": "^5.7.0" + } + }, + "node_modules/@ethersproject/bignumber": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bignumber/-/bignumber-5.7.0.tgz", + "integrity": "sha512-n1CAdIHRWjSucQO3MC1zPSVgV/6dy/fjL9pMrPP9peL+QxEg9wOsVqwD4+818B6LUEtaXzVHQiuivzRoxPxUGw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "bn.js": "^5.2.1" + } + }, + "node_modules/@ethersproject/bytes": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/bytes/-/bytes-5.7.0.tgz", + "integrity": "sha512-nsbxwgFXWh9NyYWo+U8atvmMsSdKJprTcICAkvbBffT75qDocbuggBU0SJiVK2MuTrp0q+xvLkTnGMPK1+uA9A==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/constants": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/constants/-/constants-5.7.0.tgz", + "integrity": "sha512-DHI+y5dBNvkpYUMiRQyxRBYBefZkJfo70VUkUAsRjcPs47muV9evftfZ0PJVCXYbAiCgght0DtcF9srFQmIgWA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0" + } + }, + "node_modules/@ethersproject/contracts": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/contracts/-/contracts-5.7.0.tgz", + "integrity": "sha512-5GJbzEU3X+d33CdfPhcyS+z8MzsTrBGk/sc+G+59+tPa9yFkl6HQ9D6L0QMgNTA9q8dT0XKxxkyp883XsQvbbg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/transactions": "^5.7.0" + } + }, + "node_modules/@ethersproject/hash": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hash/-/hash-5.7.0.tgz", + "integrity": "sha512-qX5WrQfnah1EFnO5zJv1v46a8HW0+E5xuBBDTwMFZLuVTx0tbU2kkx15NqdjxecrLGatQN9FGQKpb1FKdHCt+g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/hdnode": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/hdnode/-/hdnode-5.7.0.tgz", + "integrity": "sha512-OmyYo9EENBPPf4ERhR7oj6uAtUAhYGqOnIS+jE5pTXvdKBS99ikzq1E7Iv0ZQZ5V36Lqx1qZLeak0Ra16qpeOg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/json-wallets": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/json-wallets/-/json-wallets-5.7.0.tgz", + "integrity": "sha512-8oee5Xgu6+RKgJTkvEMl2wDgSPSAQ9MB/3JYjFV9jlKvcYHUXZC+cQp0njgmxdHkYWn8s6/IqIZYm0YWCjO/0g==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/pbkdf2": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "aes-js": "3.0.0", + "scrypt-js": "3.0.1" + } + }, + "node_modules/@ethersproject/keccak256": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/keccak256/-/keccak256-5.7.0.tgz", + "integrity": "sha512-2UcPboeL/iW+pSg6vZ6ydF8tCnv3Iu/8tUmLLzWWGzxWKFFqOBQFLo6uLUv6BDrLgCDfN28RJ/wtByx+jZ4KBg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "js-sha3": "0.8.0" + } + }, + "node_modules/@ethersproject/logger": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/logger/-/logger-5.7.0.tgz", + "integrity": "sha512-0odtFdXu/XHtjQXJYA3u9G0G8btm0ND5Cu8M7i5vhEcE8/HmF4Lbdqanwyv4uQTr2tx6b7fQRmgLrsnpQlmnig==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ] + }, + "node_modules/@ethersproject/networks": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/networks/-/networks-5.7.1.tgz", + "integrity": "sha512-n/MufjFYv3yFcUyfhnXotyDlNdFb7onmkSy8aQERi2PjNcnWQ66xXxa3XlS8nCcA8aJKJjIIMNJTC7tu80GwpQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/pbkdf2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/pbkdf2/-/pbkdf2-5.7.0.tgz", + "integrity": "sha512-oR/dBRZR6GTyaofd86DehG72hY6NpAjhabkhxgr3X2FpJtJuodEl2auADWBZfhDHgVCbu3/H/Ocq2uC6dpNjjw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/sha2": "^5.7.0" + } + }, + "node_modules/@ethersproject/properties": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/properties/-/properties-5.7.0.tgz", + "integrity": "sha512-J87jy8suntrAkIZtecpxEPxY//szqr1mlBaYlQ0r4RCaiD2hjheqF9s1LVE8vVuJCXisjIP+JgtK/Do54ej4Sw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/providers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/@ethersproject/providers/-/providers-5.7.2.tgz", + "integrity": "sha512-g34EWZ1WWAVgr4aptGlVBF8mhl3VWjv+8hoAnzStu8Ah22VHBsuGzP17eb6xDVRzw895G4W7vvx60lFFur/1Rg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/base64": "^5.7.0", + "@ethersproject/basex": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "bech32": "1.1.4", + "ws": "7.4.6" + } + }, + "node_modules/@ethersproject/providers/node_modules/ws": { + "version": "7.4.6", + "resolved": "https://registry.npmjs.org/ws/-/ws-7.4.6.tgz", + "integrity": "sha512-YmhHDO4MzaDLB+M9ym/mDA5z0naX8j7SIlT8f8z+I0VtzsRbekxEutHSme7NPS2qE8StCYQNUnfWdXta/Yu85A==", + "engines": { + "node": ">=8.3.0" + }, + "peerDependencies": { + "bufferutil": "^4.0.1", + "utf-8-validate": "^5.0.2" + }, + "peerDependenciesMeta": { + "bufferutil": { + "optional": true + }, + "utf-8-validate": { + "optional": true + } + } + }, + "node_modules/@ethersproject/random": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/random/-/random-5.7.0.tgz", + "integrity": "sha512-19WjScqRA8IIeWclFme75VMXSBvi4e6InrUNuaR4s5pTF2qNhcGdCUwdxUVGtDDqC00sDLCO93jPQoDUH4HVmQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/rlp": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/rlp/-/rlp-5.7.0.tgz", + "integrity": "sha512-rBxzX2vK8mVF7b0Tol44t5Tb8gomOHkj5guL+HhzQ1yBh/ydjGnpw6at+X6Iw0Kp3OzzzkcKp8N9r0W4kYSs9w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/sha2": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/sha2/-/sha2-5.7.0.tgz", + "integrity": "sha512-gKlH42riwb3KYp0reLsFTokByAKoJdgFCwI+CCiX/k+Jm2mbNs6oOaCjYQSlI1+XBVejwH2KrmCbMAT/GnRDQw==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/signing-key": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/signing-key/-/signing-key-5.7.0.tgz", + "integrity": "sha512-MZdy2nL3wO0u7gkB4nA/pEf8lu1TlFswPNmy8AiYkfKTdO6eXBJyUdmHO/ehm/htHw9K/qF8ujnTyUAD+Ry54Q==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "bn.js": "^5.2.1", + "elliptic": "6.5.4", + "hash.js": "1.1.7" + } + }, + "node_modules/@ethersproject/solidity": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/solidity/-/solidity-5.7.0.tgz", + "integrity": "sha512-HmabMd2Dt/raavyaGukF4XxizWKhKQ24DoLtdNbBmNKUOPqwjsKQSdV9GQtj9CBEea9DlzETlVER1gYeXXBGaA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/sha2": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/strings": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/strings/-/strings-5.7.0.tgz", + "integrity": "sha512-/9nu+lj0YswRNSH0NXYqrh8775XNyEdUQAuf3f+SmOrnVewcJ5SBNAjF7lpgehKi4abvNNXyf+HX86czCdJ8Mg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/transactions": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/transactions/-/transactions-5.7.0.tgz", + "integrity": "sha512-kmcNicCp1lp8qanMTC3RIikGgoJ80ztTyvtsFvCYpSCfkjhD0jZ2LOrnbcuxuToLIUYYf+4XwD1rP+B/erDIhQ==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/rlp": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0" + } + }, + "node_modules/@ethersproject/units": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/units/-/units-5.7.0.tgz", + "integrity": "sha512-pD3xLMy3SJu9kG5xDGI7+xhTEmGXlEqXU4OfNapmfnxLVY4EMSSRp7j1k7eezutBPH7RBN/7QPnwR7hzNlEFeg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/constants": "^5.7.0", + "@ethersproject/logger": "^5.7.0" + } + }, + "node_modules/@ethersproject/wallet": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wallet/-/wallet-5.7.0.tgz", + "integrity": "sha512-MhmXlJXEJFBFVKrDLB4ZdDzxcBxQ3rLyCkhNqVu3CDYvR97E+8r01UgrI+TI99Le+aYm/in/0vp86guJuM7FCA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/abstract-signer": "^5.7.0", + "@ethersproject/address": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/hdnode": "^5.7.0", + "@ethersproject/json-wallets": "^5.7.0", + "@ethersproject/keccak256": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/random": "^5.7.0", + "@ethersproject/signing-key": "^5.7.0", + "@ethersproject/transactions": "^5.7.0", + "@ethersproject/wordlists": "^5.7.0" + } + }, + "node_modules/@ethersproject/web": { + "version": "5.7.1", + "resolved": "https://registry.npmjs.org/@ethersproject/web/-/web-5.7.1.tgz", + "integrity": "sha512-Gueu8lSvyjBWL4cYsWsjh6MtMwM0+H4HvqFPZfB6dV8ctbP9zFAO73VG1cMWae0FLPCtz0peKPpZY8/ugJJX2w==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/base64": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, + "node_modules/@ethersproject/wordlists": { + "version": "5.7.0", + "resolved": "https://registry.npmjs.org/@ethersproject/wordlists/-/wordlists-5.7.0.tgz", + "integrity": "sha512-S2TFNJNfHWVHNE6cNDjbVlZ6MgE17MIxMbMg2zv3wn+3XSJGosL1m9ZVv3GXCf/2ymSsQ+hRI5IzoMJTG6aoVA==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/logger": "^5.7.0", + "@ethersproject/properties": "^5.7.0", + "@ethersproject/strings": "^5.7.0" + } + }, "node_modules/@floating-ui/core": { "version": "1.5.3", "resolved": "https://registry.npmjs.org/@floating-ui/core/-/core-1.5.3.tgz", @@ -5554,6 +6248,11 @@ "node": ">=8.9" } }, + "node_modules/aes-js": { + "version": "3.0.0", + "resolved": "https://registry.npmjs.org/aes-js/-/aes-js-3.0.0.tgz", + "integrity": "sha512-H7wUZRn8WpTq9jocdxQ2c8x2sKo9ZVmzfRE13GiNJXfp7NcKYEdvl3vspKjXox6RIG2VtaRe4JFvxG4rqp2Zuw==" + }, "node_modules/agent-base": { "version": "6.0.2", "resolved": "https://registry.npmjs.org/agent-base/-/agent-base-6.0.2.tgz", @@ -6235,11 +6934,35 @@ "resolved": "https://registry.npmjs.org/balanced-match/-/balanced-match-1.0.2.tgz", "integrity": "sha512-3oSeUO0TMV67hN1AmbXsK4yaqU7tjiHlbxRDZOpH0KW9+CeX4bRAaX0Anxt0tx2MrpRpWwQaPwIlISEJhYU5Pw==" }, + "node_modules/base64-js": { + "version": "1.5.1", + "resolved": "https://registry.npmjs.org/base64-js/-/base64-js-1.5.1.tgz", + "integrity": "sha512-AKpaYlHn8t4SVbOHCy+b5+KKgvR4vrsD8vbvrbiQJps7fKDTkjkDry6ji0rUJjC0kzbNePLwzxq8iypo41qeWA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/batch": { "version": "0.6.1", "resolved": "https://registry.npmjs.org/batch/-/batch-0.6.1.tgz", "integrity": "sha512-x+VAiMRL6UPkx+kudNvxTl6hB2XNNCG2r+7wixVfIYwu/2HKRXimwQyaumLjMveWvT2Hkd/cAJw+QBMfJ/EKVw==" }, + "node_modules/bech32": { + "version": "1.1.4", + "resolved": "https://registry.npmjs.org/bech32/-/bech32-1.1.4.tgz", + "integrity": "sha512-s0IrSOzLlbvX7yp4WBfPITzpAU8sqQcpsmwXDiKwrG4r491vwCO/XpejasRNl0piBMe/DvP4Tz0mIS/X1DPJBQ==" + }, "node_modules/bfj": { "version": "7.1.0", "resolved": "https://registry.npmjs.org/bfj/-/bfj-7.1.0.tgz", @@ -6276,6 +6999,11 @@ "resolved": "https://registry.npmjs.org/bluebird/-/bluebird-3.7.2.tgz", "integrity": "sha512-XpNj6GDQzdfW+r2Wnn7xiSAd7TM3jzkxGXBGTtWKuSXv1xUV+azxAm8jdWZN06QTQk+2N2XB9jRDkvbmQmcRtg==" }, + "node_modules/bn.js": { + "version": "5.2.1", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-5.2.1.tgz", + "integrity": "sha512-eXRvHzWyYPBuB4NBy0cmYQjGitUrtqwbvlzP3G6VFnNRbsZQIxQ10PbKKHt8gZ/HW/D/747aDl+QkDqg3KQLMQ==" + }, "node_modules/body-parser": { "version": "1.20.1", "resolved": "https://registry.npmjs.org/body-parser/-/body-parser-1.20.1.tgz", @@ -6365,6 +7093,11 @@ "node": ">=8" } }, + "node_modules/brorand": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/brorand/-/brorand-1.1.0.tgz", + "integrity": "sha512-cKV8tMCEpQs4hK/ik71d6LrPOnpkpGBR0wzxqr68g2m/LB2GxVYQroAjMJZRVM1Y4BCjCKc3vAamxSzOY2RP+w==" + }, "node_modules/browser-process-hrtime": { "version": "1.0.0", "resolved": "https://registry.npmjs.org/browser-process-hrtime/-/browser-process-hrtime-1.0.0.tgz", @@ -6409,6 +7142,29 @@ "node-int64": "^0.4.0" } }, + "node_modules/buffer": { + "version": "6.0.3", + "resolved": "https://registry.npmjs.org/buffer/-/buffer-6.0.3.tgz", + "integrity": "sha512-FTiCpNxtwiZZHEZbcbTIcZjERVICn9yq/pDFkTl95/AxzD1naBctN7YO68riM/gLSDY7sdrMby8hofADYuuqOA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ], + "dependencies": { + "base64-js": "^1.3.1", + "ieee754": "^1.2.1" + } + }, "node_modules/buffer-from": { "version": "1.1.2", "resolved": "https://registry.npmjs.org/buffer-from/-/buffer-from-1.1.2.tgz", @@ -7732,6 +8488,25 @@ "resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.4.625.tgz", "integrity": "sha512-DENMhh3MFgaPDoXWrVIqSPInQoLImywfCwrSmVl3cf9QHzoZSiutHwGaB/Ql3VkqcQV30rzgdM+BjKqBAJxo5Q==" }, + "node_modules/elliptic": { + "version": "6.5.4", + "resolved": "https://registry.npmjs.org/elliptic/-/elliptic-6.5.4.tgz", + "integrity": "sha512-iLhC6ULemrljPZb+QutR5TQGB+pdW6KGD5RSegS+8sorOZT+rdQFbsQFJgvN3eRqNALqJer4oQ16YvJHlU8hzQ==", + "dependencies": { + "bn.js": "^4.11.9", + "brorand": "^1.1.0", + "hash.js": "^1.0.0", + "hmac-drbg": "^1.0.1", + "inherits": "^2.0.4", + "minimalistic-assert": "^1.0.1", + "minimalistic-crypto-utils": "^1.0.1" + } + }, + "node_modules/elliptic/node_modules/bn.js": { + "version": "4.12.0", + "resolved": "https://registry.npmjs.org/bn.js/-/bn.js-4.12.0.tgz", + "integrity": "sha512-c98Bf3tPniI+scsdk237ku1Dc3ujXQTSgyiPUDEOe7tRkhrqridvh8klBv0HCEso1OLOYcHuCv/cS6DNxKH+ZA==" + }, "node_modules/emittery": { "version": "0.8.1", "resolved": "https://registry.npmjs.org/emittery/-/emittery-0.8.1.tgz", @@ -8656,6 +9431,53 @@ "node": ">= 0.6" } }, + "node_modules/ethers": { + "version": "5.7.2", + "resolved": "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz", + "integrity": "sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg==", + "funding": [ + { + "type": "individual", + "url": "https://gitcoin.co/grants/13/ethersjs-complete-simple-and-tiny-2" + }, + { + "type": "individual", + "url": "https://www.buymeacoffee.com/ricmoo" + } + ], + "dependencies": { + "@ethersproject/abi": "5.7.0", + "@ethersproject/abstract-provider": "5.7.0", + "@ethersproject/abstract-signer": "5.7.0", + "@ethersproject/address": "5.7.0", + "@ethersproject/base64": "5.7.0", + "@ethersproject/basex": "5.7.0", + "@ethersproject/bignumber": "5.7.0", + "@ethersproject/bytes": "5.7.0", + "@ethersproject/constants": "5.7.0", + "@ethersproject/contracts": "5.7.0", + "@ethersproject/hash": "5.7.0", + "@ethersproject/hdnode": "5.7.0", + "@ethersproject/json-wallets": "5.7.0", + "@ethersproject/keccak256": "5.7.0", + "@ethersproject/logger": "5.7.0", + "@ethersproject/networks": "5.7.1", + "@ethersproject/pbkdf2": "5.7.0", + "@ethersproject/properties": "5.7.0", + "@ethersproject/providers": "5.7.2", + "@ethersproject/random": "5.7.0", + "@ethersproject/rlp": "5.7.0", + "@ethersproject/sha2": "5.7.0", + "@ethersproject/signing-key": "5.7.0", + "@ethersproject/solidity": "5.7.0", + "@ethersproject/strings": "5.7.0", + "@ethersproject/transactions": "5.7.0", + "@ethersproject/units": "5.7.0", + "@ethersproject/wallet": "5.7.0", + "@ethersproject/web": "5.7.1", + "@ethersproject/wordlists": "5.7.0" + } + }, "node_modules/eventemitter3": { "version": "4.0.7", "resolved": "https://registry.npmjs.org/eventemitter3/-/eventemitter3-4.0.7.tgz", @@ -9610,6 +10432,15 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/hash.js": { + "version": "1.1.7", + "resolved": "https://registry.npmjs.org/hash.js/-/hash.js-1.1.7.tgz", + "integrity": "sha512-taOaskGt4z4SOANNseOviYDvjEJinIkRgmp7LbKP2YTTmVxWBl87s/uzK9r+44BclBSp2X7K1hqeNfz9JbBeXA==", + "dependencies": { + "inherits": "^2.0.3", + "minimalistic-assert": "^1.0.1" + } + }, "node_modules/hasown": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/hasown/-/hasown-2.0.0.tgz", @@ -9629,6 +10460,16 @@ "he": "bin/he" } }, + "node_modules/hmac-drbg": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/hmac-drbg/-/hmac-drbg-1.0.1.tgz", + "integrity": "sha512-Tti3gMqLdZfhOQY1Mzf/AanLiqh1WTiJgEj26ZuYQ9fbkLomzGchCws4FyrSd4VkpBfiNhaE1On+lOz894jvXg==", + "dependencies": { + "hash.js": "^1.0.3", + "minimalistic-assert": "^1.0.0", + "minimalistic-crypto-utils": "^1.0.1" + } + }, "node_modules/hoist-non-react-statics": { "version": "3.3.2", "resolved": "https://registry.npmjs.org/hoist-non-react-statics/-/hoist-non-react-statics-3.3.2.tgz", @@ -9925,6 +10766,25 @@ "node": ">=4" } }, + "node_modules/ieee754": { + "version": "1.2.1", + "resolved": "https://registry.npmjs.org/ieee754/-/ieee754-1.2.1.tgz", + "integrity": "sha512-dcyqhDvX1C46lXZcVqCpK+FtMRQVdIMN6/Df5js2zouUsqG7I6sFxitIC+7KYK29KdXOLHdu9zL4sFnoVQnqaA==", + "funding": [ + { + "type": "github", + "url": "https://github.com/sponsors/feross" + }, + { + "type": "patreon", + "url": "https://www.patreon.com/feross" + }, + { + "type": "consulting", + "url": "https://feross.org/support" + } + ] + }, "node_modules/ignore": { "version": "5.3.0", "resolved": "https://registry.npmjs.org/ignore/-/ignore-5.3.0.tgz", @@ -12576,6 +13436,11 @@ "jiti": "bin/jiti.js" } }, + "node_modules/js-sha3": { + "version": "0.8.0", + "resolved": "https://registry.npmjs.org/js-sha3/-/js-sha3-0.8.0.tgz", + "integrity": "sha512-gF1cRrHhIzNfToc802P800N8PpXS+evLLXfsVpowqmAFR9uwbi89WvXg2QspOmXL8QL86J4T1EpFu+yUkwJY3Q==" + }, "node_modules/js-tokens": { "version": "4.0.0", "resolved": "https://registry.npmjs.org/js-tokens/-/js-tokens-4.0.0.tgz", @@ -12654,6 +13519,12 @@ "resolved": "https://registry.npmjs.org/json-buffer/-/json-buffer-3.0.1.tgz", "integrity": "sha512-4bV5BfR2mqfQTJm+V5tPPdf+ZpuhiIvTuAB5g8kcrXOZpTT/QwwVRWBywX1ozr6lEuPdbHxwaJlm9G6mI2sfSQ==" }, + "node_modules/json-loader": { + "version": "0.5.7", + "resolved": "https://registry.npmjs.org/json-loader/-/json-loader-0.5.7.tgz", + "integrity": "sha512-QLPs8Dj7lnf3e3QYS1zkCo+4ZwqOiF9d/nZnYozTISxXWCfNs9yuky5rJw4/W34s7POaNlbZmQGaB5NiXCbP4w==", + "dev": true + }, "node_modules/json-parse-even-better-errors": { "version": "2.3.1", "resolved": "https://registry.npmjs.org/json-parse-even-better-errors/-/json-parse-even-better-errors-2.3.1.tgz", @@ -13148,6 +14019,11 @@ "resolved": "https://registry.npmjs.org/minimalistic-assert/-/minimalistic-assert-1.0.1.tgz", "integrity": "sha512-UtJcAD4yEaGtjPezWuO9wC4nwUnVH/8/Im3yEHQP4b67cXlD/Qr9hdITCU1xDbSEXg2XKNaP8jsReV7vQd00/A==" }, + "node_modules/minimalistic-crypto-utils": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/minimalistic-crypto-utils/-/minimalistic-crypto-utils-1.0.1.tgz", + "integrity": "sha512-JIYlbt6g8i5jKfJ3xz7rF0LXmv2TkDxBLUkiBeZ7bAx4GnnNMr8xFpGnOxn6GhTEHx3SjRrZEoU+j04prX1ktg==" + }, "node_modules/minimatch": { "version": "3.1.2", "resolved": "https://registry.npmjs.org/minimatch/-/minimatch-3.1.2.tgz", @@ -16204,6 +17080,11 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/scrypt-js": { + "version": "3.0.1", + "resolved": "https://registry.npmjs.org/scrypt-js/-/scrypt-js-3.0.1.tgz", + "integrity": "sha512-cdwTTnqPu0Hyvf5in5asVdZocVDTNRmR7XEcJuIzMjJeSHybHl7vpB66AzwTaIg6CLSbtjcxc8fqcySfnTkccA==" + }, "node_modules/select-hose": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/select-hose/-/select-hose-2.0.0.tgz", diff --git a/admin_frontend/package.json b/admin_frontend/package.json index e4dadf5..c234130 100644 --- a/admin_frontend/package.json +++ b/admin_frontend/package.json @@ -1,6 +1,6 @@ { "name": "admin_frontend", - "version": "1.0.0", + "version": "1.0.2", "private": true, "dependencies": { "@emotion/react": "11.11.3", @@ -11,6 +11,8 @@ "@testing-library/jest-dom": "5.17.0", "@testing-library/react": "13.4.0", "@testing-library/user-event": "13.5.0", + "buffer": "6.0.3", + "ethers": "5.7.2", "react": "18.2.0", "react-dom": "18.2.0", "react-hot-toast": "2.4.1", @@ -45,6 +47,7 @@ }, "devDependencies": { "autoprefixer": "10.4.16", + "json-loader": "0.5.7", "postcss": "8.4.33", "tailwindcss": "3.4.1" } diff --git a/admin_frontend/src/App.js b/admin_frontend/src/App.js index 0f9752f..8d4c97e 100644 --- a/admin_frontend/src/App.js +++ b/admin_frontend/src/App.js @@ -10,6 +10,8 @@ import ApiKeysPage from "./components/ApiKeys"; // Css import { ThemeProvider, createTheme } from '@mui/material/styles'; import CssBaseline from '@mui/material/CssBaseline'; +import { AuthContextProvider } from "./context/AuthContext"; +import ProtectedRoute from "./components/ProtectedRoute"; const darkTheme = createTheme({ palette: { @@ -23,18 +25,23 @@ function App() { <> - -
- - } /> - } /> - } /> - -
-
+ +
+ + + } /> + + + } /> + } /> + + +
+ ); } -export default App; \ No newline at end of file +export default App; diff --git a/admin_frontend/src/components/ApiKeys.jsx b/admin_frontend/src/components/ApiKeys.jsx index e5174d2..2bfd197 100644 --- a/admin_frontend/src/components/ApiKeys.jsx +++ b/admin_frontend/src/components/ApiKeys.jsx @@ -1,8 +1,10 @@ import { useEffect, useState } from "react"; -import AddCircleIcon from "@mui/icons-material/AddCircle"; -import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import { Buffer } from "buffer"; import toast from "react-hot-toast"; + // components +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; import { TextField } from "@mui/material"; import Table from "@mui/material/Table"; import TableBody from "@mui/material/TableBody"; @@ -15,311 +17,349 @@ import InputAdornment from "@mui/material/InputAdornment"; import Visibility from "@mui/icons-material/Visibility"; import VisibilityOff from "@mui/icons-material/VisibilityOff"; import IconButton from "@mui/material/IconButton"; -import OutlinedInput from "@mui/material/OutlinedInput"; -import FormControl from "@mui/material/FormControl"; -import InputLabel from "@mui/material/InputLabel"; -import Checkbox from "@mui/material/Checkbox"; +import Button from "@mui/material/Button"; import Header from "./Header"; -// constants -import { ENDPOINTS } from "../constants/common"; +//Modals +import AddSupportedNetworksModal from "../modals/AddSupportedNetworksModal"; +import ViewSupportedNetworksModal from "../modals/ViewSupportedNetworksModal"; +import AddERC20PaymasterModal from "../modals/AddERC20Paymaster"; +import ViewERC20PaymasterModal from "../modals/ViewERC20Paymaster"; + +//constants +import defaultSupportedNetworks from "../constants/defaultNetworks"; +import { ENDPOINTS } from "../constants/constants"; const ApiKeysPage = () => { - const [keys, setKeys] = useState([]); - const [loading, setLoading] = useState(false); - const [apiKey, setApiKey] = useState(""); - const [privateKey, setPrivateKey] = useState(""); - const [supportedNetworks, setSupportedNetworks] = useState(""); - const [customErc20Paymaster, setCustomErc20Paymaster] = useState(""); - const [txnMode, setTxnMode] = useState(0); - const [noOfTxn, setNoOfTxn] = useState(10); - const [showPassword, setShowPassword] = useState(false); + const [keys, setKeys] = useState([]); + const [loading, setLoading] = useState(false); + const [apiKey, setApiKey] = useState(""); + const [privateKey, setPrivateKey] = useState(""); + const [supportedNetworks, setSupportedNetworks] = useState( + defaultSupportedNetworks + ); + const [customErc20Paymaster, setCustomErc20Paymaster] = useState({}); + const [showPassword, setShowPassword] = useState(false); + const [open, setOpen] = useState(false); + const [customErc20Open, setCustomErc20Open] = useState(false); + const [viewModalOpen, setViewModalOpen] = useState(false); + const [viewErc20Open, setViewErc20Open] = useState(false); + const [edit, setEdit] = useState(false); + const [ERC20Tokens, setERC20Tokens] = useState([]); + const handleOpen = () => { + setSupportedNetworks(defaultSupportedNetworks); + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + }; + + const handleCustomErc20Open = (edit, data) => { + setEdit(edit); + setCustomErc20Paymaster(data); + setCustomErc20Open(true); + }; + const handleCustomErc20Close = () => { + setCustomErc20Open(false); + }; + const handleViewERC20Open = (data) => { + setERC20Tokens(data); + setViewErc20Open(true); + }; + + const handleViewERC20Close = () => { + setViewErc20Open(false); + }; + const handleViewOpen = (networks) => { + setSupportedNetworks(networks); + setViewModalOpen(true); + }; - const handleClickShowPassword = () => setShowPassword(!showPassword); + const handleViewClose = () => { + setViewModalOpen(false); + }; - const handleMouseDownPassword = (event) => { - event.preventDefault(); - }; + const handleClickShowPassword = () => setShowPassword(!showPassword); - const handleChange = (event) => { - setTxnMode(event.target.checked ? 1 : 0); - }; + const handleMouseDownPassword = (event) => { + event.preventDefault(); + }; - const fetchData = async () => { - try { - setLoading(true); - const data = await ( - await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['getKeys']}`, { - method: "GET", - }) - ).json(); - setKeys(data); - setLoading(false); - } catch (err) { - if (err.message.includes("Falied to fetch")) - toast.error("Failed to connect. Please make sure that the backend is running") - else - toast.error(err.message) - } - }; + const fetchData = async () => { + try { + setLoading(true); + const data = await fetch( + `${process.env.REACT_APP_SERVER_URL}${ENDPOINTS["getKeys"]}`, + { + method: "GET", + } + ); + const dataJson = await data.json(); + dataJson.filter((element) => { + if (element.SUPPORTED_NETWORKS) { + const buffer = Buffer.from(element.SUPPORTED_NETWORKS, "base64"); + const parsedSupportedNetowrks = JSON.parse(buffer.toString()); + element.SUPPORTED_NETWORKS = parsedSupportedNetowrks; + } + if (element.ERC20_PAYMASTERS) { + const buffer = Buffer.from(element.ERC20_PAYMASTERS, "base64"); + const parsedErc20Paymasters = JSON.parse(buffer.toString()); + element.ERC20_PAYMASTERS = parsedErc20Paymasters; + } + return element; + }); + setKeys(dataJson); + setLoading(false); + } catch (err) { + if (err?.message?.includes("Failed to fetch")) { + toast.error("There was a problem communicating with the server. Please try again, or contact Arka support team."); + } else toast.error(err?.message); + } + }; - useEffect(() => { - fetchData(); - }, []); + useEffect(() => { + fetchData(); + }, []); - const handleSubmit = async () => { - if (apiKey === "" || privateKey === "") { - toast.error("Please input both API_KEY & PRIVATE_KEY field"); - return; - } - if ( - !/^(?=.*[a-z])(?=.*[A-Z])(?=.*\d)(?=.*[@$!%*-_&])[A-Za-z\d@$!%*-_&]{8,}$/.test( - apiKey - ) - ) { - toast.error( - "Invalid Validation: API_KEY format. Please see the docs for more info" - ); - return; - } - try { - setLoading(true); - const requestData = { - API_KEY: apiKey, - PRIVATE_KEY: privateKey, - SUPPORTED_NETWORKS: supportedNetworks ?? "", - ERC20_PAYMASTERS: customErc20Paymaster ?? "", - TRANSACTION_LIMIT: txnMode, - NO_OF_TRANSACTIONS_IN_A_MONTH: noOfTxn, - INDEXER_ENDPOINT: - process.env.REACT_APP_INDEXER_ENDPOINT ?? "http://localhost:3003", - }; - const data = await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['saveKey']}`, { - method: "POST", - body: JSON.stringify(requestData), - }); - const dataJson = await data.json(); - if (!dataJson.error) { - toast.success("Saved Successfully"); - setApiKey(""); - setPrivateKey(""); - fetchData(); - } else { - setLoading(false); - toast.error("Could not save"); - } - } catch (err) { - if (err.message.includes("Falied to fetch")) - toast.error("Failed to connect. Please make sure that the backend is running") - else - toast.error(err.message) - setLoading(false); - } - }; + const handleSubmit = async () => { + if (apiKey === "" || privateKey === "") { + toast.error("Please input both API_KEY & PRIVATE_KEY field"); + return; + } + try { + setLoading(true); + let base64Erc20 = ""; + if (customErc20Paymaster != {}) + base64Erc20 = Buffer.from( + JSON.stringify(customErc20Paymaster) + ).toString("base64"); + const requestData = { + API_KEY: apiKey, + PRIVATE_KEY: privateKey, + SUPPORTED_NETWORKS: + Buffer.from(JSON.stringify(supportedNetworks)).toString("base64") ?? + "", + ERC20_PAYMASTERS: base64Erc20 ?? "", + }; + const data = await fetch( + `${process.env.REACT_APP_SERVER_URL}${ENDPOINTS["saveKey"]}`, + { + method: "POST", + body: JSON.stringify(requestData), + } + ); + const dataJson = data.json(); + if (!dataJson.error) { + toast.success("Saved Successfully"); + setApiKey(""); + setPrivateKey(""); + fetchData(); + } else { + setLoading(false); + toast.error(`${dataJson.message} Please try again or contant Arka support team`); + } + } catch (err) { + if (err?.message?.includes("Failed to fetch")) { + toast.error("There was a problem communicating with the server. Please try again, or contact Arka support team."); + } else toast.error(err?.message); + setLoading(false); + } + }; - const handleDelete = async (key) => { - try { - setLoading(true); - const data = await ( - await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['deleteKey']}`, { - method: "POST", - body: JSON.stringify({ API_KEY: key }), - }) - ).json(); - if (!data.error) { - toast.success("Deleted Successfully"); - fetchData(); - } else { - setLoading(false); - toast.error("Could not save"); - } - } catch (err) { - if (err.message.includes("Falied to fetch")) - toast.error("Failed to connect. Please make sure that the backend is running") - else - toast.error(err.message) - setLoading(false); - } - }; + const handleDelete = async (key) => { + try { + setLoading(true); + const data = await fetch( + `${process.env.REACT_APP_SERVER_URL}${ENDPOINTS["deleteKey"]}`, + { + method: "POST", + body: JSON.stringify({ API_KEY: key }), + } + ); + const dataJson = data.json(); + if (!dataJson.error) { + toast.success("Deleted Successfully"); + fetchData(); + } else { + setLoading(false); + toast.error(`${dataJson.message} Please try again or contant Arka support team`); + } + } catch (err) { + if (err?.message?.includes("Failed to fetch")) { + toast.error("There was a problem communicating with the server. Please try again, or contact Arka support team."); + } else toast.error(err?.message); + setLoading(false); + } + }; - return ( - <> -
- - - - - Wallet Address - Api Key - Private Key - Supported Networks - Custom ERC20 Paymasters - Transaction Limit Mode - No of Transactions Allowed - Actions Available - - - - - - - - - setApiKey(e.target.value)} - value={apiKey} - required - multiline - fullWidth - /> - - - - - PRIVATE_KEY - - - - {showPassword ? : } - - - } - label="PRIVATE_KEY" - multiline - value={privateKey} - onChange={(e) => setPrivateKey(e.target.value)} - /> - - - - setSupportedNetworks(e.target.value)} - value={supportedNetworks} - required - multiline - fullWidth - /> - - - setCustomErc20Paymaster(e.target.value)} - value={customErc20Paymaster} - required - multiline - fullWidth - /> - - - - - - setNoOfTxn(e.target.value)} - value={noOfTxn} - required - fullWidth - /> - - - } - variant="contained" - onClick={() => { - handleSubmit(); - }} - > - Add Row - - - - {keys.map((row) => ( - - {row.WALLET_ADDRESS} - {row.API_KEY} - -
-
{showPassword ? row.PRIVATE_KEY : "*****"}
-
- - - {showPassword ? : } - - -
-
-
- {row.SUPPORTED_NETWORKS} - {row.ERC20_PAYMASTERS} - - {row.TRANSACTION_LIMIT === 0 ? "OFF" : "ON"} - - {row.NO_OF_TRANSACTIONS_IN_A_MONTH} - - } - variant="contained" - onClick={() => { - handleDelete(row.API_KEY); - }} - > - Delete Row - - -
- ))} -
-
-
- - ); + return ( + <> +
+ + + + + Wallet Address + Api Key + Private Key + Supported Networks + Custom ERC20 Paymasters + Actions Available + + + + + + + + + setApiKey(e.target.value)} + value={apiKey} + required + fullWidth + /> + + + setPrivateKey(e.target.value)} + required + type={showPassword ? "text" : "password"} + InputProps={{ + endAdornment: ( + + + {showPassword ? : } + + + ), + }} + /> + + + + + + + + + } + variant="contained" + onClick={() => { + handleSubmit(); + }} + > + Add Row + + + + {keys.map((row, index) => ( + + {row.WALLET_ADDRESS} + {row.API_KEY} + +
+
{showPassword ? row.PRIVATE_KEY : "*****"}
+
+ + + {showPassword ? : } + + +
+
+
+ + + + + + + + } + variant="contained" + onClick={() => { + handleDelete(row.API_KEY); + }} + > + Delete Row + + +
+ ))} +
+
+
+ + + + + + ); }; export default ApiKeysPage; diff --git a/admin_frontend/src/components/Dashboard.jsx b/admin_frontend/src/components/Dashboard.jsx index b513ff5..8a8d092 100644 --- a/admin_frontend/src/components/Dashboard.jsx +++ b/admin_frontend/src/components/Dashboard.jsx @@ -1,303 +1,439 @@ import { useEffect, useState } from "react"; +import { Buffer } from "buffer"; + +// components import { TextField } from "@mui/material"; import LoadingButton from "@mui/lab/LoadingButton"; +import { styled } from "styled-components"; import SaveIcon from "@mui/icons-material/Save"; import toast from "react-hot-toast"; import Header from "./Header"; +import Button from "@mui/material/Button"; + +// context +import { UserAuth } from "../context/AuthContext"; + +// Modals +import CoingeckoIdModal from "../modals/CoingeckoId"; +import DeployedPaymastersModal from "../modals/DeployedPaymasters"; + +// contants +import { ENDPOINTS } from "../constants/constants"; + +const SettingsText = styled.span` + margin: '3px 0 4px 8px', + font-size: '24px', + padding-right: 5rem, + text-align: 'center', + color: '#cfcfcf' + `; + +const InfoTextStyle = { + fontSize: "small", + color: "grey", +}; const Dashboard = () => { - const defaultConfig = { - COINGECKO_API_URL: - "", - COINGECKO_IDS: "", - CRON_TIME: "", - CUSTOM_CHAINLINK_DEPLOYED: - "", - DEPLOYED_ERC20_PAYMASTERS: - "", - PYTH_MAINNET_CHAIN_IDS: "", - PYTH_MAINNET_URL: "", - PYTH_TESTNET_CHAIN_IDS: "", - PYTH_TESTNET_URL: - "", - id: 1, - }; - const [config, setConfig] = useState(defaultConfig); - const [edittedConfig, setEdittedConfig] = useState(defaultConfig); - const [disableSave, setDisableSave] = useState(true); - const [loading, setLoading] = useState(false); + const defaultConfig = { + COINGECKO_API_URL: "", + COINGECKO_IDS: "", + CRON_TIME: "", + CUSTOM_CHAINLINK_DEPLOYED: "", + DEPLOYED_ERC20_PAYMASTERS: "", + PYTH_MAINNET_CHAIN_IDS: "", + PYTH_MAINNET_URL: "", + PYTH_TESTNET_CHAIN_IDS: "", + PYTH_TESTNET_URL: "", + id: 1, + }; + const [config, setConfig] = useState(defaultConfig); + const [edittedConfig, setEdittedConfig] = useState(defaultConfig); + const [disableSave, setDisableSave] = useState(true); + const [loading, setLoading] = useState(false); + const { user } = UserAuth(); + const [signedIn, setSignedIn] = useState(false); + const [open, setOpen] = useState(false); + const [coingeckoIds, setCoingeckoIds] = useState({}); + const [dpOpen, setDpOpen] = useState(false); + const [deployedPaymasters, setDeployedPaymasters] = useState({}); + const [customChainlink, setCustomChainlink] = useState({}); + const [customChainlinkOpen, setCustomChainlinkOpen] = useState(false); + + const handleOpen = () => { + setOpen(true); + }; + const handleClose = () => { + setOpen(false); + setDpOpen(false); + setCustomChainlinkOpen(false); + setDisableSave(false); + }; + + const handleDpOpen = () => { + setDpOpen(true); + }; + + const fetchData = async () => { + if (signedIn) { + try { + setLoading(true); + const data = await fetch( + `${process.env.REACT_APP_SERVER_URL}${ENDPOINTS["getConfig"]}`, + { + method: "GET", + } + ); + const dataJson = data.json(); + setConfig(dataJson); + setEdittedConfig(dataJson); + let buffer; + if (data.COINGECKO_IDS && data.COINGECKO_IDS !== "") { + buffer = Buffer.from(data.COINGECKO_IDS, "base64"); + const coingeckoIds = JSON.parse(buffer.toString()); + setCoingeckoIds(coingeckoIds); + } + if ( + data.DEPLOYED_ERC20_PAYMASTERS && + data.DEPLOYED_ERC20_PAYMASTERS !== "" + ) { + buffer = Buffer.from(data.DEPLOYED_ERC20_PAYMASTERS, "base64"); + setDeployedPaymasters(JSON.parse(buffer.toString())); + } + if ( + data.CUSTOM_CHAINLINK_DEPLOYED && + data.CUSTOM_CHAINLINK_DEPLOYED !== "" + ) { + buffer = Buffer.from(data.CUSTOM_CHAINLINK_DEPLOYED, "base64"); + setCustomChainlink(JSON.parse(buffer.toString())); + } + setDisableSave(true); + setLoading(false); + } catch (err) { + if (err?.message?.includes("Failed to fetch")) { + toast.error("Failed to access the server url"); + } else toast.error(err?.message); + } + } + }; - const fetchData = async () => { - try { - setLoading(true); - const data = await ( - await fetch("http://localhost:5050/getConfig", { - method: "GET", - }) - ).json(); - console.log("data: ", data); - setConfig(data); - setEdittedConfig(data); - setDisableSave(true); - setLoading(false); - } catch (err) { - toast.error( - "Check Backend Service for more info" - ); - } - }; + useEffect(() => { + setLoading(false); + if (user?.address) { + setSignedIn(true); + fetchData(); + } + setLoading(false); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [user]); - useEffect(() => { - fetchData(); - }, []); + useEffect(() => { + fetchData(); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [signedIn]); - const handleSubmit = async (edittedConfig) => { - try { - setLoading(true); - console.log("edittedConfig: ", edittedConfig); - const data = await ( - await fetch("http://localhost:5050/saveConfig", { - method: "POST", - body: JSON.stringify(edittedConfig), - }) - ).json(); - if (!data.error) { - toast.success("Saved Successfully"); - fetchData(); - } else { - toast.error("Could not save"); - } - setLoading(false); - } catch (err) { - toast.error( - "Check Backend Service for more info" - ); - } - }; + const handleSubmit = async (edittedConfig) => { + if (signedIn) { + try { + setLoading(true); + edittedConfig.COINGECKO_IDS = Buffer.from( + JSON.stringify(coingeckoIds) + ).toString("base64"); + edittedConfig.DEPLOYED_ERC20_PAYMASTERS = Buffer.from( + JSON.stringify(deployedPaymasters) + ).toString("base64"); + edittedConfig.CUSTOM_CHAINLINK_DEPLOYED = Buffer.from( + JSON.stringify(customChainlink) + ).toString("base64"); + const data = await fetch( + `${process.env.REACT_APP_SERVER_URL}${ENDPOINTS["saveConfig"]}`, + { + method: "POST", + body: JSON.stringify(edittedConfig), + } + ); + const dataJson = data.json(); + if (!dataJson.error) { + toast.success("Saved Successfully"); + fetchData(); + } else { + toast.error(`${dataJson.message} Please try again or contant Arka support team`); + } + setLoading(false); + } catch (err) { + if (err?.message?.includes("Failed to fetch")) { + toast.error("Failed to access the server url"); + } else toast.error(err?.message); + } + } else { + toast.error("Please login to edit and save changes"); + } + }; - return ( - <> -
-
- { - setEdittedConfig({ - ...edittedConfig, - COINGECKO_IDS: e.target.value, - }); - if (disableSave && e.target.value !== config.COINGECKO_IDS) - setDisableSave(false); - else if (!disableSave && e.target.value === config.COINGECKO_IDS) - setDisableSave(true); - }} - value={edittedConfig.COINGECKO_IDS} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - COINGECKO_API_URL: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if ( - !disableSave && - e.target.value === config.COINGECKO_API_URL - ) - setDisableSave(true); - }} - value={edittedConfig.COINGECKO_API_URL} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - CRON_TIME: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if (!disableSave && e.target.value === config.CRON_TIME) - setDisableSave(true); - }} - value={edittedConfig.CRON_TIME} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - CUSTOM_CHAINLINK_DEPLOYED: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if ( - !disableSave && - e.target.value === config.CUSTOM_CHAINLINK_DEPLOYED - ) - setDisableSave(true); - }} - value={edittedConfig.CUSTOM_CHAINLINK_DEPLOYED} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - DEPLOYED_ERC20_PAYMASTERS: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if ( - !disableSave && - e.target.value === config.DEPLOYED_ERC20_PAYMASTERS - ) - setDisableSave(true); - }} - value={edittedConfig.DEPLOYED_ERC20_PAYMASTERS} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - PYTH_MAINNET_CHAIN_IDS: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if ( - !disableSave && - e.target.value === config.PYTH_MAINNET_CHAIN_IDS - ) - setDisableSave(true); - }} - value={edittedConfig.PYTH_MAINNET_CHAIN_IDS} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - PYTH_MAINNET_URL: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if (!disableSave && e.target.value === config.PYTH_MAINNET_URL) - setDisableSave(true); - }} - value={edittedConfig.PYTH_MAINNET_URL} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - PYTH_TESTNET_CHAIN_IDS: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if ( - !disableSave && - e.target.value === config.PYTH_TESTNET_CHAIN_IDS - ) - setDisableSave(true); - }} - value={edittedConfig.PYTH_TESTNET_CHAIN_IDS} - required - fullWidth - multiline - /> -
-
- { - setEdittedConfig({ - ...edittedConfig, - PYTH_TESTNET_URL: e.target.value, - }); - if (disableSave) setDisableSave(false); - else if (!disableSave && e.target.value === config.PYTH_TESTNET_URL) - setDisableSave(true); - }} - value={edittedConfig.PYTH_TESTNET_URL} - required - fullWidth - multiline - /> -
- } - variant="contained" - onClick={() => { - handleSubmit(edittedConfig); - }} - fullWidth - > - Save - - - ); + return ( + <> +
+
+ COINGECKO_IDS + +
+
+ + COINGECKO_IDS are for Deployed paymasters with custom chainlink + oracles. Can be left blank if you dont use custom erc20 paymasters + deployed + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + COINGECKO_API_URL: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if ( + !disableSave && + e.target.value === config.COINGECKO_API_URL + ) + setDisableSave(true); + }} + value={edittedConfig.COINGECKO_API_URL} + required + fullWidth + multiline + /> +
+
+ + COINGECKO_API_URL is the url for requesting price feeds for CRON-JOB + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + CRON_TIME: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if (!disableSave && e.target.value === config.CRON_TIME) + setDisableSave(true); + }} + value={edittedConfig.CRON_TIME} + required + fullWidth + /> +
+
+ + CRON TIME requires exact cron time format as a string + +
+
+ Custom Chainlink for CRONJOB + +
+
+ + Custom deployments of erc20 paymaster supported by chainlink oracles + tokens to update the oracle price manually in the above specified cron + time + +
+
+ Deployed Paymasters for CRONJOB + +
+
+ + Custom deployed erc20 paymasters to update the price feed on the + paymaster + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + PYTH_MAINNET_CHAIN_IDS: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if ( + !disableSave && + e.target.value === config.PYTH_MAINNET_CHAIN_IDS + ) + setDisableSave(true); + }} + value={edittedConfig.PYTH_MAINNET_CHAIN_IDS} + required + fullWidth + multiline + /> +
+
+ + PYTH mainnet chainIds. Can be left blank if you do not tend to use + PYTH oracle + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + PYTH_MAINNET_URL: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if (!disableSave && e.target.value === config.PYTH_MAINNET_URL) + setDisableSave(true); + }} + value={edittedConfig.PYTH_MAINNET_URL} + required + fullWidth + multiline + /> +
+
+ + PYTH mainnet price feed URL. Can be left blank if you do not tend to + use PYTH oracle + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + PYTH_TESTNET_CHAIN_IDS: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if ( + !disableSave && + e.target.value === config.PYTH_TESTNET_CHAIN_IDS + ) + setDisableSave(true); + }} + value={edittedConfig.PYTH_TESTNET_CHAIN_IDS} + required + fullWidth + multiline + /> +
+
+ + PYTH testnet chainIds. Can be left blank if you do not tend to use + PYTH oracle + +
+
+ { + setEdittedConfig({ + ...edittedConfig, + PYTH_TESTNET_URL: e.target.value, + }); + if (disableSave) setDisableSave(false); + else if (!disableSave && e.target.value === config.PYTH_TESTNET_URL) + setDisableSave(true); + }} + value={edittedConfig.PYTH_TESTNET_URL} + required + fullWidth + multiline + /> +
+
+ + PYTH testnet price feed URL. Can be left blank if you do not tend to + use PYTH oracle + +
+ } + variant="contained" + onClick={() => { + handleSubmit(edittedConfig); + }} + fullWidth + > + Save + + + + + + ); }; export default Dashboard; diff --git a/admin_frontend/src/components/Header.jsx b/admin_frontend/src/components/Header.jsx index 5ce8be8..94c9ccf 100644 --- a/admin_frontend/src/components/Header.jsx +++ b/admin_frontend/src/components/Header.jsx @@ -1,23 +1,69 @@ -import React from "react"; +import { useEffect, useState } from "react"; import { styled } from "styled-components"; +import Link from "@mui/material/Link"; +import { useNavigate } from "react-router-dom"; +// context +import { UserAuth } from "../context/AuthContext"; // assets -import EtherspotLogo from '../assets/internal-48-etherspot@2x.png'; +import EtherspotLogo from "../assets/internal-48-etherspot@2x.png"; const LogoText = styled.span` - margin: '3px 0 4px 8px', - font-size: '24px', - text-align: 'center', + margin: '3px 0 4px 8px', + font-size: '24px', + text-align: 'center', color: '#cfcfcf' - ` + `; const Header = ({ text }) => { + const { user, signIn } = UserAuth(); + const navigate = useNavigate(); + const [signedIn, setSignedIn] = useState(false); + + useEffect(() => { + if (user?.address) setSignedIn(true); + }, [user]); + return ( -
+
- {'EtherspotLogo'} + {"EtherspotLogo"} {text}
+
+ {signedIn ? ( + <> + navigate("/")} + > + Home + + navigate("/apikeys")} + > + Api Keys + + + ) : ( + <> + )} + {signedIn ? ( + {user?.address} + ) : ( + + )} +
); }; diff --git a/admin_frontend/src/components/NotFound.jsx b/admin_frontend/src/components/NotFound.jsx index e591aca..6d2ce0f 100644 --- a/admin_frontend/src/components/NotFound.jsx +++ b/admin_frontend/src/components/NotFound.jsx @@ -1,5 +1,4 @@ const NotFound = () => { - return (

Page Not Found

diff --git a/admin_frontend/src/components/ProtectedRoute.js b/admin_frontend/src/components/ProtectedRoute.js new file mode 100644 index 0000000..093ae04 --- /dev/null +++ b/admin_frontend/src/components/ProtectedRoute.js @@ -0,0 +1,24 @@ +import React, { useEffect } from 'react'; +import { useNavigate } from 'react-router-dom'; +import toast from "react-hot-toast"; +import { UserAuth } from '../context/AuthContext'; + +const ProtectedRoute = ({ children }) => { + const { user } = UserAuth(); + const navigate = useNavigate() + + useEffect(() => { + if (!user) { + toast.error('Please connect to wallet to verify') + navigate('/') + } + }, [user, navigate]) + + return ( + <> + {children} + + ) +}; + +export default ProtectedRoute; diff --git a/admin_frontend/src/constants/common.js b/admin_frontend/src/constants/common.js index b5ab577..94db3cd 100644 --- a/admin_frontend/src/constants/common.js +++ b/admin_frontend/src/constants/common.js @@ -1,5 +1,5 @@ export const ENDPOINTS = { 'getKeys': '/getKeys', - 'saveKey' : '/saveKey', + 'saveKey': '/saveKey', 'deleteKey': '/deleteKey', -} \ No newline at end of file +} diff --git a/admin_frontend/src/constants/constants.js b/admin_frontend/src/constants/constants.js new file mode 100644 index 0000000..0dacdcd --- /dev/null +++ b/admin_frontend/src/constants/constants.js @@ -0,0 +1,8 @@ +export const ENDPOINTS = { + "getConfig": "/getConfig", + "saveConfig": "/saveConfig", + "getKeys": "/getKeys", + "saveKey": "/saveKey", + "deleteKey": "/deleteKey", + "adminLogin": "/adminLogin" +} diff --git a/admin_frontend/src/constants/defaultNetworks.json b/admin_frontend/src/constants/defaultNetworks.json new file mode 100644 index 0000000..6adae86 --- /dev/null +++ b/admin_frontend/src/constants/defaultNetworks.json @@ -0,0 +1,170 @@ +[ + { + "chainId": 1, + "bundler": "https://ethereum-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x7F690e93CecFca5A31E6e1dF50A33F6d3059048c" + } + }, + { + "chainId": 5, + "bundler": "https://goerli-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0xcaDBADcFeD5530A49762DFc9d1d712CcD6b09b25" + } + }, + { + "chainId": 10, + "bundler": "https://optimism-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x805650ce74561C85baA44a8Bd13E19633Fd0F79d" + } + }, + { + "chainId": 14, + "bundler": "https://flare-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x8A41594e5c6Fe492e437414c24eA6f401186b8d2" + } + }, + { + "chainId": 56, + "bundler": "https://bnb-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0xEA5ecE95D3A28f9faB161779d20128b449F9EC9C" + } + }, + { + "chainId": 97, + "bundler": "https://bnbtestnet-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x153e26707DF3787183945B88121E4Eb188FDCAAA" + } + }, + { + "chainId": 100, + "bundler": "https://gnosis-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x373aBcF1EA9e5802778E32870e7f72C8A6a90349" + } + }, + { + "chainId": 114, + "bundler": "https://flaretestnet-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x2a18C360b525824B3e5656B5a705554f2a5036Be" + } + }, + { + "chainId": 122, + "bundler": "https://fuse-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0xEC2EE24E79C73DB13Dd9bC782856a5296626b7eb" + } + }, + { + "chainId": 137, + "bundler": "https://polygon-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x26FeC24b0D467C9de105217B483931e8f944ff50" + } + }, + { + "chainId": 420, + "bundler": "https://optimismgoerli-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a" + } + }, + { + "chainId": 1001, + "bundler": "https://klaytntestnet-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x810FA4C915015b703db0878CF2B9344bEB254a40" + } + }, + { + "chainId": 5000, + "bundler": "https://mantle-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x8A41594e5c6Fe492e437414c24eA6f401186b8d2" + } + }, + { + "chainId": 5001, + "bundler": "https://mantletestnet-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0xb56eC212C60C47fb7385f13b7247886FFa5E9D5C" + } + }, + { + "chainId": 8217, + "bundler": "https://klaytn-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x4ebd86AAF89151b5303DB072e0205C668e31E5E7" + } + }, + { + "chainId": 8453, + "bundler": "https://base-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x810FA4C915015b703db0878CF2B9344bEB254a40" + } + }, + { + "chainId": 42161, + "bundler": "https://arbitrum-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0xEC2EE24E79C73DB13Dd9bC782856a5296626b7eb" + } + }, + { + "chainId": 43114, + "bundler": "https://avalanche-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0x527569794781671319f20374A050BDbef4181aB3" + } + }, + { + "chainId": 59144, + "bundler": "https://linea-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0xB3AD9B9B06c6016f81404ee8FcCD0526F018Cf0C" + } + }, + { + "chainId": 80001, + "bundler": "https://mumbai-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x8350355c08aDAC387b443782124A30A8942BeC2e" + } + }, + { + "chainId": 84531, + "bundler": "https://basegoerli-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a" + } + }, + { + "chainId": 421613, + "bundler": "https://arbitrumgoerli-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0x898c530A5fA37720DcF1843AeCC34b6B0cBaEB8a" + } + }, + { + "chainId": 534351, + "bundler": "https://scrollsepolia-bundler.etherspot.io/", + "contracts": { + "etherspotPaymasterAddress": "0xe893A26DD53b325BffAacDfA224692EfF4C448c4" + } + }, + { + "chainId": 11155111, + "bundler": "https://sepolia-bundler.etherspot.io", + "contracts": { + "etherspotPaymasterAddress": "0xcaDBADcFeD5530A49762DFc9d1d712CcD6b09b25" + } + } +] diff --git a/admin_frontend/src/context/AuthContext.js b/admin_frontend/src/context/AuthContext.js new file mode 100644 index 0000000..3d893ce --- /dev/null +++ b/admin_frontend/src/context/AuthContext.js @@ -0,0 +1,86 @@ +import { createContext, useContext, useState } from 'react'; +import toast from "react-hot-toast"; +import { ENDPOINTS } from '../constants/constants'; + +const UserContext = createContext(); + +export const AuthContextProvider = ({ children }) => { + const [user, setUser] = useState(); + const [isSigningIn, setIsSigningIn] = useState(false); + + const initializeProvider = async () => { + const res = await window.ethereum + .request({ method: "eth_requestAccounts" }); + return res[0]; + } + + const accountChangeHandler = async (accounts) => { + try { + const data = await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['adminLogin']}`, { + method: "POST", + body: JSON.stringify({ WALLET_ADDRESS: accounts[0] }), + }); + const dataJson = await data.json(); + if (!dataJson.error) { + toast.success("Logged In Successfully"); + setUser({ address: accounts[0] }); + } else { + toast.error("Failed to authenticate with this wallet. Please make sure the address is associated with admin address given and try again"); + } + } catch (error) { + if (error?.message?.includes('Failed to fetch')) { + toast.error('Failed to access the server url'); + } else { + toast.error(error?.message); + } + setUser(null); + } + }; + + const signIn = async () => { + try { + if (!window.ethereum) { + toast.error('Cannot determine any injected wallet') + return null; + } + setIsSigningIn(true); + const address = await initializeProvider(); + const data = await fetch(`${process.env.REACT_APP_SERVER_URL}${ENDPOINTS['adminLogin']}`, { + method: "POST", + body: JSON.stringify({ WALLET_ADDRESS: address }), + }); + const dataJson = await data.json(); + if (!dataJson.error) { + toast.success("Logged In Successfully"); + setUser({ address }); + setIsSigningIn(false); + return { address }; + } else { + toast.error("Failed to authenticate with this wallet. Please make sure the address is associated with admin address given and try again"); + setIsSigningIn(false); + return null; + } + } catch (error) { + if (error?.message?.includes('Failed to fetch')) { + toast.error('Failed to access the server url') + } else { + toast.error(error?.message); + } + setIsSigningIn(false); + setUser(null); + return null; + } + }; + + window.ethereum.on('accountsChanged', accountChangeHandler) + + const logout = async () => { + setUser(null) + }; + + return {children}; +}; + +export const UserAuth = () => { + return useContext(UserContext); +}; diff --git a/admin_frontend/src/modals/AddERC20Paymaster.jsx b/admin_frontend/src/modals/AddERC20Paymaster.jsx new file mode 100644 index 0000000..0fb83b5 --- /dev/null +++ b/admin_frontend/src/modals/AddERC20Paymaster.jsx @@ -0,0 +1,257 @@ +import { useEffect, useState } from "react"; +import { utils } from "ethers"; +import toast from "react-hot-toast"; + +// components +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import { TextField } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const defaultERC20Row = { + chainId: 0, + token: "", + erc20PaymasterAddress: "", +}; + +const AddERC20PaymasterModal = ({ + supportedNetworks, + setSupportedNetworks, + open, + handleClose, + Edit, +}) => { + const [ERC20Row, setERC20Row] = useState(defaultERC20Row); + const [tokens, setTokens] = useState([]); + + const addRow = () => { + if ( + ERC20Row.erc20PaymasterAddress === "" || + ERC20Row.token === "" || + ERC20Row.chainId === 0 + ) { + toast.error("Please fill all textfields"); + return; + } + if (!utils.isAddress(ERC20Row.erc20PaymasterAddress)) { + toast.error("Please input a valid address"); + return; + } + if ( + supportedNetworks == {} || + !supportedNetworks[ERC20Row.chainId] || + !supportedNetworks[ERC20Row.chainId][ERC20Row.token] + ) { + supportedNetworks[ERC20Row.chainId] = { + ...supportedNetworks[ERC20Row.chainId], + [ERC20Row.token]: ERC20Row.erc20PaymasterAddress, + }; + setSupportedNetworks(supportedNetworks); + setERC20Row(defaultERC20Row); + Object.keys(supportedNetworks).map((key) => { + Object.keys(supportedNetworks[key]).map((sym) => { + console.log( + tokens.find( + (element) => element.chainId == key && element.token == sym + ) + ); + if ( + !tokens.find( + (element) => element.chainId == key && element.token == sym + ) + ) { + tokens.push({ + chainId: key, + token: sym, + address: supportedNetworks[key][sym], + }); + setTokens(tokens); + } + return; + }); + return; + }); + } else { + toast.error("Chain ID and Token already present"); + } + }; + + const deleteRow = (element) => { + delete supportedNetworks[element.chainId][element.token]; + if (Object.keys(supportedNetworks[element.chainId]).length === 0) + delete supportedNetworks[element.chainId]; + setSupportedNetworks(supportedNetworks); + setTokens( + tokens.filter( + (token) => + element.chainId !== token.chainId && element.token !== token.token + ) + ); + setERC20Row(defaultERC20Row); + }; + + useEffect(() => { + Object.keys(supportedNetworks).map((key) => { + Object.keys(supportedNetworks[key]).map((sym) => { + console.log( + tokens.find( + (element) => element.chainId == key && element.token == sym + ) + ); + if ( + !tokens.find( + (element) => element.chainId == key && element.token == sym + ) + ) { + tokens.push({ + chainId: key, + token: sym, + address: supportedNetworks[key][sym], + }); + setTokens(tokens); + } + }); + }); + }); + + return ( + + +
+ + ERC20 Paymasters + + +
+ + + + + Chain ID + Token + ERC20 Paymaster Address + Actions Available + + + + + + { + if (!isNaN(e.target.value)) + setERC20Row({ + ...ERC20Row, + chainId: Number(e.target.value), + }); + }} + value={ERC20Row.chainId} + required + fullWidth + /> + + + + setERC20Row({ + ...ERC20Row, + token: e.target.value, + }) + } + value={ERC20Row.token} + required + fullWidth + /> + + + + setERC20Row({ + ...ERC20Row, + erc20PaymasterAddress: e.target.value, + }) + } + value={ERC20Row.erc20PaymasterAddress} + required + fullWidth + /> + + + + + + + {tokens.map((element, index) => { + return ( + + {element.chainId} + {element.token} + {element.address} + + + + + ); + })} + +
+
+
+
+ ); +}; + +export default AddERC20PaymasterModal; diff --git a/admin_frontend/src/modals/AddSupportedNetworksModal.jsx b/admin_frontend/src/modals/AddSupportedNetworksModal.jsx new file mode 100644 index 0000000..2676159 --- /dev/null +++ b/admin_frontend/src/modals/AddSupportedNetworksModal.jsx @@ -0,0 +1,211 @@ +import { useState } from "react"; +import { utils } from "ethers"; +import toast from "react-hot-toast"; + +// components +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import { TextField } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const defaultSupportedNetworksRow = { + chainId: 0, + bundler: "", + contracts: { + etherspotPaymasterAddress: "", + }, +}; + +const AddSupportedNetworksModal = ({ + supportedNetworks, + setSupportedNetworks, + open, + handleClose, +}) => { + const [supportedNetworkRow, setSupportedNetworkRow] = useState( + defaultSupportedNetworksRow + ); + + const addRow = () => { + if ( + supportedNetworkRow.bundler === "" || + supportedNetworkRow.contracts.etherspotPaymasterAddress === "" || + supportedNetworkRow.chainId === 0 + ) { + toast.error("Please fill all textfields"); + return; + } + if ( + !utils.isAddress(supportedNetworkRow.contracts.etherspotPaymasterAddress) + ) { + toast.error("Please input a valid address"); + return; + } + if ( + !supportedNetworks.find( + (network) => network.chainId === supportedNetworkRow.chainId + ) + ) { + supportedNetworks.push(supportedNetworkRow); + setSupportedNetworks( + supportedNetworks.sort((a, b) => a.chainId - b.chainId) + ); + setSupportedNetworkRow(defaultSupportedNetworksRow); + } else { + toast.error("Chain ID already present"); + } + }; + + const deleteRow = (chainId) => { + setSupportedNetworks( + supportedNetworks.filter((element) => element.chainId != chainId) + ); + setSupportedNetworkRow(defaultSupportedNetworksRow); + }; + + return ( + + +
+ + Supported Networks + + +
+ + + + + Chain ID + Bundler URL + Paymaster Address + Actions Available + + + + + + { + if (!isNaN(e.target.value)) + setSupportedNetworkRow({ + ...supportedNetworkRow, + chainId: Number(e.target.value), + }); + }} + value={supportedNetworkRow.chainId} + required + fullWidth + /> + + + + setSupportedNetworkRow({ + ...supportedNetworkRow, + bundler: e.target.value, + }) + } + value={supportedNetworkRow.bundler} + required + fullWidth + /> + + + + setSupportedNetworkRow({ + ...supportedNetworkRow, + contracts: { + etherspotPaymasterAddress: e.target.value, + }, + }) + } + value={ + supportedNetworkRow.contracts.etherspotPaymasterAddress + } + required + fullWidth + /> + + + + + + {supportedNetworks.map((network, index) => { + return ( + + {network.chainId} + {network.bundler} + + {network.contracts.etherspotPaymasterAddress} + + + + + + ); + })} + +
+
+
+
+ ); +}; + +export default AddSupportedNetworksModal; diff --git a/admin_frontend/src/modals/CoingeckoId.jsx b/admin_frontend/src/modals/CoingeckoId.jsx new file mode 100644 index 0000000..fb07c0d --- /dev/null +++ b/admin_frontend/src/modals/CoingeckoId.jsx @@ -0,0 +1,216 @@ +import { useEffect, useState } from "react"; +import toast from "react-hot-toast"; + +// components +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import { TextField } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const defaultSupportedNetworksRow = { + chainId: "", + coingeckoId: "", +}; + +const CoingeckoIdModal = ({ + supportedNetworks, + setSupportedNetworks, + open, + handleClose, +}) => { + const [supportedNetworkRow, setSupportedNetworkRow] = useState( + defaultSupportedNetworksRow + ); + const [ids, setIds] = useState([]); + + const addRow = () => { + const tempId = ids; + if ( + supportedNetworkRow.coingeckoId === "" || + supportedNetworkRow.chainId === "" || + supportedNetworkRow.chainId === 0 + ) { + toast.error("Please fill all textfields"); + return; + } + if ( + !ids.find( + (network) => + network.chainId === supportedNetworkRow.chainId && + network.coingeckoId === supportedNetworkRow.coingeckoId + ) + ) { + if (!supportedNetworks[supportedNetworkRow.chainId.toString()]) + supportedNetworks[supportedNetworkRow.chainId.toString()] = [ + supportedNetworkRow.coingeckoId, + ]; + else { + supportedNetworks[supportedNetworkRow.chainId.toString()].push( + supportedNetworkRow.coingeckoId + ); + } + tempId.push({ + chainId: supportedNetworkRow.chainId, + coingeckoId: supportedNetworkRow.coingeckoId, + }); + setIds(tempId); + setSupportedNetworks(supportedNetworks); + setSupportedNetworkRow(defaultSupportedNetworksRow); + } else { + toast.error("Chain ID already present"); + } + }; + + const deleteRow = (network) => { + setIds( + ids.filter( + (element) => + element.chainId !== network.chainId && + element.coingeckoId !== network.coingeckoId + ) + ); + setSupportedNetworks( + supportedNetworks[network.chainId.toString()].filter( + (element) => element !== network.coingeckoId + ) + ); + setSupportedNetworkRow(defaultSupportedNetworksRow); + }; + + useEffect(() => { + const coingeckoIds = []; + for (const chain in supportedNetworks) { + for (const id of supportedNetworks[chain]) { + coingeckoIds.push({ + chainId: chain, + coingeckoId: id, + }); + } + } + setIds(coingeckoIds); + console.log(coingeckoIds); + }, [supportedNetworks]); + + return ( + + +
+ + CoinGecko IDs + + +
+ + + + + Chain ID + Coingecko ID + Actions Available + + + + + + { + if (!isNaN(e.target.value)) + setSupportedNetworkRow({ + ...supportedNetworkRow, + chainId: Number(e.target.value), + }); + }} + value={supportedNetworkRow.chainId} + required + fullWidth + /> + + + { + setSupportedNetworkRow({ + ...supportedNetworkRow, + coingeckoId: e.target.value, + }); + }} + value={supportedNetworkRow.coingeckoId} + required + fullWidth + /> + + + + + + {ids.map((network, index) => { + return ( + + {network.chainId} + {network.coingeckoId} + + + + + ); + })} + +
+
+
+
+ ); +}; + +export default CoingeckoIdModal; diff --git a/admin_frontend/src/modals/DeployedPaymasters.jsx b/admin_frontend/src/modals/DeployedPaymasters.jsx new file mode 100644 index 0000000..ccb4242 --- /dev/null +++ b/admin_frontend/src/modals/DeployedPaymasters.jsx @@ -0,0 +1,217 @@ +import { useEffect, useState } from "react"; +import { utils } from "ethers"; +import toast from "react-hot-toast"; + +// components +import AddCircleIcon from "@mui/icons-material/AddCircle"; +import RemoveCircleIcon from "@mui/icons-material/RemoveCircle"; +import { TextField } from "@mui/material"; +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const defaultSupportedNetworksRow = { + chainId: "", + address: "", +}; + +const DeployedPaymastersModal = ({ + supportedNetworks, + setSupportedNetworks, + open, + handleClose, + title, +}) => { + const [supportedNetworkRow, setSupportedNetworkRow] = useState( + defaultSupportedNetworksRow + ); + const [addresses, setAddresses] = useState([]); + + const addRow = () => { + const tempId = addresses; + if ( + supportedNetworkRow.address === "" || + supportedNetworkRow.chainId === "" || + supportedNetworkRow.chainId === 0 + ) { + toast.error("Please fill all textfields"); + return; + } + if (!utils.isAddress(supportedNetworkRow.address)) { + toast.error("Please input a valid address"); + return; + } + if ( + !addresses.find( + (network) => + network.chainId === supportedNetworkRow.chainId && + network.address === supportedNetworkRow.address + ) + ) { + const chain = supportedNetworkRow.chainId.toString(); + if (!supportedNetworks[chain]) { + supportedNetworks[chain] = []; + } + supportedNetworks[chain].push(supportedNetworkRow.address); + tempId.push({ + chainId: supportedNetworkRow.chainId, + address: supportedNetworkRow.address, + }); + setAddresses(tempId); + setSupportedNetworks(supportedNetworks); + setSupportedNetworkRow(defaultSupportedNetworksRow); + } else { + toast.error("Chain ID already present"); + } + }; + + const deleteRow = (network) => { + const tempAddr = addresses.filter( + (element) => + element.chainId.toString() !== network.chainId.toString() && + element.address !== network.address + ); + setAddresses(tempAddr); + supportedNetworks[network.chainId] = supportedNetworks[ + network.chainId + ].filter((element) => element !== network.address.toString()); + setSupportedNetworks(supportedNetworks); + setSupportedNetworkRow(defaultSupportedNetworksRow); + }; + + useEffect(() => { + const addr = []; + for (const chain in supportedNetworks) { + for (const address of supportedNetworks[chain]) { + addr.push({ + chainId: chain, + address, + }); + } + } + console.log(addr); + setAddresses(addr); + console.log(supportedNetworks); + }, [supportedNetworks]); + + return ( + + +
+ + {title} + + +
+ + + + + Chain ID + Address + Actions Available + + + + + + { + if (!isNaN(e.target.value)) + setSupportedNetworkRow({ + ...supportedNetworkRow, + chainId: Number(e.target.value), + }); + }} + value={supportedNetworkRow.chainId} + required + multiline + fullWidth + /> + + + { + setSupportedNetworkRow({ + ...supportedNetworkRow, + address: e.target.value, + }); + }} + value={supportedNetworkRow.address} + required + multiline + fullWidth + /> + + + + + + {addresses.map((network, index) => { + return ( + + {network.chainId} + {network.address} + + + + + ); + })} + +
+
+
+
+ ); +}; + +export default DeployedPaymastersModal; diff --git a/admin_frontend/src/modals/ViewERC20Paymaster.jsx b/admin_frontend/src/modals/ViewERC20Paymaster.jsx new file mode 100644 index 0000000..47bb7dc --- /dev/null +++ b/admin_frontend/src/modals/ViewERC20Paymaster.jsx @@ -0,0 +1,91 @@ +import { useEffect, useState } from "react"; + +// components +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const ViewERC20PaymasterModal = ({ supportedNetworks, open, handleClose }) => { + const [tokens, setTokens] = useState([]); + + useEffect(() => { + const tempTokens = []; + for (const key in supportedNetworks) { + for (const sym in supportedNetworks[key]) { + tempTokens.push({ + chainId: key, + token: sym, + address: supportedNetworks[key][sym], + }); + } + } + setTokens(tempTokens); + }, [supportedNetworks]); + + return ( + + +
+ + ERC20 Paymaster Address + + +
+ + + + + Chain ID + Token + ERC20 Paymaster Address + + + + {tokens.map((element) => { + return ( + + {element.chainId} + {element.token} + {element.address} + + ); + })} + +
+
+
+
+ ); +}; + +export default ViewERC20PaymasterModal; diff --git a/admin_frontend/src/modals/ViewSupportedNetworksModal.jsx b/admin_frontend/src/modals/ViewSupportedNetworksModal.jsx new file mode 100644 index 0000000..73b2657 --- /dev/null +++ b/admin_frontend/src/modals/ViewSupportedNetworksModal.jsx @@ -0,0 +1,77 @@ +// components +import Table from "@mui/material/Table"; +import TableBody from "@mui/material/TableBody"; +import TableCell from "@mui/material/TableCell"; +import TableContainer from "@mui/material/TableContainer"; +import TableHead from "@mui/material/TableHead"; +import TableRow from "@mui/material/TableRow"; +import Box from "@mui/material/Box"; +import Button from "@mui/material/Button"; +import Typography from "@mui/material/Typography"; +import Modal from "@mui/material/Modal"; + +const style = { + width: "100%", + bgcolor: "background.paper", + border: "2px solid #000", + boxShadow: 24, + p: 2, + flexDirection: "column", + overflowX: "scroll", +}; + +const ViewSupportedNetworksModal = ({ + supportedNetworks, + open, + handleClose, +}) => { + return ( + + +
+ + Supported Networks + + +
+ + + + + Chain ID + Bundler URL + Paymaster Address + + + + {supportedNetworks.map((network) => { + return ( + + {network.chainId} + {network.bundler} + + {network.contracts.etherspotPaymasterAddress} + + + ); + })} + +
+
+
+
+ ); +}; + +export default ViewSupportedNetworksModal; diff --git a/backend/package.json b/backend/package.json index 262d3f5..b48d40d 100644 --- a/backend/package.json +++ b/backend/package.json @@ -1,6 +1,6 @@ { "name": "arka", - "version": "1.1.0", + "version": "1.1.2", "description": "ARKA - (Albanian for Cashier's case) is the first open source Paymaster as a service software", "type": "module", "directories": { diff --git a/backend/src/constants/ErrorMessage.ts b/backend/src/constants/ErrorMessage.ts index 750e81c..06e4616 100644 --- a/backend/src/constants/ErrorMessage.ts +++ b/backend/src/constants/ErrorMessage.ts @@ -8,6 +8,7 @@ export default { INVALID_MODE: 'Invalid mode selected', DUPLICATE_RECORD: 'Duplicate record found', QUOTA_EXCEEDED: 'Quota exceeded for this month', + INVALID_USER: 'Unauthorised User', RECORD_NOT_FOUND: 'Api Key provided not found', API_KEY_VALIDATION_FAILED: 'Api Key is not in the right format as described in readme file', } diff --git a/backend/src/migrations/001.default.sql b/backend/src/migrations/001.default.sql index a0e329e..b808ab9 100644 --- a/backend/src/migrations/001.default.sql +++ b/backend/src/migrations/001.default.sql @@ -32,7 +32,7 @@ INSERT INTO config ( "5000", "0 0 * * *", "ewogICAgIjgwMDAxIjogWyIweGMzM2MzOEE3QkZFQmJCOTk3ZEQ0MDExQ0RkQWY0ZWJEMWU4ODAzQzAiXQp9", - "panther", + "eyI4MDAwMSI6WyJwYW50aGVyIl19", "https://api.coingecko.com/api/v3/simple/price?vs_currencies=usd&precision=8&ids="); -------------------------------------------------------------------------------- diff --git a/backend/src/plugins/config.ts b/backend/src/plugins/config.ts index 3a8d866..2cac84e 100644 --- a/backend/src/plugins/config.ts +++ b/backend/src/plugins/config.ts @@ -17,6 +17,7 @@ const ConfigSchema = Type.Strict( API_HOST: Type.String(), API_PORT: Type.String(), SUPPORTED_NETWORKS: Type.String() || undefined, + ADMIN_WALLET_ADDRESS: Type.String() || undefined, }) ); @@ -45,6 +46,7 @@ const configPlugin: FastifyPluginAsync = async (server) => { API_PORT: process.env.API_PORT ?? '', API_HOST: process.env.API_HOST ?? '', SUPPORTED_NETWORKS: process.env.SUPPORTED_NETWORKS ?? '', + ADMIN_WALLET_ADDRESS: process.env.ADMIN_WALLET_ADDRESS ?? '0x80a1874E1046B1cc5deFdf4D3153838B72fF94Ac', } server.decorate("config", config); diff --git a/backend/src/routes/admin.ts b/backend/src/routes/admin.ts index f039438..850830f 100644 --- a/backend/src/routes/admin.ts +++ b/backend/src/routes/admin.ts @@ -8,6 +8,20 @@ import { encode, decode } from "../utils/crypto.js"; import SupportedNetworks from "../../config.json" assert { type: "json" }; const adminRoutes: FastifyPluginAsync = async (server) => { + + server.post('/adminLogin', async function (request, reply) { + try { + const body: any = JSON.parse(request.body as string); + if (!body) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.EMPTY_BODY }); + if (!body.WALLET_ADDRESS) return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_DATA }); + console.log(body, server.config.ADMIN_WALLET_ADDRESS) + if (ethers.utils.getAddress(body.WALLET_ADDRESS) === server.config.ADMIN_WALLET_ADDRESS) return reply.code(ReturnCode.SUCCESS).send({error: null, message: "Successfully Logged in"}); + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_USER }); + } catch (err: any) { + return reply.code(ReturnCode.FAILURE).send({ error: ErrorMessage.INVALID_USER }); + } + }) + server.get("/getConfig", async function (request, reply) { try { const result: any = await new Promise((resolve, reject) => { diff --git a/backend/src/server.ts b/backend/src/server.ts index a7e7f68..1627359 100644 --- a/backend/src/server.ts +++ b/backend/src/server.ts @@ -101,14 +101,16 @@ const initializeServer = async (): Promise => { } } const customChainlinkDeploymentsbase64 = ConfigData.CUSTOM_CHAINLINK_DEPLOYED; + const coingeckoIdsbase64 = ConfigData.COINGECKO_IDS; if (customChainlinkDeploymentsbase64) { try { - const buffer = Buffer.from(customChainlinkDeploymentsbase64, 'base64'); + let buffer = Buffer.from(customChainlinkDeploymentsbase64, 'base64'); const customChainlinks = JSON.parse(buffer.toString()); + buffer = Buffer.from(coingeckoIdsbase64, 'base64'); + const coingeckoIds = JSON.parse(buffer.toString()); const customChainlinkDeployments = customChainlinks[chain] ?? []; if (customChainlinkDeployments.includes(deployedPaymaster)) { - const coingeckoIds = ConfigData.COINGECKO_IDS?.split(',') ?? ['']; - const coingeckoId = coingeckoIds[customChainlinkDeployments.indexOf(deployedPaymaster)] + const coingeckoId = coingeckoIds[chain][customChainlinkDeployments.indexOf(deployedPaymaster)] const response: any = await (await fetch(`${ConfigData.COINGECKO_API_URL}${coingeckoId}`)).json(); const price = ethers.utils.parseUnits(response[coingeckoId].usd.toString(), 8); if (price) { diff --git a/docker-compose.yml b/docker-compose.yml index 471bb10..382614e 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -2,6 +2,9 @@ version: "3.8" services: frontend: + environment: + - REACT_APP_INDEXER_ENDPOINT=http://localhost:3003 + - REACT_APP_SERVER_URL=http://localhost:5050 build: context: ./frontend dockerfile: Dockerfile @@ -18,7 +21,7 @@ services: - UNSAFE_MODE=false - SUPPORTED_NETWORKS= - CRON_PRIVATE_KEY= - - REACT_APP_INDEXER_ENDPOINT=http://localhost:3003 + - DEFAULT_INDEXER_ENDPOINT=http://localhost:3003 build: context: ./backend dockerfile: Dockerfile diff --git a/frontend/Dockerfile b/frontend/Dockerfile index 7311fc2..bf744d2 100644 --- a/frontend/Dockerfile +++ b/frontend/Dockerfile @@ -6,5 +6,6 @@ COPY package.json /frontend/ COPY config-overrides.js /frontend/ COPY postcss.config.js /frontend/ COPY tailwind.config.js /frontend/ +COPY demo.env /frontend/.env RUN npm install CMD ["npm", "start"] diff --git a/frontend/demo.env b/frontend/demo.env new file mode 100644 index 0000000..06a5d0c --- /dev/null +++ b/frontend/demo.env @@ -0,0 +1 @@ +REACT_APP_SERVER_URL=http://localhost:5050 diff --git a/frontend/package-lock.json b/frontend/package-lock.json index 1286d4a..d816516 100644 --- a/frontend/package-lock.json +++ b/frontend/package-lock.json @@ -1,12 +1,12 @@ { "name": "arka_frontend", - "version": "1.0.0", + "version": "1.0.2", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "arka_frontend", - "version": "1.0.0", + "version": "1.0.2", "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11", "@emotion/react": "^11.11.1", diff --git a/frontend/package.json b/frontend/package.json index 37b2add..a0e374d 100644 --- a/frontend/package.json +++ b/frontend/package.json @@ -1,6 +1,6 @@ { "name": "arka_frontend", - "version": "1.0.0", + "version": "1.0.2", "private": true, "dependencies": { "@babel/plugin-proposal-private-property-in-object": "7.21.11",