diff --git a/.env.example b/.env.example index d3a14e62..47cb26e7 100644 --- a/.env.example +++ b/.env.example @@ -1,2 +1,4 @@ VITE_APP_PROJECT_ID="your-project-id" -VITE_IPFS_KEY="your-ipfs-key" \ No newline at end of file +VITE_IPFS_KEY="your-ipfs-key" +VITE_ALCHEMY_KEY="your-alchemy-key" +VITE_DEV_ALCHEMY_KEY="your-dev-alchemy-key" \ No newline at end of file diff --git a/.storybook/preview.tsx b/.storybook/preview.tsx index f06095df..9109f4b4 100644 --- a/.storybook/preview.tsx +++ b/.storybook/preview.tsx @@ -6,15 +6,16 @@ * LICENSE file in the root directory of this source tree. */ -import type { Preview } from '@storybook/react'; import { withThemeByClassName } from '@storybook/addon-styling'; +import type { Preview } from '@storybook/react'; + import '../src/index.css'; import React from 'react'; import { MemoryRouter } from 'react-router-dom'; -import { wagmiClientDecorator } from '../src/lib/decorators/wagmiClientDecorator'; -import { AragonSDKWrapper } from '../src/context/AragonSDK'; -import { DiamondSDKWrapper } from '../src/context/DiamondGovernanceSDK'; + import { TooltipProvider } from '../src/components/ui/Tooltip'; +import { DiamondSDKWrapper } from '../src/context/DiamondGovernanceSDK'; +import { wagmiClientDecorator } from '../src/lib/decorators/wagmiClientDecorator'; const preview: Preview = { parameters: { @@ -48,16 +49,14 @@ const globalDecorator = (Story, context) => { document.documentElement.style.background = color; return ( - - - {/* MemoryRouter mimics a BrowserRouter, but without actually changing the URL in the browser, for testing */} - - - - - - - + + {/* MemoryRouter mimics a BrowserRouter, but without actually changing the URL in the browser, for testing */} + + + + + + ); }; diff --git a/README.md b/README.md index 9e6b19df..3aad5c9f 100644 --- a/README.md +++ b/README.md @@ -285,12 +285,12 @@ It is also worth mentioning that it is highly encouraged to write stories for th ### Web3 packages -#### Aragon SDK +#### Alchemy SDK -[Documentation](https://devs.aragon.org/docs/sdk/) +[Documentation](https://docs.alchemy.com/reference/alchemy-sdk-quickstart) -Primarily used for: interacting with default Aragon smart contracts -Aragon SDK is used for interacting with the smart contracts from Aragon. We've been extending the base smart contracts with our own plugins, so for those, we use the custom Plopmens SDK to interact with our custom plugins. Because of our own SDK implementation that supports our plugin, the aragon sdk might become unused. +Used for: fetching DAO transfers and token balances. +An Alchemy key is expected in the [`.env.`](./.env) file. #### Ethers diff --git a/package-lock.json b/package-lock.json index 0027b854..8af47ccd 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,16 +1,15 @@ { "name": "dao-webapp", - "version": "0.5.3", + "version": "0.6.0", "lockfileVersion": 3, "requires": true, "packages": { "": { "name": "dao-webapp", - "version": "0.5.3", + "version": "0.6.0", "license": "MIT", "dependencies": { - "@aragon/sdk-client": "^1.4.0", - "@plopmenz/diamond-governance-sdk": "^0.2.2", + "@plopmenz/diamond-governance-sdk": "^1.0.1", "@radix-ui/react-accordion": "^1.1.1", "@radix-ui/react-alert-dialog": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.2", @@ -38,6 +37,7 @@ "@walletconnect/types": "^2.7.2", "@web3modal/ethereum": "^2.3.7", "@web3modal/react": "^2.3.7", + "alchemy-sdk": "^2.9.0", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", "date-fns": "^2.30.0", @@ -134,57 +134,6 @@ "node": ">=6.0.0" } }, - "node_modules/@aragon/osx-ethers": { - "version": "1.2.1", - "resolved": "https://registry.npmjs.org/@aragon/osx-ethers/-/osx-ethers-1.2.1.tgz", - "integrity": "sha512-3Fscq8C9elIktiI6OT7fR5iaAvim+ghU6IUvZF3P/phvWm9roNp/GXAROhA/Vx41NQxeqmfXokgFo6KOWt4drA==", - "license": "AGPL-3.0-or-later", - "dependencies": { - "ethers": "^5.6.2" - } - }, - "node_modules/@aragon/sdk-client": { - "version": "1.4.0", - "license": "MIT", - "dependencies": { - "@aragon/osx-ethers": "^1.2.1", - "@aragon/sdk-common": "^1.1.1", - "@aragon/sdk-ipfs": "^1.1.0", - "@ethersproject/abstract-signer": "^5.5.0", - "@ethersproject/bignumber": "^5.6.0", - "@ethersproject/constants": "^5.6.0", - "@ethersproject/contracts": "^5.5.0", - "@ethersproject/providers": "^5.5.0", - "@ethersproject/wallet": "^5.6.0", - "graphql": "^16.5.0", - "graphql-request": "^4.3.0" - }, - "engines": { - "node": ">=16" - } - }, - "node_modules/@aragon/sdk-common": { - "version": "1.1.1", - "license": "MIT", - "engines": { - "node": ">=14" - } - }, - "node_modules/@aragon/sdk-ipfs": { - "version": "1.1.0", - "resolved": "https://registry.npmjs.org/@aragon/sdk-ipfs/-/sdk-ipfs-1.1.0.tgz", - "integrity": "sha512-2uAh/QPcmaita4AfHYV93lESzAhrmGEZ6CL7pvOH86HTkU6j7LnePvD1ly+x0hxRznTb+zgVgSPPKUn0ArPycw==", - "license": "MIT", - "dependencies": { - "@web-std/fetch": "^4.1.0", - "@web-std/file": "^3.0.2", - "@web-std/form-data": "^3.0.2", - "isomorphic-unfetch": "^3.1.0" - }, - "engines": { - "node": ">=16" - } - }, "node_modules/@arcanis/slice-ansi": { "version": "1.1.1", "resolved": "https://registry.npmjs.org/@arcanis/slice-ansi/-/slice-ansi-1.1.1.tgz", @@ -5885,9 +5834,9 @@ "integrity": "sha512-HaW78NszGzRZd9SeoI3JD11JqY+lubnaOx7Pewj5pfjqWXOEATpeKIFb9Z4t2WBUK2iryiXX3lzWwmYWgUL0Ug==" }, "node_modules/@plopmenz/diamond-governance-sdk": { - "version": "0.2.2", - "resolved": "https://registry.npmjs.org/@plopmenz/diamond-governance-sdk/-/diamond-governance-sdk-0.2.2.tgz", - "integrity": "sha512-iUw3jwuw59P55yXzIVyQjcosLajE/XthEuHknvjOetTPlBgYubrvvKI1dT+rACp/v5OxiaNOtE9T1GcvdOxAdA==", + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/@plopmenz/diamond-governance-sdk/-/diamond-governance-sdk-1.0.1.tgz", + "integrity": "sha512-+uWnbCHGCVnnAu4WNB95Y4iqgpVbD4xvOpuzGQTim7XzC5Ds4Cl4bCwUmuR6q3DeOEZqL5Cg+ibWDL3BjBlnUg==", "dependencies": { "@ethersproject/abi": "^5.7.0", "@ethersproject/abstract-signer": "^5.7.0", @@ -12282,75 +12231,6 @@ "tslib": "1.14.1" } }, - "node_modules/@web-std/blob": { - "version": "3.0.4", - "resolved": "https://registry.npmjs.org/@web-std/blob/-/blob-3.0.4.tgz", - "integrity": "sha512-+dibyiw+uHYK4dX5cJ7HA+gtDAaUUe6JsOryp2ZpAC7h4ICsh49E34JwHoEKPlPvP0llCrNzz45vvD+xX5QDBg==", - "license": "MIT", - "dependencies": { - "@web-std/stream": "1.0.0", - "web-encoding": "1.1.5" - } - }, - "node_modules/@web-std/blob/node_modules/@web-std/stream": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@web-std/stream/-/stream-1.0.0.tgz", - "integrity": "sha512-jyIbdVl+0ZJyKGTV0Ohb9E6UnxP+t7ZzX4Do3AHjZKxUXKMs9EmqnBDQgHF7bEw0EzbQygOjtt/7gvtmi//iCQ==", - "license": "MIT", - "dependencies": { - "web-streams-polyfill": "^3.1.1" - } - }, - "node_modules/@web-std/fetch": { - "version": "4.1.0", - "resolved": "https://registry.npmjs.org/@web-std/fetch/-/fetch-4.1.0.tgz", - "integrity": "sha512-ZRizMcP8YyuRlhIsRYNFD9x/w28K7kbUhNGmKM9hDy4qeQ5xMTk//wA89EF+Clbl6EP4ksmCcN+4TqBMSRL8Zw==", - "license": "MIT", - "dependencies": { - "@web-std/blob": "^3.0.3", - "@web-std/form-data": "^3.0.2", - "@web-std/stream": "^1.0.1", - "@web3-storage/multipart-parser": "^1.0.0", - "data-uri-to-buffer": "^3.0.1", - "mrmime": "^1.0.0" - }, - "engines": { - "node": "^10.17 || >=12.3" - } - }, - "node_modules/@web-std/file": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@web-std/file/-/file-3.0.2.tgz", - "integrity": "sha512-pIH0uuZsmY8YFvSHP1NsBIiMT/1ce0suPrX74fEeO3Wbr1+rW0fUGEe4d0R99iLwXtyCwyserqCFI4BJkJlkRA==", - "license": "MIT", - "dependencies": { - "@web-std/blob": "^3.0.3" - } - }, - "node_modules/@web-std/form-data": { - "version": "3.0.2", - "resolved": "https://registry.npmjs.org/@web-std/form-data/-/form-data-3.0.2.tgz", - "integrity": "sha512-rhc8IRw66sJ0FHcnC84kT3mTN6eACTuNftkt1XSl1Ef6WRKq4Pz65xixxqZymAZl1K3USpwhLci4SKNn4PYxWQ==", - "license": "MIT", - "dependencies": { - "web-encoding": "1.1.5" - } - }, - "node_modules/@web-std/stream": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/@web-std/stream/-/stream-1.0.1.tgz", - "integrity": "sha512-tsz4Y0WNDgFA5jwLSeV7/UV5rfMIlj0cPsSLVfTihjaVW0OJPd5NxJ3le1B3yLyqqzRpeG5OAfJAADLc4VoGTA==", - "license": "MIT", - "dependencies": { - "web-streams-polyfill": "^3.1.1" - } - }, - "node_modules/@web3-storage/multipart-parser": { - "version": "1.0.0", - "resolved": "https://registry.npmjs.org/@web3-storage/multipart-parser/-/multipart-parser-1.0.0.tgz", - "integrity": "sha512-BEO6al7BYqcnfX15W2cnGR+Q566ACXAT9UQykORCWW80lmkpWsnEob6zJS1ZVBKsSJC8+7vJkHwlp+lXG1UCdw==", - "license": "(Apache-2.0 AND MIT)" - }, "node_modules/@web3modal/core": { "version": "2.3.7", "license": "Apache-2.0", @@ -12859,13 +12739,6 @@ "node": ">=10.19.0" } }, - "node_modules/@zxing/text-encoding": { - "version": "0.9.0", - "resolved": "https://registry.npmjs.org/@zxing/text-encoding/-/text-encoding-0.9.0.tgz", - "integrity": "sha512-U/4aVJ2mxI0aDNI8Uq0wEhMgY+u4CNtEb0om3+y3+niDAsoTCOB33UF0sxpzqzdqXLqmvc+vZyAt4O8pPdfkwA==", - "license": "(Unlicense OR Apache-2.0)", - "optional": true - }, "node_modules/abitype": { "version": "0.3.0", "resolved": "https://registry.npmjs.org/abitype/-/abitype-0.3.0.tgz", @@ -13043,6 +12916,35 @@ "ajv": "^6.9.1" } }, + "node_modules/alchemy-sdk": { + "version": "2.9.0", + "resolved": "https://registry.npmjs.org/alchemy-sdk/-/alchemy-sdk-2.9.0.tgz", + "integrity": "sha512-vPFMGP43LrFQ9XOLHgVNyJCd1DDPK5qXniF5pdZzUA52h/FMHKkMaOayZNf63Y6GDQ1KGHqtyJIoSZtbPeg9kw==", + "dependencies": { + "@ethersproject/abi": "^5.7.0", + "@ethersproject/abstract-provider": "^5.7.0", + "@ethersproject/bignumber": "^5.7.0", + "@ethersproject/bytes": "^5.7.0", + "@ethersproject/contracts": "^5.7.0", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/networks": "^5.7.0", + "@ethersproject/providers": "^5.7.0", + "@ethersproject/units": "^5.7.0", + "@ethersproject/wallet": "^5.7.0", + "@ethersproject/web": "^5.7.0", + "axios": "^0.26.1", + "sturdy-websocket": "^0.2.1", + "websocket": "^1.0.34" + } + }, + "node_modules/alchemy-sdk/node_modules/axios": { + "version": "0.26.1", + "resolved": "https://registry.npmjs.org/axios/-/axios-0.26.1.tgz", + "integrity": "sha512-fPwcX4EvnSHuInCMItEhAGnaSEXRBjtzh9fOtsE6E1G6p7vl7edEeZe11QHf18+6+9gR5PbKV/sGKNaD8YaMeA==", + "dependencies": { + "follow-redirects": "^1.14.8" + } + }, "node_modules/ansi-align": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/ansi-align/-/ansi-align-3.0.1.tgz", @@ -14179,7 +14081,6 @@ "integrity": "sha512-kukuqc39WOHtdxtw4UScxF/WVnMFVSQVKhtx3AjZJzhd0RGZZldcrfSEbVsWWe6KNH253574cq5F+wpv0G9pJw==", "hasInstallScript": true, "license": "MIT", - "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -15184,26 +15085,25 @@ "node": ">=0.8" } }, + "node_modules/d": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/d/-/d-1.0.1.tgz", + "integrity": "sha512-m62ShEObQ39CfralilEQRjH6oAMtNCV1xJyEx5LpRYUVN+EviphDgUc/F3hnYbADmkiNs67Y+3ylmlG7Lnu+FA==", + "dependencies": { + "es5-ext": "^0.10.50", + "type": "^1.0.1" + } + }, "node_modules/dash-get": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/dash-get/-/dash-get-1.0.2.tgz", "integrity": "sha512-4FbVrHDwfOASx7uQVxeiCTo7ggSdYZbqs8lH+WU6ViypPlDbe9y6IP5VVUDQBv9DcnyaiPT5XT0UWHgJ64zLeQ==", "license": "MIT" }, - "node_modules/data-uri-to-buffer": { - "version": "3.0.1", - "resolved": "https://registry.npmjs.org/data-uri-to-buffer/-/data-uri-to-buffer-3.0.1.tgz", - "integrity": "sha512-WboRycPNsVw3B3TL559F7kuBUM4d8CgMEvk6xEJlOp7OBPjt6G7z8WMWlD2rOFZLk6OYfFIUGsCOWzcQH9K2og==", - "license": "MIT", - "engines": { - "node": ">= 6" - } - }, "node_modules/date-fns": { "version": "2.30.0", "resolved": "https://registry.npmjs.org/date-fns/-/date-fns-2.30.0.tgz", "integrity": "sha512-fnULvOpxnC5/Vg3NCiWelDsLiUc9bRwAPs/+LfTLNvetFCtCTN+yQz15C/fs4AwX1R9K5GLtLfn8QW+dWisaAw==", - "license": "MIT", "dependencies": { "@babel/runtime": "^7.21.0" }, @@ -16032,6 +15932,20 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/es5-ext": { + "version": "0.10.62", + "resolved": "https://registry.npmjs.org/es5-ext/-/es5-ext-0.10.62.tgz", + "integrity": "sha512-BHLqn0klhEpnOKSrzn/Xsz2UIW8j+cGmo9JLzr8BiUapV8hPL9+FliFqjwr9ngW7jWdnxv6eO+/LqyhJVqgrjA==", + "hasInstallScript": true, + "dependencies": { + "es6-iterator": "^2.0.3", + "es6-symbol": "^3.1.3", + "next-tick": "^1.1.0" + }, + "engines": { + "node": ">=0.10" + } + }, "node_modules/es6-error": { "version": "4.1.1", "resolved": "https://registry.npmjs.org/es6-error/-/es6-error-4.1.1.tgz", @@ -16039,6 +15953,16 @@ "dev": true, "license": "MIT" }, + "node_modules/es6-iterator": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/es6-iterator/-/es6-iterator-2.0.3.tgz", + "integrity": "sha512-zw4SRzoUkd+cl+ZoE15A9o1oQd920Bb0iOJMQkQhl3jNc03YqVjAhG7scf9C5KWRU/R13Orf588uCC6525o02g==", + "dependencies": { + "d": "1", + "es5-ext": "^0.10.35", + "es6-symbol": "^3.1.1" + } + }, "node_modules/es6-object-assign": { "version": "1.1.0", "resolved": "https://registry.npmjs.org/es6-object-assign/-/es6-object-assign-1.1.0.tgz", @@ -16059,6 +15983,15 @@ "es6-promise": "^4.0.3" } }, + "node_modules/es6-symbol": { + "version": "3.1.3", + "resolved": "https://registry.npmjs.org/es6-symbol/-/es6-symbol-3.1.3.tgz", + "integrity": "sha512-NJ6Yn3FuDinBaBRWl/q5X/s4koRHBrgKAu+yGI6JCBeiu3qrcbJhwT2GeR/EXVfylRk8dpQVJoLEFhK+Mu31NA==", + "dependencies": { + "d": "^1.0.1", + "ext": "^1.1.2" + } + }, "node_modules/esbuild": { "version": "0.17.19", "resolved": "https://registry.npmjs.org/esbuild/-/esbuild-0.17.19.tgz", @@ -16923,24 +16856,25 @@ "url": "https://github.com/sponsors/ljharb" } }, + "node_modules/ext": { + "version": "1.7.0", + "resolved": "https://registry.npmjs.org/ext/-/ext-1.7.0.tgz", + "integrity": "sha512-6hxeJYaL110a9b5TEJSj0gojyHQAmA2ch5Os+ySCiA1QGdS697XWY1pzsrSjqA9LDEEgdB/KypIlR59RcLuHYw==", + "dependencies": { + "type": "^2.7.2" + } + }, + "node_modules/ext/node_modules/type": { + "version": "2.7.2", + "resolved": "https://registry.npmjs.org/type/-/type-2.7.2.tgz", + "integrity": "sha512-dzlvlNlt6AXU7EBSfpAscydQ7gXB+pPGsPnfJnZpiNJBDj7IaJzQlBZYGdEi4R9HmPdBv2XmWJ6YUtoTa7lmCw==" + }, "node_modules/extend": { "version": "3.0.2", "resolved": "https://registry.npmjs.org/extend/-/extend-3.0.2.tgz", "integrity": "sha512-fjquC59cD7CyW6urNXK0FBufkZcoiGG80wTuPujX590cB5Ttln20E2UB4S/WARVqhXffZl2LNgS+gQdPIIim/g==", "dev": true }, - "node_modules/extract-files": { - "version": "9.0.0", - "resolved": "https://registry.npmjs.org/extract-files/-/extract-files-9.0.0.tgz", - "integrity": "sha512-CvdFfHkC95B4bBBk36hcEmvdR2awOdhhVUYH6S/zrVj3477zven/fJMYg7121h4T1xHZC+tetUpubpAhxwI7hQ==", - "license": "MIT", - "engines": { - "node": "^10.17.0 || ^12.0.0 || >= 13.7.0" - }, - "funding": { - "url": "https://github.com/sponsors/jaydenseric" - } - }, "node_modules/extract-zip": { "version": "1.7.0", "resolved": "https://registry.npmjs.org/extract-zip/-/extract-zip-1.7.0.tgz", @@ -17495,6 +17429,7 @@ "version": "3.0.1", "resolved": "https://registry.npmjs.org/form-data/-/form-data-3.0.1.tgz", "integrity": "sha512-RHkBKtLWUVwd7SqRIvCZMEvAMoGUp0XU+seQiZejj0COz3RI3hWP4sCv3gZWWLjJTd7rGwcsF5eKZGii0r/hbg==", + "dev": true, "license": "MIT", "dependencies": { "asynckit": "^0.4.0", @@ -18044,29 +17979,6 @@ "integrity": "sha512-bzh50DW9kTPM00T8y4o8vQg89Di9oLJVLW/KaOGIXJWP/iqCN6WKYkbNOF04vFLJhwcpYUh9ydh/+5vpOqV4YQ==", "license": "MIT" }, - "node_modules/graphql": { - "version": "16.6.0", - "resolved": "https://registry.npmjs.org/graphql/-/graphql-16.6.0.tgz", - "integrity": "sha512-KPIBPDlW7NxrbT/eh4qPXz5FiFdL5UbaA0XUNz2Rp3Z3hqBSkbj0GVjwFDztsWVauZUWsbKHgMg++sk8UX0bkw==", - "license": "MIT", - "engines": { - "node": "^12.22.0 || ^14.16.0 || ^16.0.0 || >=17.0.0" - } - }, - "node_modules/graphql-request": { - "version": "4.3.0", - "resolved": "https://registry.npmjs.org/graphql-request/-/graphql-request-4.3.0.tgz", - "integrity": "sha512-2v6hQViJvSsifK606AliqiNiijb1uwWp6Re7o0RTyH+uRTv/u7Uqm2g4Fjq/LgZIzARB38RZEvVBFOQOVdlBow==", - "license": "MIT", - "dependencies": { - "cross-fetch": "^3.1.5", - "extract-files": "^9.0.0", - "form-data": "^3.0.0" - }, - "peerDependencies": { - "graphql": "14 - 16" - } - }, "node_modules/gunzip-maybe": { "version": "1.4.2", "resolved": "https://registry.npmjs.org/gunzip-maybe/-/gunzip-maybe-1.4.2.tgz", @@ -19375,6 +19287,7 @@ "version": "3.1.0", "resolved": "https://registry.npmjs.org/isomorphic-unfetch/-/isomorphic-unfetch-3.1.0.tgz", "integrity": "sha512-geDJjpoZ8N0kWexiwkX8F9NkTsXhetLPVbZFQ+JTW239QNOwvB0gniuR1Wc6f0AMTn7/mFGyXvHTifrCp/GH8Q==", + "dev": true, "license": "MIT", "dependencies": { "node-fetch": "^2.6.1", @@ -26034,15 +25947,6 @@ "node": ">=4" } }, - "node_modules/mrmime": { - "version": "1.0.1", - "resolved": "https://registry.npmjs.org/mrmime/-/mrmime-1.0.1.tgz", - "integrity": "sha512-hzzEagAgDyoU1Q6yg5uI+AorQgdvMCur3FcKf7NhMKWsaYg+RnbTyHRa/9IlLF9rf455MOCtcqqrQQ83pPP7Uw==", - "license": "MIT", - "engines": { - "node": ">=10" - } - }, "node_modules/ms": { "version": "2.1.2", "resolved": "https://registry.npmjs.org/ms/-/ms-2.1.2.tgz", @@ -26154,6 +26058,11 @@ "dev": true, "license": "MIT" }, + "node_modules/next-tick": { + "version": "1.1.0", + "resolved": "https://registry.npmjs.org/next-tick/-/next-tick-1.1.0.tgz", + "integrity": "sha512-CXdUiJembsNjuToQvxayPZF9Vqht7hewsvy2sOWafLvi2awflj9mOC6bHIg50orX8IJvWKY9wYQ/zB2kogPslQ==" + }, "node_modules/node-addon-api": { "version": "2.0.2", "resolved": "https://registry.npmjs.org/node-addon-api/-/node-addon-api-2.0.2.tgz", @@ -30649,6 +30558,11 @@ "url": "https://github.com/sponsors/sindresorhus" } }, + "node_modules/sturdy-websocket": { + "version": "0.2.1", + "resolved": "https://registry.npmjs.org/sturdy-websocket/-/sturdy-websocket-0.2.1.tgz", + "integrity": "sha512-NnzSOEKyv4I83qbuKw9ROtJrrT6Z/Xt7I0HiP/e6H6GnpeTDvzwGIGeJ8slai+VwODSHQDooW2CAilJwT9SpRg==" + }, "node_modules/style-loader": { "version": "1.3.0", "resolved": "https://registry.npmjs.org/style-loader/-/style-loader-1.3.0.tgz", @@ -31642,6 +31556,11 @@ "node": ">=0.6.11 <=0.7.0 || >=0.7.3" } }, + "node_modules/type": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/type/-/type-1.2.0.tgz", + "integrity": "sha512-+5nt5AAniqsCnu2cEQQdpzCAh33kVx8n0VoFidKpB1dVVLAN/F+bgVOqOJqOnEnrhp222clB5p3vUlD+1QAnfg==" + }, "node_modules/type-check": { "version": "0.4.0", "resolved": "https://registry.npmjs.org/type-check/-/type-check-0.4.0.tgz", @@ -31778,6 +31697,7 @@ "version": "4.2.0", "resolved": "https://registry.npmjs.org/unfetch/-/unfetch-4.2.0.tgz", "integrity": "sha512-F9p7yYCn6cIW9El1zi0HI6vqpeIvBsr3dSuRO6Xuppb1u5rXpCPmMvLSyECLhybr9isec8Ohl0hPekMVrEinDA==", + "dev": true, "license": "MIT" }, "node_modules/unicode-canonical-property-names-ecmascript": { @@ -32103,7 +32023,6 @@ "integrity": "sha512-Z6czzLq4u8fPOyx7TU6X3dvUZVvoJmxSQ+IcrlmagKhilxlhZgxPK6C5Jqbkw1IDUmFTM+cz9QDnnLTwDz/2gQ==", "hasInstallScript": true, "license": "MIT", - "optional": true, "dependencies": { "node-gyp-build": "^4.3.0" }, @@ -32409,27 +32328,6 @@ "defaults": "^1.0.3" } }, - "node_modules/web-encoding": { - "version": "1.1.5", - "resolved": "https://registry.npmjs.org/web-encoding/-/web-encoding-1.1.5.tgz", - "integrity": "sha512-HYLeVCdJ0+lBYV2FvNZmv3HJ2Nt0QYXqZojk3d9FJOLkwnuhzM9tmamh8d7HPM8QqjKH8DeHkFTx+CFlWpZZDA==", - "license": "MIT", - "dependencies": { - "util": "^0.12.3" - }, - "optionalDependencies": { - "@zxing/text-encoding": "0.9.0" - } - }, - "node_modules/web-streams-polyfill": { - "version": "3.2.1", - "resolved": "https://registry.npmjs.org/web-streams-polyfill/-/web-streams-polyfill-3.2.1.tgz", - "integrity": "sha512-e0MO3wdXWKrLbL0DgGnUV7WHVuw9OUvL4hjgnPkIeEvESk74gAITi5G606JtZPp39cd8HA9VQzCIvA49LpPN5Q==", - "license": "MIT", - "engines": { - "node": ">= 8" - } - }, "node_modules/webidl-conversions": { "version": "3.0.1", "resolved": "https://registry.npmjs.org/webidl-conversions/-/webidl-conversions-3.0.1.tgz", @@ -32538,6 +32436,35 @@ "url": "https://opencollective.com/webpack" } }, + "node_modules/websocket": { + "version": "1.0.34", + "resolved": "https://registry.npmjs.org/websocket/-/websocket-1.0.34.tgz", + "integrity": "sha512-PRDso2sGwF6kM75QykIesBijKSVceR6jL2G8NGYyq2XrItNC2P5/qL5XeR056GhA+Ly7JMFvJb9I312mJfmqnQ==", + "dependencies": { + "bufferutil": "^4.0.1", + "debug": "^2.2.0", + "es5-ext": "^0.10.50", + "typedarray-to-buffer": "^3.1.5", + "utf-8-validate": "^5.0.2", + "yaeti": "^0.0.6" + }, + "engines": { + "node": ">=4.0.0" + } + }, + "node_modules/websocket/node_modules/debug": { + "version": "2.6.9", + "resolved": "https://registry.npmjs.org/debug/-/debug-2.6.9.tgz", + "integrity": "sha512-bC7ElrdJaJnPbAP+1EotYvqZsb3ecl5wi6Bfi6BJTUcNowp6cvspg0jXznRTKDjm/E7AdgFBVeAPVMNcKGsHMA==", + "dependencies": { + "ms": "2.0.0" + } + }, + "node_modules/websocket/node_modules/ms": { + "version": "2.0.0", + "resolved": "https://registry.npmjs.org/ms/-/ms-2.0.0.tgz", + "integrity": "sha512-Tpp60P6IUJDTuOq/5Z8cdskzJujfwqfOTkrwIwj7IRISpnkJnT6SyJ4PCPnGMoFjC9ddhal5KVIYtAt97ix05A==" + }, "node_modules/whatwg-encoding": { "version": "2.0.0", "resolved": "https://registry.npmjs.org/whatwg-encoding/-/whatwg-encoding-2.0.0.tgz", @@ -32783,6 +32710,14 @@ "node": ">=10" } }, + "node_modules/yaeti": { + "version": "0.0.6", + "resolved": "https://registry.npmjs.org/yaeti/-/yaeti-0.0.6.tgz", + "integrity": "sha512-MvQa//+KcZCUkBTIC9blM+CU9J2GzuTytsOUwf2lidtvkx/6gnEp1QvJv34t9vdjhFmha/mUiNDbN0D0mJWdug==", + "engines": { + "node": ">=0.10.32" + } + }, "node_modules/yallist": { "version": "3.1.1", "resolved": "https://registry.npmjs.org/yallist/-/yallist-3.1.1.tgz", diff --git a/package.json b/package.json index a83880e3..c9392ddc 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "name": "dao-webapp", "private": true, - "version": "0.5.3", + "version": "0.6.0", "type": "module", "license": "MIT", "scripts": { @@ -16,8 +16,7 @@ "test": "jest" }, "dependencies": { - "@aragon/sdk-client": "^1.4.0", - "@plopmenz/diamond-governance-sdk": "^0.2.2", + "@plopmenz/diamond-governance-sdk": "^1.0.1", "@radix-ui/react-accordion": "^1.1.1", "@radix-ui/react-alert-dialog": "^1.0.3", "@radix-ui/react-collapsible": "^1.0.2", @@ -45,6 +44,7 @@ "@walletconnect/types": "^2.7.2", "@web3modal/ethereum": "^2.3.7", "@web3modal/react": "^2.3.7", + "alchemy-sdk": "^2.9.0", "class-variance-authority": "^0.6.0", "clsx": "^1.2.1", "date-fns": "^2.30.0", diff --git a/src/components/finance/DepositAssets.tsx b/src/components/finance/DepositAssets.tsx index 81f3fc48..97b836ed 100644 --- a/src/components/finance/DepositAssets.tsx +++ b/src/components/finance/DepositAssets.tsx @@ -6,9 +6,10 @@ * LICENSE file in the root directory of this source tree. */ -import { useEffect, useState } from 'react'; +import { useState } from 'react'; import Loading from '@/src/components/icons/Loading'; import { Address } from '@/src/components/ui/Address'; +import { Button } from '@/src/components/ui/Button'; import { Card } from '@/src/components/ui/Card'; import { ConditionalButton, @@ -17,9 +18,10 @@ import { } from '@/src/components/ui/ConditionalButton'; import { ErrorWrapper } from '@/src/components/ui/ErrorWrapper'; import Header from '@/src/components/ui/Header'; -import { LabelledInput } from '@/src/components/ui/Input'; +import { Input } from '@/src/components/ui/Input'; import { Label } from '@/src/components/ui/Label'; import { Link } from '@/src/components/ui/Link'; +import { MaxButton } from '@/src/components/ui/MaxButton'; import { Select, SelectContent, @@ -30,43 +32,32 @@ import { SelectValue, } from '@/src/components/ui/Select'; import { useDiamondSDKContext } from '@/src/context/DiamondGovernanceSDK'; -import { useSecoinBalance } from '@/src/hooks/useSecoinBalance'; +import { + Pools, + TokenData, + pools, + useDepositAssets, +} from '@/src/hooks/useDepositAssets'; import { ContractTransactionToast, toast } from '@/src/hooks/useToast'; import { PREFERRED_NETWORK_METADATA } from '@/src/lib/constants/chains'; import { NumberPattern } from '@/src/lib/constants/patterns'; import { TOKENS } from '@/src/lib/constants/tokens'; import { parseTokenAmount } from '@/src/lib/utils/token'; import { BigNumber } from 'ethers'; +import { formatUnits } from 'ethers/lib/utils.js'; import { Controller, useForm, useWatch } from 'react-hook-form'; import { HiChevronLeft } from 'react-icons/hi2'; -import { - erc20ABI, - useAccount, - useBalance, - useContractWrite, - useNetwork, - usePrepareContractWrite, - usePrepareSendTransaction, - useSendTransaction, -} from 'wagmi'; +import { useAccount, useBalance, useNetwork, Address as wAddress } from 'wagmi'; type DepositAssetsData = { token: Token; + pool?: Pools; amount?: string; }; -type AddressString = `0x${string}`; -type TokenData = - | { - address: AddressString; - isNativeToken: boolean; - decimals: number; - } - | undefined; type Token = (typeof Tokens)[number]; // All tokens (including native tokens) -// NOTE: Currently, only tokens with exactly 18 decimals are supported -const Tokens = ['Matic', 'SECOIN', 'Other'] as const; +const Tokens = ['Matic', 'SECOIN', 'DAI', 'Other'] as const; export const DepositAssets = () => { const { @@ -75,29 +66,39 @@ export const DepositAssets = () => { handleSubmit, formState: { errors }, setError, - } = useForm({}); + setValue, + } = useForm({ + defaultValues: { + pool: 'General', + }, + }); // Context const { daoAddress, secoinAddress } = useDiamondSDKContext(); const { isConnected, address } = useAccount(); - const { secoinBalance } = useSecoinBalance({ address }); const { data: maticData } = useBalance({ address }); const { chain } = useNetwork(); - // Creating 'tokens', the object displaying known tokens that can be deposited through this component, using ERC20 contract writes or native token transaction. - const secoin: TokenData = secoinAddress + // Creating 'tokens', the object displaying known tokens that can be deposited through this component, + // using ERC20 contract writes or native token transaction. + const secoin: TokenData | undefined = secoinAddress ? { - address: secoinAddress as AddressString, + address: secoinAddress as wAddress, isNativeToken: false, decimals: TOKENS.secoin.decimals, } : undefined; - const tokens: Record = { + const tokens: Record = { Matic: { - address: '0x0000000000000000000000000000000000001010', + address: PREFERRED_NETWORK_METADATA.nativeToken.address, isNativeToken: true, - decimals: PREFERRED_NETWORK_METADATA.nativeCurrency.decimals, + decimals: PREFERRED_NETWORK_METADATA.nativeToken.decimals, }, SECOIN: secoin, + DAI: { + address: '0x8f3Cf7ad23Cd3CaDbD9735AFf958023239c6A063', + isNativeToken: false, + decimals: 18, + }, Other: undefined, }; @@ -109,35 +110,22 @@ export const DepositAssets = () => { const watchAmount = useWatch({ control: control, name: 'amount' }); const amount = parseTokenAmount(watchAmount, token?.decimals); - // Hooks for non native tokens - const debouncedTokenId = useDebounce( - [amount ?? BigNumber.from(0), token?.address], - 500 - ); - const { config, error } = usePrepareContractWrite({ - address: token?.address, - abi: erc20ABI, - functionName: 'transfer', - args: [daoAddress as AddressString, amount ?? BigNumber.from(0)], - enabled: Boolean(debouncedTokenId) && !token?.isNativeToken, - }); - - const { writeAsync } = useContractWrite(config); + const pool = useWatch({ control, name: 'pool' }); - // Hooks for native tokens - const { config: configNative, error: errorNative } = - usePrepareSendTransaction({ - request: { to: daoAddress as string, value: amount ?? BigNumber.from(0) }, - chainId: PREFERRED_NETWORK_METADATA.id, - enabled: Boolean(debouncedTokenId) && token?.isNativeToken, + const { isLoading, error, isApproved, balance, approve, depositAssets } = + useDepositAssets({ + token, + pool, + amount: amount ?? undefined, }); - const { sendTransactionAsync } = useSendTransaction(configNative); - // State for loading symbol during transaction const [isSendingTransaction, setIsSendingTransaction] = useState(false); + // State for loading symbol during approval + const [isSendingApproval, setIsSendingApproval] = useState(false); + // OnSubmit: First validate data, then send the transaction const onSubmit = (data: DepositAssetsData) => { //Can only send known tokens @@ -152,7 +140,7 @@ export const DepositAssets = () => { if (token === undefined) { setError('root.deposit', { type: 'custom', - message: 'You cannnot deposit this type of token', + message: 'You can not deposit this type of token', }); return; } @@ -173,56 +161,41 @@ export const DepositAssets = () => { return; } + if (pool !== 'General' && watchToken !== 'SECOIN') { + setError('root.deposit', { + type: 'custom', + message: `Only ${'SECOIN'} can be send to the ${pool} pool`, + }); + return; + } + + if (isLoading) { + setError('root.deposit', { + type: 'custom', + message: 'Loading', + }); + return; + } + + if (error !== null) { + setError('root.deposit', { + type: 'custom', + message: error, + }); + return; + } + const toasterConfig: ContractTransactionToast = { success: 'Deposit successful!', error: 'Deposit failed', onFinish: () => { setIsSendingTransaction(false); + setValue('amount', '0'); }, }; - if (token.isNativeToken) { - if (errorNative !== null) { - setError('root.deposit', { - type: 'custom', - message: 'Could not create transaction', - }); - console.error(error); - return; - } - - if (!sendTransactionAsync) { - setError('root.deposit', { - type: 'custom', - message: 'Could not create transaction', - }); - return; - } - - // send transaction (Note that the toast will set the loading state to false) - setIsSendingTransaction(true); - toast.contractTransaction(() => sendTransactionAsync(), toasterConfig); - } else { - if (error !== null) { - setError('root.deposit', { - type: 'custom', - message: 'Could not create transaction', - }); - console.error(error); - return; - } - - if (!writeAsync) { - setError('root.deposit', { - type: 'custom', - message: 'Could not create transaction', - }); - return; - } - // send transaction (Note that the toast will set the loading state to false) - setIsSendingTransaction(true); - toast.contractTransaction(() => writeAsync(), toasterConfig); - } + setIsSendingTransaction(true); + toast.contractTransaction(() => depositAssets(), toasterConfig); }; return ( @@ -236,69 +209,149 @@ export const DepositAssets = () => { className="text-lg" /> -
Deposit assets
+
Deposit
-
- - - ( - - )} - /> - +
+
+ + + ( + + )} + /> + +
+
+ + + ( + + )} + /> + +
{watchToken !== 'Other' && ( - { - // only Validate if this is active - if (!isKnownToken) return true; + <> + +
+ { + // only Validate if this is active + if (!isKnownToken) return true; - // Required - if (v === undefined || v === '') - return 'Please enter an amount'; + // Required + if (v === undefined || v === '') + return 'Please enter an amount'; - // Number Pattern - if (!NumberPattern.test(v)) - return 'Please enter a number, e.g. 3.141'; + // Number Pattern + if (!NumberPattern.test(v)) + return 'Please enter a number, e.g. 3.141'; - // Otherwise this is valid - return true; - }, - })} - id="amount" - tooltip={`Amount of ${watchToken} to deposit`} - label="Amount" - error={errors.amount} - disabled={!isKnownToken || isSendingTransaction} - /> + // Otherwise this is valid + return true; + }, + })} + id="amount" + className="border-none text-2xl [&::-webkit-inner-spin-button]:appearance-none [&::-webkit-outer-spin-button]:appearance-none [appearance:textfield] focus:ring-0 focus:ring-offset-0" + placeholder={'0.0'} + error={errors.amount} + disabled={!isKnownToken || isSendingTransaction} + /> + {balance !== undefined && ( +
+
+ {balance.symbol} +
+ { + setValue( + 'amount', + formatUnits(balance.value, balance.decimals) + ); + }} + /> +
+ )} +
+ )}
{watchToken === 'Other' ? ( @@ -316,9 +369,27 @@ export const DepositAssets = () => { ) : (
+ {!isApproved && ( +
); }; - -function useDebounce(value: T, delay?: number): T { - const [debouncedValue, setDebouncedValue] = useState(value); - - useEffect(() => { - const timer = setTimeout(() => setDebouncedValue(value), delay || 500); - - return () => { - clearTimeout(timer); - }; - }, [value, delay]); - - return debouncedValue; -} diff --git a/src/components/icons/Rep.tsx b/src/components/icons/Rep.tsx index c22fa96e..6fc9a933 100644 --- a/src/components/icons/Rep.tsx +++ b/src/components/icons/Rep.tsx @@ -19,9 +19,28 @@ const Rep = ({ className, ...props }: React.BaseHTMLAttributes) => { > - - R - + + + + + ); diff --git a/src/components/layout/UserBalances.tsx b/src/components/layout/UserBalances.tsx index 1108fd80..5563c028 100644 --- a/src/components/layout/UserBalances.tsx +++ b/src/components/layout/UserBalances.tsx @@ -25,9 +25,11 @@ const UserBalances = () => { const { address, isConnected } = useAccount(); const { votingPower, loading: repLoading } = useVotingPower({ address, + watch: true, }); const { secoinBalance, loading: secoinLoading } = useSecoinBalance({ address, + watch: true, }); const showBalances = diff --git a/src/components/newProposal/actions/ChangeParamInput.tsx b/src/components/newProposal/actions/ChangeParamInput.tsx index 11eacff2..3db03a49 100644 --- a/src/components/newProposal/actions/ChangeParamInput.tsx +++ b/src/components/newProposal/actions/ChangeParamInput.tsx @@ -28,6 +28,11 @@ import { SelectTrigger, SelectValue, } from '@/src/components/ui/Select'; +import { + Tooltip, + TooltipContent, + TooltipTrigger, +} from '@/src/components/ui/Tooltip'; import { useDaoVariable } from '@/src/hooks/useDaoVariable'; import { useDaoVariables } from '@/src/hooks/useDaoVariables'; import { @@ -43,8 +48,6 @@ import { indexObject, isNullOrUndefined } from '@/src/lib/utils'; import { Controller, useFormContext, useWatch } from 'react-hook-form'; import { HiCog, HiQuestionMarkCircle, HiXMark } from 'react-icons/hi2'; -import { Tooltip, TooltipContent, TooltipTrigger } from '../../ui/Tooltip'; - export interface ProposalFormChangeParamData { name: 'change_param'; plugin: string; @@ -259,6 +262,7 @@ export const ChangeParamInput = () => { diff --git a/src/components/newProposal/actions/WhitelistMemberInput.tsx b/src/components/newProposal/actions/WhitelistMemberInput.tsx index 5e36a0d9..9ef70766 100644 --- a/src/components/newProposal/actions/WhitelistMemberInput.tsx +++ b/src/components/newProposal/actions/WhitelistMemberInput.tsx @@ -7,21 +7,20 @@ */ import { useContext } from 'react'; -import { ACTIONS } from '@/src/lib/constants/actions'; -import { AddressPattern } from '@/src/lib/constants/patterns'; -import { useFormContext } from 'react-hook-form'; -import { HiXMark } from 'react-icons/hi2'; - -import { Button } from '../../ui/Button'; -import { ErrorWrapper } from '../../ui/ErrorWrapper'; -import { Input } from '../../ui/Input'; -import { Label } from '../../ui/Label'; -import { MainCard } from '../../ui/MainCard'; import { ActionFormContext, ActionFormError, ProposalFormActions, -} from '../steps/Actions'; +} from '@/src/components/newProposal/steps/Actions'; +import { Button } from '@/src/components/ui/Button'; +import { ErrorWrapper } from '@/src/components/ui/ErrorWrapper'; +import { Input } from '@/src/components/ui/Input'; +import { Label } from '@/src/components/ui/Label'; +import { MainCard } from '@/src/components/ui/MainCard'; +import { ACTIONS } from '@/src/lib/constants/actions'; +import { AddressPattern } from '@/src/lib/constants/patterns'; +import { useFormContext } from 'react-hook-form'; +import { HiXMark } from 'react-icons/hi2'; export interface ProposalFormWhitelistData { name: 'whitelist_member'; diff --git a/src/components/newProposal/actions/WithdrawAssetsInput.tsx b/src/components/newProposal/actions/WithdrawAssetsInput.tsx index 6261a869..d9ff34af 100644 --- a/src/components/newProposal/actions/WithdrawAssetsInput.tsx +++ b/src/components/newProposal/actions/WithdrawAssetsInput.tsx @@ -29,18 +29,17 @@ import { import TokenAmount from '@/src/components/ui/TokenAmount'; import { useDiamondSDKContext } from '@/src/context/DiamondGovernanceSDK'; import { useDaoBalance } from '@/src/hooks/useDaoBalance'; +import { useTokenFetch } from '@/src/hooks/useTokenFetch'; import { PREFERRED_NETWORK_METADATA } from '@/src/lib/constants/chains'; import { AddressPattern, IntegerPattern, NumberPattern, } from '@/src/lib/constants/patterns'; +import { TokenType } from '@/src/lib/constants/tokens'; import { anyNullOrUndefined, assertUnreachable, cn } from '@/src/lib/utils'; -import { getTokenInfo } from '@/src/lib/utils/token'; -import { TokenType } from '@aragon/sdk-client'; import { Controller, useFormContext, useWatch } from 'react-hook-form'; import { HiBanknotes, HiXMark } from 'react-icons/hi2'; -import { useProvider } from 'wagmi'; export interface ProposalFormWithdrawData { name: 'withdraw_assets'; @@ -71,7 +70,7 @@ type TokenTypeInfo = { const tokenTypesInfo: TokenTypeInfo[] = [ { type: TokenType.NATIVE, - displayName: `Native token: ${PREFERRED_NETWORK_METADATA.nativeCurrency.name}`, + displayName: `Native token: ${PREFERRED_NETWORK_METADATA.nativeToken.name}`, }, { type: TokenType.ERC20, @@ -95,8 +94,6 @@ export const WithdrawAssetsInput = () => { getValues, } = useFormContext(); - const provider = useProvider(); - const { prefix, index, onRemove } = useContext(ActionFormContext); const errors: ActionFormError = formErrors.actions @@ -108,12 +105,12 @@ export const WithdrawAssetsInput = () => { error || loading || !daoBalances ? [] : daoBalances.filter( - (token) => + (bal) => !anyNullOrUndefined( - token.name, - token.symbol, - token.address, - token.balance + bal.token?.name, + bal.token?.symbol, + bal.token?.address, + bal.balance ) ); @@ -126,6 +123,7 @@ export const WithdrawAssetsInput = () => { const address = useWatch({ control, name: `${prefix}.tokenAddress` }); const tokenType = useWatch({ control, name: `${prefix}.tokenType` }); + const { getTokenInfo } = useTokenFetch(); const [isManualDecimalEntry, setIsManualDecimalEntry] = useState(false); @@ -135,9 +133,7 @@ export const WithdrawAssetsInput = () => { setValue(`${prefix}.tokenDecimals`, decimalsLoadingText); const tokenInfo = await getTokenInfo( - getValues(`${prefix}.tokenAddressCustom`), - provider, - PREFERRED_NETWORK_METADATA.nativeCurrency + getValues(`${prefix}.tokenAddressCustom`) ); if (tokenInfo?.decimals) { setValue(`${prefix}.tokenDecimals`, tokenInfo.decimals.toString()); @@ -204,10 +200,12 @@ export const WithdrawAssetsInput = () => { +