From 2a620f10a87665cfa90564271afe764f88d1b979 Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 21 Sep 2023 16:03:14 -0300 Subject: [PATCH 01/13] =?UTF-8?q?=F0=9F=8F=B7=EF=B8=8F=20add=20DelegateReg?= =?UTF-8?q?istry=20abi?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- abi/DelegateRegistry.json | 107 ++++++++++++++++++++++++++++++++++++++ wagmi.config.ts | 2 + 2 files changed, 109 insertions(+) create mode 100644 abi/DelegateRegistry.json diff --git a/abi/DelegateRegistry.json b/abi/DelegateRegistry.json new file mode 100644 index 000000000..f6414a8fd --- /dev/null +++ b/abi/DelegateRegistry.json @@ -0,0 +1,107 @@ +[ + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + } + ], + "name": "ClearDelegate", + "type": "event" + }, + { + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "delegator", + "type": "address" + }, + { + "indexed": true, + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "indexed": true, + "internalType": "address", + "name": "delegate", + "type": "address" + } + ], + "name": "SetDelegate", + "type": "event" + }, + { + "inputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + }, + { + "internalType": "bytes32", + "name": "", + "type": "bytes32" + } + ], + "name": "delegation", + "outputs": [ + { + "internalType": "address", + "name": "", + "type": "address" + } + ], + "stateMutability": "view", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + }, + { + "internalType": "address", + "name": "delegate", + "type": "address" + } + ], + "name": "setDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + }, + { + "inputs": [ + { + "internalType": "bytes32", + "name": "id", + "type": "bytes32" + } + ], + "name": "clearDelegate", + "outputs": [], + "stateMutability": "nonpayable", + "type": "function" + } +] diff --git a/wagmi.config.ts b/wagmi.config.ts index 8c827e399..499637c26 100644 --- a/wagmi.config.ts +++ b/wagmi.config.ts @@ -21,6 +21,7 @@ import InterestRateModel from '@exactly/protocol/deployments/goerli/InterestRate import SablierV2LockupLinear from '@exactly/protocol/deployments/goerli/SablierV2LockupLinear.json' assert { type: 'json' }; import SablierV2NFTDescriptor from '@exactly/protocol/deployments/goerli/SablierV2NFTDescriptor.json' assert { type: 'json' }; import ExtraFinanceLendingABI from './abi/extraFinanceLending.json' assert { type: 'json' }; +import DelegateRegistryABI from './abi/DelegateRegistry.json' assert { type: 'json' }; import { Abi } from 'viem'; @@ -47,6 +48,7 @@ export default defineConfig({ { name: 'SablierV2LockupLinear', abi: SablierV2LockupLinear.abi as Abi }, { name: 'SablierV2NFTDescriptor', abi: SablierV2NFTDescriptor.abi as Abi }, { name: 'ExtraFinanceLending', abi: ExtraFinanceLendingABI as Abi }, + { name: 'DelegateRegistry', abi: DelegateRegistryABI as Abi }, ], plugins: [ react({ From b4623ab4686821990e6b7b3694ecc4c8fbc851f3 Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 21 Sep 2023 20:44:33 -0300 Subject: [PATCH 02/13] =?UTF-8?q?=E2=9E=95=20add=20@snapshot-labs/snapshot?= =?UTF-8?q?.js?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- package-lock.json | 227 +++++++++++++++++++++------------------------- package.json | 1 + 2 files changed, 105 insertions(+), 123 deletions(-) diff --git a/package-lock.json b/package-lock.json index a9ead75da..d613fc894 100644 --- a/package-lock.json +++ b/package-lock.json @@ -18,6 +18,7 @@ "@mui/material": "^5.14.3", "@sentry/integrations": "^7.61.1", "@sentry/nextjs": "^7.61.1", + "@snapshot-labs/snapshot.js": "^0.6.1", "@socket.tech/plugin": "^1.2.1", "@upstash/ratelimit": "^0.4.4", "@vercel/kv": "^0.2.2", @@ -359,6 +360,11 @@ "version": "0.3.1", "license": "MIT" }, + "node_modules/@ensdomains/eth-ens-namehash": { + "version": "2.0.15", + "resolved": "https://registry.npmjs.org/@ensdomains/eth-ens-namehash/-/eth-ens-namehash-2.0.15.tgz", + "integrity": "sha512-JRDFP6+Hczb1E0/HhIg0PONgBYasfGfDheujmfxaZaAv/NAH4jE6Kf48WbqfRZdxt4IZI3jl3Ri7sZ1nP09lgw==" + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.4.0", "dev": true, @@ -2337,6 +2343,50 @@ "mkdirp": "bin/cmd.js" } }, + "node_modules/@snapshot-labs/snapshot.js": { + "version": "0.6.1", + "resolved": "https://registry.npmjs.org/@snapshot-labs/snapshot.js/-/snapshot.js-0.6.1.tgz", + "integrity": "sha512-kIJJPE7N7izOxZM5FvrHrAdH3DWVa3p1d2w3P1TqrBDI6129pq4YyHXaFVFu3shWSAm4z243aVRDHwYgQbkkbQ==", + "dependencies": { + "@ensdomains/eth-ens-namehash": "^2.0.15", + "@ethersproject/abi": "^5.6.4", + "@ethersproject/address": "^5.6.1", + "@ethersproject/bytes": "^5.6.1", + "@ethersproject/contracts": "^5.6.2", + "@ethersproject/hash": "^5.7.0", + "@ethersproject/providers": "^5.6.8", + "@ethersproject/units": "^5.7.0", + "@ethersproject/wallet": "^5.6.2", + "ajv": "^8.11.0", + "ajv-formats": "^2.1.1", + "cross-fetch": "^3.1.6", + "json-to-graphql-query": "^2.2.4", + "lodash.set": "^4.3.2" + }, + "engines": { + "node": ">=14" + } + }, + "node_modules/@snapshot-labs/snapshot.js/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/@snapshot-labs/snapshot.js/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/@socket.tech/ll-core": { "version": "0.1.47", "resolved": "https://registry.npmjs.org/@socket.tech/ll-core/-/ll-core-0.1.47.tgz", @@ -4496,6 +4546,42 @@ "url": "https://github.com/sponsors/epoberezkin" } }, + "node_modules/ajv-formats": { + "version": "2.1.1", + "resolved": "https://registry.npmjs.org/ajv-formats/-/ajv-formats-2.1.1.tgz", + "integrity": "sha512-Wx0Kx52hxE7C18hkMEggYlEifqWZtYaRgouJor+WMdPnQyEK13vgEWyVNup7SoeeoLMsr4kf5h6dOW11I15MUA==", + "dependencies": { + "ajv": "^8.0.0" + }, + "peerDependencies": { + "ajv": "^8.0.0" + }, + "peerDependenciesMeta": { + "ajv": { + "optional": true + } + } + }, + "node_modules/ajv-formats/node_modules/ajv": { + "version": "8.12.0", + "resolved": "https://registry.npmjs.org/ajv/-/ajv-8.12.0.tgz", + "integrity": "sha512-sRu1kpcO9yLtYxBKvqfTeh9KzZEwO3STyX1HT+4CaDzC6HpTGYhIhPIzj9XuKU7KYDwnaeh5hcOwjy1QuJzBPA==", + "dependencies": { + "fast-deep-equal": "^3.1.1", + "json-schema-traverse": "^1.0.0", + "require-from-string": "^2.0.2", + "uri-js": "^4.2.2" + }, + "funding": { + "type": "github", + "url": "https://github.com/sponsors/epoberezkin" + } + }, + "node_modules/ajv-formats/node_modules/json-schema-traverse": { + "version": "1.0.0", + "resolved": "https://registry.npmjs.org/json-schema-traverse/-/json-schema-traverse-1.0.0.tgz", + "integrity": "sha512-NM8/P9n3XjXhIZn1lLhkFaACTOURQXjWhV4BA/RnOv8xvgqtqpAX9IO4mRQxSx1Rlo4tqzeqb0sOlruaOy3dug==" + }, "node_modules/ajv-keywords": { "version": "3.5.2", "devOptional": true, @@ -7742,7 +7828,6 @@ }, "node_modules/fast-deep-equal": { "version": "3.1.3", - "devOptional": true, "license": "MIT" }, "node_modules/fast-diff": { @@ -9291,6 +9376,11 @@ "version": "5.0.1", "license": "ISC" }, + "node_modules/json-to-graphql-query": { + "version": "2.2.5", + "resolved": "https://registry.npmjs.org/json-to-graphql-query/-/json-to-graphql-query-2.2.5.tgz", + "integrity": "sha512-5Nom9inkIMrtY992LMBBG1Zaekrc10JaRhyZgprwHBVMDtRgllTvzl0oBbg13wJsVZoSoFNNMaeIVQs0P04vsA==" + }, "node_modules/json5": { "version": "2.2.3", "dev": true, @@ -9488,6 +9578,11 @@ "dev": true, "license": "MIT" }, + "node_modules/lodash.set": { + "version": "4.3.2", + "resolved": "https://registry.npmjs.org/lodash.set/-/lodash.set-4.3.2.tgz", + "integrity": "sha512-4hNPN5jlm/N/HLMCO43v8BXKq9Z7QdAGc/VGrRD61w8gN9g/6jF9A4L1pbUgBLCffi0w9VsXfTOij5x8iTyFvg==" + }, "node_modules/log-symbols": { "version": "4.1.0", "dev": true, @@ -10901,7 +10996,6 @@ }, "node_modules/punycode": { "version": "2.3.0", - "devOptional": true, "license": "MIT", "engines": { "node": ">=6" @@ -11458,6 +11552,14 @@ "node": ">=0.10.0" } }, + "node_modules/require-from-string": { + "version": "2.0.2", + "resolved": "https://registry.npmjs.org/require-from-string/-/require-from-string-2.0.2.tgz", + "integrity": "sha512-Xf0nWe6RseziFMu+Ap9biiUbmplq6S9/p+7w7YXP/JBHhrUDDUhwa+vANyubuqfZWTveU//DYVGsDG7RKL/vEw==", + "engines": { + "node": ">=0.10.0" + } + }, "node_modules/require-main-filename": { "version": "2.0.0", "license": "ISC" @@ -13677,7 +13779,6 @@ }, "node_modules/uri-js": { "version": "4.4.1", - "devOptional": true, "license": "BSD-2-Clause", "dependencies": { "punycode": "^2.1.0" @@ -14532,126 +14633,6 @@ "optional": true } } - }, - "node_modules/@next/swc-darwin-x64": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.12.tgz", - "integrity": "sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "darwin" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-gnu": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.12.tgz", - "integrity": "sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-arm64-musl": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.12.tgz", - "integrity": "sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-gnu": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.12.tgz", - "integrity": "sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-linux-x64-musl": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.12.tgz", - "integrity": "sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "linux" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-arm64-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.12.tgz", - "integrity": "sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==", - "cpu": [ - "arm64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-ia32-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.12.tgz", - "integrity": "sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==", - "cpu": [ - "ia32" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } - }, - "node_modules/@next/swc-win32-x64-msvc": { - "version": "13.4.12", - "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.12.tgz", - "integrity": "sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==", - "cpu": [ - "x64" - ], - "optional": true, - "os": [ - "win32" - ], - "engines": { - "node": ">= 10" - } } } } diff --git a/package.json b/package.json index 12860c323..25de3a8e7 100644 --- a/package.json +++ b/package.json @@ -29,6 +29,7 @@ "@mui/material": "^5.14.3", "@sentry/integrations": "^7.61.1", "@sentry/nextjs": "^7.61.1", + "@snapshot-labs/snapshot.js": "^0.6.1", "@socket.tech/plugin": "^1.2.1", "@upstash/ratelimit": "^0.4.4", "@vercel/kv": "^0.2.2", From 709a12a59061aa6bcd290f094a876d2d38171725 Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 21 Sep 2023 21:02:12 -0300 Subject: [PATCH 03/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20change?= =?UTF-8?q?=20proposals=20section?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Proposals/index.tsx | 30 +++--- i18n/es/translation.json | 6 +- package-lock.json | 120 ++++++++++++++++++++++ 3 files changed, 137 insertions(+), 19 deletions(-) diff --git a/components/governance/Proposals/index.tsx b/components/governance/Proposals/index.tsx index 8bd1eae02..74ed1b0c1 100644 --- a/components/governance/Proposals/index.tsx +++ b/components/governance/Proposals/index.tsx @@ -1,6 +1,7 @@ import React from 'react'; -import { Box, Divider, Typography } from '@mui/material'; +import { Box, Button, Divider, Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; +import OpenInNewIcon from '@mui/icons-material/OpenInNew'; const Proposals = () => { const { t } = useTranslation(); @@ -9,23 +10,20 @@ const Proposals = () => { - + {t('Proposals')} - - {t('Coming soon')} - + + {t('See our proposals on the governance forum via Snapshot.')} + + {t("You can also create your own proposals and vote on other's proposals.")} + + - - {t( - "Stay tuned to our Discord and Twitter for updates, and get ready to vote and shape the protocol's evolution.", - )} - + + + ); diff --git a/i18n/es/translation.json b/i18n/es/translation.json index e76a7e89d..20f5df019 100644 --- a/i18n/es/translation.json +++ b/i18n/es/translation.json @@ -311,8 +311,6 @@ "Enter delegate address": "Ingresa la dirección del delegado", "Delegate Votes to": "Delegar Votos a", "Proposals": "Propuestas", - "Coming soon": "Próximamente", - "Stay tuned to our Discord and Twitter for updates, and get ready to vote and shape the protocol's evolution.": "Mantente atento a nuestro Discord y Twitter para actualizaciones, y prepárate para votar y dar forma a la evolución del protocolo.", "Expired": "Expirado", "Processing transaction...": "Procesando transacción...", "Account Health Factor": "Factor de Salud de la cuenta", @@ -481,5 +479,7 @@ "Exactly": "Exactly", "Third-Party": "Terceros", "Security": "Seguridad", - "The DebtManager contract is responsible for the leverage, deleverage, and rollover functionality of the protocol.": "El contrato DebtManager es responsable de la funcionalidad de apalancamiento, desapalancamiento y refinanciamiento del protocolo." + "The DebtManager contract is responsible for the leverage, deleverage, and rollover functionality of the protocol.": "El contrato DebtManager es responsable de la funcionalidad de apalancamiento, desapalancamiento y refinanciamiento del protocolo.", + "See our proposals on the governance forum via Snapshot.": "Mira nuestras propuestas en el foro de gobernanza a través de Snapshot.", + "You can also create your own proposals and vote on other's proposals.": "También puedes crear tus propias propuestas y votar en las propuestas de otros." } diff --git a/package-lock.json b/package-lock.json index d613fc894..1fd0950e1 100644 --- a/package-lock.json +++ b/package-lock.json @@ -14633,6 +14633,126 @@ "optional": true } } + }, + "node_modules/@next/swc-darwin-x64": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-darwin-x64/-/swc-darwin-x64-13.4.12.tgz", + "integrity": "sha512-WRvH7RxgRHlC1yb5oG0ZLx8F7uci9AivM5/HGGv9ZyG2Als8Ij64GC3d+mQ5sJhWjusyU6T6V1WKTUoTmOB0zQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "darwin" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-gnu": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-gnu/-/swc-linux-arm64-gnu-13.4.12.tgz", + "integrity": "sha512-YEKracAWuxp54tKiAvvq73PUs9lok57cc8meYRibTWe/VdPB2vLgkTVWFcw31YDuRXdEhdX0fWS6Q+ESBhnEig==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-arm64-musl": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-arm64-musl/-/swc-linux-arm64-musl-13.4.12.tgz", + "integrity": "sha512-LhJR7/RAjdHJ2Isl2pgc/JaoxNk0KtBgkVpiDJPVExVWA1c6gzY57+3zWuxuyWzTG+fhLZo2Y80pLXgIJv7g3g==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-gnu": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-gnu/-/swc-linux-x64-gnu-13.4.12.tgz", + "integrity": "sha512-1DWLL/B9nBNiQRng+1aqs3OaZcxC16Nf+mOnpcrZZSdyKHek3WQh6j/fkbukObgNGwmCoVevLUa/p3UFTTqgqg==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-linux-x64-musl": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-linux-x64-musl/-/swc-linux-x64-musl-13.4.12.tgz", + "integrity": "sha512-kEAJmgYFhp0VL+eRWmUkVxLVunn7oL9Mdue/FS8yzRBVj7Z0AnIrHpTIeIUl1bbdQq1VaoOztnKicAjfkLTRCQ==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "linux" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-arm64-msvc": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-arm64-msvc/-/swc-win32-arm64-msvc-13.4.12.tgz", + "integrity": "sha512-GMLuL/loR6yIIRTnPRY6UGbLL9MBdw2anxkOnANxvLvsml4F0HNIgvnU3Ej4BjbqMTNjD4hcPFdlEow4XHPdZA==", + "cpu": [ + "arm64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-ia32-msvc": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-ia32-msvc/-/swc-win32-ia32-msvc-13.4.12.tgz", + "integrity": "sha512-PhgNqN2Vnkm7XaMdRmmX0ZSwZXQAtamBVSa9A/V1dfKQCV1rjIZeiy/dbBnVYGdj63ANfsOR/30XpxP71W0eww==", + "cpu": [ + "ia32" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } + }, + "node_modules/@next/swc-win32-x64-msvc": { + "version": "13.4.12", + "resolved": "https://registry.npmjs.org/@next/swc-win32-x64-msvc/-/swc-win32-x64-msvc-13.4.12.tgz", + "integrity": "sha512-Z+56e/Ljt0bUs+T+jPjhFyxYBcdY2RIq9ELFU+qAMQMteHo7ymbV7CKmlcX59RI9C4YzN8PgMgLyAoi916b5HA==", + "cpu": [ + "x64" + ], + "optional": true, + "os": [ + "win32" + ], + "engines": { + "node": ">= 10" + } } } } From 8967ce2b91c90860facbda4331b5155d54b2fdb1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 21 Sep 2023 23:01:38 -0300 Subject: [PATCH 04/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20get=20?= =?UTF-8?q?vp=20with=20snapshot=20api?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Claimable/index.tsx | 2 +- components/governance/Delegation/index.tsx | 2 +- components/governance/VotingPower/index.tsx | 28 +++--------- hooks/useGovernance.ts | 49 +++++++++++++++++++++ pages/governance.tsx | 4 +- 5 files changed, 61 insertions(+), 24 deletions(-) create mode 100644 hooks/useGovernance.ts diff --git a/components/governance/Claimable/index.tsx b/components/governance/Claimable/index.tsx index b21eb2840..d48a51b5a 100644 --- a/components/governance/Claimable/index.tsx +++ b/components/governance/Claimable/index.tsx @@ -167,4 +167,4 @@ const NFT: FC = () => { ); }; -export default Claimable; +export default React.memo(Claimable); diff --git a/components/governance/Delegation/index.tsx b/components/governance/Delegation/index.tsx index 4a1671fae..0575a4d71 100644 --- a/components/governance/Delegation/index.tsx +++ b/components/governance/Delegation/index.tsx @@ -254,4 +254,4 @@ const Delegation = ({ amount }: Props) => { ); }; -export default Delegation; +export default React.memo(Delegation); diff --git a/components/governance/VotingPower/index.tsx b/components/governance/VotingPower/index.tsx index 1030572ba..d51636e09 100644 --- a/components/governance/VotingPower/index.tsx +++ b/components/governance/VotingPower/index.tsx @@ -1,42 +1,28 @@ -import React, { useMemo } from 'react'; +import React, { FC } from 'react'; import { Box, Skeleton, Typography } from '@mui/material'; -import { formatEther } from 'viem'; import { useTranslation } from 'react-i18next'; -import { useEXAGetVotes, useEXADelegates } from 'hooks/useEXA'; import formatNumber from 'utils/formatNumber'; -import { useWeb3 } from 'hooks/useWeb3'; -import { useAirdropStreams } from 'hooks/useAirdrop'; -import { useSablierV2LockupLinearGetWithdrawnAmount } from 'hooks/useSablier'; type Props = { - amount: bigint; + votingPower?: number; }; -const VotingPower = ({ amount }: Props) => { +const VotingPower: FC = ({ votingPower }) => { const { t } = useTranslation(); - const { walletAddress } = useWeb3(); - const { data: votes, isLoading: isLoadingGetVotes } = useEXAGetVotes(); - const { data: stream } = useAirdropStreams(); - const { data: delegatee, isLoading: isLoadingDelegatee } = useEXADelegates(); - const { data: withdrawn, isLoading: isLoadingWithdrawn } = useSablierV2LockupLinearGetWithdrawnAmount(stream); - - const totalVotes = useMemo(() => { - return (votes ?? 0n) + (delegatee === walletAddress ? amount - (withdrawn ?? 0n) : 0n); - }, [votes, amount, walletAddress, delegatee, withdrawn]); return ( {t('Voting Power')} - {votes === undefined || isLoadingDelegatee || isLoadingWithdrawn || isLoadingGetVotes ? ( + {votingPower === undefined ? ( ) : ( - {formatNumber(formatEther(totalVotes))} + {formatNumber(votingPower)} )} - {votes === 0n && ( + {votingPower === 0 && ( {t('You have no voting power in your connected wallet.')} @@ -45,4 +31,4 @@ const VotingPower = ({ amount }: Props) => { ); }; -export default VotingPower; +export default React.memo(VotingPower); diff --git a/hooks/useGovernance.ts b/hooks/useGovernance.ts new file mode 100644 index 000000000..708323ad0 --- /dev/null +++ b/hooks/useGovernance.ts @@ -0,0 +1,49 @@ +import snapshot from '@snapshot-labs/snapshot.js'; +import { useWeb3 } from './useWeb3'; +import { useEffect, useMemo, useState } from 'react'; +import optimismEXA from '@exactly/protocol/deployments/optimism/EXA.json'; +import goerliEXA from '@exactly/protocol/deployments/goerli/EXA.json'; + +export default function useGovernance() { + const [votingPower, setVotingPower] = useState(undefined); + const { chain, walletAddress } = useWeb3(); + + const exaAddress = useMemo(() => (chain.id === 10 ? optimismEXA.address : goerliEXA.address), [chain.id]); + const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + + const strategies = useMemo( + () => [ + { + name: 'erc20-balance-of', + params: { + symbol: 'EXA', + address: exaAddress, + decimals: 18, + }, + }, + { + name: 'sablier-v2', + params: { + policy: 'reserved-recipient', + symbol: 'EXA', + address: exaAddress, + decimals: 18, + }, + }, + ], + [exaAddress], + ); + + const delegation = true; + const url = 'https://score.snapshot.org/'; + + useEffect(() => { + if (!walletAddress) return; + + snapshot.utils + .getVp(walletAddress, String(chain.id), strategies, 'latest', space, delegation, { url }) + .then(({ vp }) => setVotingPower(vp)); + }, [chain.id, chain.network, delegation, space, strategies, url, votingPower, walletAddress]); + + return { votingPower }; +} diff --git a/pages/governance.tsx b/pages/governance.tsx index 40596dc53..611844159 100644 --- a/pages/governance.tsx +++ b/pages/governance.tsx @@ -5,6 +5,7 @@ import { usePageView } from 'hooks/useAnalytics'; import { Box, Typography } from '@mui/material'; import { useTranslation, Trans } from 'react-i18next'; import { useWeb3 } from 'hooks/useWeb3'; +import useGovernance from 'hooks/useGovernance'; import ConnectWalletGovernance from 'components/governance/ConnectWalletGovernance'; import Claimable from 'components/governance/Claimable'; import Delegation from 'components/governance/Delegation'; @@ -15,6 +16,7 @@ import useMerkleTree from 'hooks/useMerkleTree'; const Governance: NextPage = () => { const { t } = useTranslation(); const { isConnected, walletAddress, impersonateActive } = useWeb3(); + const { votingPower } = useGovernance(); const mTree = useMerkleTree(walletAddress); usePageView('/governance', 'Governance'); @@ -44,7 +46,7 @@ const Governance: NextPage = () => { bgcolor={({ palette }) => (palette.mode === 'dark' ? 'grey.100' : 'white')} > {mTree.canClaim && } - + From 8c3c0bf3d3357f52ffca06fc6d959792999dfe7b Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Fri, 22 Sep 2023 00:00:32 -0300 Subject: [PATCH 05/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20change?= =?UTF-8?q?=20delegation=20component=20to=20use=20delegate=20registry?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Delegation/index.tsx | 69 +++++++++++---------- components/governance/VotingPower/index.tsx | 2 +- hooks/useDelegateRegistry.ts | 48 ++++++++++++++ hooks/useGovernance.ts | 30 ++++++--- pages/governance.tsx | 4 +- 5 files changed, 109 insertions(+), 44 deletions(-) create mode 100644 hooks/useDelegateRegistry.ts diff --git a/components/governance/Delegation/index.tsx b/components/governance/Delegation/index.tsx index 0575a4d71..ce15c53e5 100644 --- a/components/governance/Delegation/index.tsx +++ b/components/governance/Delegation/index.tsx @@ -1,46 +1,53 @@ -import React, { useMemo, useState } from 'react'; +import React, { FC, useMemo, useState } from 'react'; import { Avatar, Box, Button, Collapse, Divider, Skeleton, TextField, Typography } from '@mui/material'; import { Trans, useTranslation } from 'react-i18next'; import { LoadingButton } from '@mui/lab'; import HowToVoteIcon from '@mui/icons-material/HowToVote'; import AddIcon from '@mui/icons-material/Add'; -import { isAddress, parseEther, zeroAddress, formatEther } from 'viem'; +import { isAddress, zeroAddress } from 'viem'; import { formatWallet } from 'utils/utils'; -import { useEXA, useEXADelegates, useEXAPrepareDelegate } from 'hooks/useEXA'; -import formatNumber from 'utils/formatNumber'; +import { useEXA } from 'hooks/useEXA'; import useBalance from 'hooks/useBalance'; import { useWeb3 } from 'hooks/useWeb3'; -import { useExaDelegate } from 'types/abi'; +import { useDelegateRegistryClearDelegate, useDelegateRegistrySetDelegate } from 'types/abi'; import { mainnet, useEnsAvatar, useEnsName, useNetwork, useSwitchNetwork, useWaitForTransaction } from 'wagmi'; import * as blockies from 'blockies-ts'; -import { useAirdropStreams } from 'hooks/useAirdrop'; -import { useSablierV2LockupLinearGetWithdrawnAmount } from 'hooks/useSablier'; +import { useDelegation, usePrepareClearDelegate, usePrepareDelegate } from 'hooks/useDelegateRegistry'; +import formatNumber from 'utils/formatNumber'; +import useGovernance from 'hooks/useGovernance'; type Props = { - amount: bigint; + fetchVotingPower: () => void; }; -const Delegation = ({ amount }: Props) => { +const Delegation: FC = ({ fetchVotingPower }) => { + const { votingPower } = useGovernance(false); const { t } = useTranslation(); const { chain: displayNetwork, walletAddress, impersonateActive, exitImpersonate } = useWeb3(); const [selected, setSelected] = useState<'self-delegate' | 'add-delegate'>(); const [input, setInput] = useState(''); const exa = useEXA(); const exaBalance = useBalance('EXA', exa?.address); - const { data: delegate, isLoading: isLoadingDelegate, refetch: refetchDelegate } = useEXADelegates(); - const { config } = useEXAPrepareDelegate({ - args: - delegate !== zeroAddress - ? [zeroAddress] - : selected === 'add-delegate' && isAddress(input) - ? [input] - : [walletAddress ?? zeroAddress], - }); - const { write, isLoading: submitLoading, data } = useExaDelegate(config); + const { data: delegate, isLoading: isLoadingDelegate, refetch: refetchDelegate } = useDelegation(); + const { config } = usePrepareDelegate( + delegate !== zeroAddress + ? zeroAddress + : selected === 'add-delegate' && isAddress(input) + ? input + : walletAddress ?? zeroAddress, + ); + const { write, isLoading: submitLoading, data } = useDelegateRegistrySetDelegate(config); + const { config: configClearDelegate } = usePrepareClearDelegate(); + const { + write: writeClearDelegate, + isLoading: clearDelegateLoading, + data: clearDelegateData, + } = useDelegateRegistryClearDelegate(configClearDelegate); const { isLoading: waitingDelegate } = useWaitForTransaction({ - hash: data?.hash, + hash: data?.hash ?? clearDelegateData?.hash, onSettled: () => { refetchDelegate(); + fetchVotingPower(); }, }); const { data: delegateENS } = useEnsName({ address: delegate, chainId: mainnet.id }); @@ -52,13 +59,6 @@ const Delegation = ({ amount }: Props) => { const { chain } = useNetwork(); const { switchNetwork, isLoading: switchIsLoading } = useSwitchNetwork(); - const { data: stream } = useAirdropStreams(); - const { data: withdrawn } = useSablierV2LockupLinearGetWithdrawnAmount(stream); - - const totalVotes = useMemo(() => { - return formatNumber(formatEther(parseEther(exaBalance ?? '0') + (amount - (withdrawn ?? 0n)))); - }, [exaBalance, amount, withdrawn]); - const delegateAvatar = useMemo(() => { if (!delegate) return ''; if (delegateENSAvatar && !ensAvatarError) return delegateENSAvatar; @@ -81,14 +81,14 @@ const Delegation = ({ amount }: Props) => { {t('Votes Delegation')} - {exaBalance !== undefined ? ( + {exaBalance !== undefined && votingPower !== undefined ? ( , }} - values={{ amount: totalVotes }} + values={{ amount: formatNumber(votingPower, 'USD', true) }} /> ) : ( @@ -111,7 +111,12 @@ const Delegation = ({ amount }: Props) => { {t('Please switch to {{network}} network', { network: displayNetwork.name })} ) : ( - + {t('Revoke delegation')} )} @@ -124,14 +129,14 @@ const Delegation = ({ amount }: Props) => { {t('Votes Delegation')} - {exaBalance !== undefined ? ( + {exaBalance !== undefined && votingPower !== undefined ? ( , }} - values={{ amount: totalVotes }} + values={{ amount: formatNumber(votingPower, 'USD', true) }} /> ) : ( diff --git a/components/governance/VotingPower/index.tsx b/components/governance/VotingPower/index.tsx index d51636e09..b9d1b808a 100644 --- a/components/governance/VotingPower/index.tsx +++ b/components/governance/VotingPower/index.tsx @@ -18,7 +18,7 @@ const VotingPower: FC = ({ votingPower }) => { ) : ( - {formatNumber(votingPower)} + {formatNumber(votingPower, 'USD', true)} )} diff --git a/hooks/useDelegateRegistry.ts b/hooks/useDelegateRegistry.ts new file mode 100644 index 000000000..97083778c --- /dev/null +++ b/hooks/useDelegateRegistry.ts @@ -0,0 +1,48 @@ +import { Address, stringToHex, zeroAddress } from 'viem'; +import { + useDelegateRegistryDelegation, + usePrepareDelegateRegistryClearDelegate, + usePrepareDelegateRegistrySetDelegate, +} from 'types/abi'; +import { useWeb3 } from './useWeb3'; +import { useMemo } from 'react'; + +const DELEGATE_REGISTRY_ADDRESS = '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446'; + +export const useDelegation = () => { + const { chain, walletAddress } = useWeb3(); + const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); + + return useDelegateRegistryDelegation({ + chainId: chain.id, + address: DELEGATE_REGISTRY_ADDRESS, + args: [walletAddress ?? zeroAddress, encodedSpace], + }); +}; + +export const usePrepareDelegate = (address: Address) => { + const { chain, walletAddress } = useWeb3(); + const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); + + return usePrepareDelegateRegistrySetDelegate({ + chainId: chain.id, + address: DELEGATE_REGISTRY_ADDRESS, + account: walletAddress ?? zeroAddress, + args: [encodedSpace, address], + }); +}; + +export const usePrepareClearDelegate = () => { + const { chain, walletAddress } = useWeb3(); + const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); + + return usePrepareDelegateRegistryClearDelegate({ + chainId: chain.id, + address: DELEGATE_REGISTRY_ADDRESS, + account: walletAddress ?? zeroAddress, + args: [encodedSpace], + }); +}; diff --git a/hooks/useGovernance.ts b/hooks/useGovernance.ts index 708323ad0..e7d2f6b34 100644 --- a/hooks/useGovernance.ts +++ b/hooks/useGovernance.ts @@ -1,10 +1,10 @@ import snapshot from '@snapshot-labs/snapshot.js'; import { useWeb3 } from './useWeb3'; -import { useEffect, useMemo, useState } from 'react'; +import { useCallback, useEffect, useMemo, useState } from 'react'; import optimismEXA from '@exactly/protocol/deployments/optimism/EXA.json'; import goerliEXA from '@exactly/protocol/deployments/goerli/EXA.json'; -export default function useGovernance() { +export default function useGovernance(delegation = true) { const [votingPower, setVotingPower] = useState(undefined); const { chain, walletAddress } = useWeb3(); @@ -34,16 +34,28 @@ export default function useGovernance() { [exaAddress], ); - const delegation = true; const url = 'https://score.snapshot.org/'; - useEffect(() => { + const fetchVotingPower = useCallback(async () => { if (!walletAddress) return; + const { vp } = await snapshot.utils.getVp( + walletAddress, + String(chain.id), + strategies, + 'latest', + space, + delegation, + { + url, + }, + ); + setVotingPower(vp); + return vp; + }, [chain.id, delegation, space, strategies, walletAddress]); - snapshot.utils - .getVp(walletAddress, String(chain.id), strategies, 'latest', space, delegation, { url }) - .then(({ vp }) => setVotingPower(vp)); - }, [chain.id, chain.network, delegation, space, strategies, url, votingPower, walletAddress]); + useEffect(() => { + fetchVotingPower(); + }, [fetchVotingPower]); - return { votingPower }; + return { votingPower, fetchVotingPower }; } diff --git a/pages/governance.tsx b/pages/governance.tsx index 611844159..bd6e590e4 100644 --- a/pages/governance.tsx +++ b/pages/governance.tsx @@ -16,7 +16,7 @@ import useMerkleTree from 'hooks/useMerkleTree'; const Governance: NextPage = () => { const { t } = useTranslation(); const { isConnected, walletAddress, impersonateActive } = useWeb3(); - const { votingPower } = useGovernance(); + const { votingPower, fetchVotingPower } = useGovernance(); const mTree = useMerkleTree(walletAddress); usePageView('/governance', 'Governance'); @@ -47,7 +47,7 @@ const Governance: NextPage = () => { > {mTree.canClaim && } - + ) : ( From 4b46bb47006484b38ca9a726ff1958de6e58782d Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Fri, 22 Sep 2023 08:49:35 -0300 Subject: [PATCH 06/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20change?= =?UTF-8?q?=20space=20url=20for=20goerli?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Proposals/index.tsx | 12 ++++++++++-- hooks/useDelegateRegistry.ts | 9 ++++++--- hooks/useGovernance.ts | 5 +++-- 3 files changed, 19 insertions(+), 7 deletions(-) diff --git a/components/governance/Proposals/index.tsx b/components/governance/Proposals/index.tsx index 74ed1b0c1..ee3289bad 100644 --- a/components/governance/Proposals/index.tsx +++ b/components/governance/Proposals/index.tsx @@ -1,10 +1,18 @@ -import React from 'react'; +import React, { useMemo } from 'react'; import { Box, Button, Divider, Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; import OpenInNewIcon from '@mui/icons-material/OpenInNew'; +import { optimism } from 'wagmi/chains'; +import { useWeb3 } from 'hooks/useWeb3'; const Proposals = () => { const { t } = useTranslation(); + const { chain } = useWeb3(); + + const spaceURL = useMemo( + () => (chain.id === optimism.id ? 'https://gov.exact.ly/' : 'https://demo.snapshot.org/#/exa.eth'), + [chain], + ); return ( @@ -19,7 +27,7 @@ const Proposals = () => { - + diff --git a/hooks/useDelegateRegistry.ts b/hooks/useDelegateRegistry.ts index 97083778c..ff126155d 100644 --- a/hooks/useDelegateRegistry.ts +++ b/hooks/useDelegateRegistry.ts @@ -6,12 +6,15 @@ import { } from 'types/abi'; import { useWeb3 } from './useWeb3'; import { useMemo } from 'react'; +import { optimism } from 'wagmi/chains'; const DELEGATE_REGISTRY_ADDRESS = '0x469788fE6E9E9681C6ebF3bF78e7Fd26Fc015446'; +const SNAPSHOT_SPACE_OPTIMISM = 'gov.exa.eth'; +const SNAPSHOT_SPACE_GOERLI = 'exa.eth'; export const useDelegation = () => { const { chain, walletAddress } = useWeb3(); - const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const space = useMemo(() => (chain.id === optimism.id ? SNAPSHOT_SPACE_OPTIMISM : SNAPSHOT_SPACE_GOERLI), [chain.id]); const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); return useDelegateRegistryDelegation({ @@ -23,7 +26,7 @@ export const useDelegation = () => { export const usePrepareDelegate = (address: Address) => { const { chain, walletAddress } = useWeb3(); - const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const space = useMemo(() => (chain.id === optimism.id ? SNAPSHOT_SPACE_OPTIMISM : SNAPSHOT_SPACE_GOERLI), [chain.id]); const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); return usePrepareDelegateRegistrySetDelegate({ @@ -36,7 +39,7 @@ export const usePrepareDelegate = (address: Address) => { export const usePrepareClearDelegate = () => { const { chain, walletAddress } = useWeb3(); - const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const space = useMemo(() => (chain.id === optimism.id ? SNAPSHOT_SPACE_OPTIMISM : SNAPSHOT_SPACE_GOERLI), [chain.id]); const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); return usePrepareDelegateRegistryClearDelegate({ diff --git a/hooks/useGovernance.ts b/hooks/useGovernance.ts index e7d2f6b34..0719d4f5b 100644 --- a/hooks/useGovernance.ts +++ b/hooks/useGovernance.ts @@ -3,13 +3,14 @@ import { useWeb3 } from './useWeb3'; import { useCallback, useEffect, useMemo, useState } from 'react'; import optimismEXA from '@exactly/protocol/deployments/optimism/EXA.json'; import goerliEXA from '@exactly/protocol/deployments/goerli/EXA.json'; +import { optimism } from 'wagmi/chains'; export default function useGovernance(delegation = true) { const [votingPower, setVotingPower] = useState(undefined); const { chain, walletAddress } = useWeb3(); - const exaAddress = useMemo(() => (chain.id === 10 ? optimismEXA.address : goerliEXA.address), [chain.id]); - const space = useMemo(() => (chain.id === 10 ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); + const exaAddress = useMemo(() => (chain.id === optimism.id ? optimismEXA.address : goerliEXA.address), [chain.id]); + const space = useMemo(() => (chain.id === optimism.id ? 'gov.exa.eth' : 'exa.eth'), [chain.id]); const strategies = useMemo( () => [ From 640d59e474b3edb9894df602f05ae02f13f0518e Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 28 Sep 2023 08:39:23 -0300 Subject: [PATCH 07/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20refact?= =?UTF-8?q?or=20delegate=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Delegation/index.tsx | 346 ++++++++++---------- components/governance/Proposals/index.tsx | 16 +- components/governance/VotingPower/index.tsx | 4 +- hooks/useDelegateRegistry.ts | 4 +- 4 files changed, 188 insertions(+), 182 deletions(-) diff --git a/components/governance/Delegation/index.tsx b/components/governance/Delegation/index.tsx index ce15c53e5..15ad801ba 100644 --- a/components/governance/Delegation/index.tsx +++ b/components/governance/Delegation/index.tsx @@ -1,13 +1,24 @@ -import React, { FC, useMemo, useState } from 'react'; -import { Avatar, Box, Button, Collapse, Divider, Skeleton, TextField, Typography } from '@mui/material'; +import React, { FC, useCallback, useMemo, useState } from 'react'; +import { + Avatar, + Box, + Button, + Dialog, + Divider, + IconButton, + Skeleton, + Slide, + TextField, + Typography, + useMediaQuery, + useTheme, +} from '@mui/material'; +import CloseIcon from '@mui/icons-material/Close'; + import { Trans, useTranslation } from 'react-i18next'; import { LoadingButton } from '@mui/lab'; -import HowToVoteIcon from '@mui/icons-material/HowToVote'; -import AddIcon from '@mui/icons-material/Add'; import { isAddress, zeroAddress } from 'viem'; import { formatWallet } from 'utils/utils'; -import { useEXA } from 'hooks/useEXA'; -import useBalance from 'hooks/useBalance'; import { useWeb3 } from 'hooks/useWeb3'; import { useDelegateRegistryClearDelegate, useDelegateRegistrySetDelegate } from 'types/abi'; import { mainnet, useEnsAvatar, useEnsName, useNetwork, useSwitchNetwork, useWaitForTransaction } from 'wagmi'; @@ -24,33 +35,36 @@ const Delegation: FC = ({ fetchVotingPower }) => { const { votingPower } = useGovernance(false); const { t } = useTranslation(); const { chain: displayNetwork, walletAddress, impersonateActive, exitImpersonate } = useWeb3(); - const [selected, setSelected] = useState<'self-delegate' | 'add-delegate'>(); + const [open, setOpen] = useState(false); const [input, setInput] = useState(''); - const exa = useEXA(); - const exaBalance = useBalance('EXA', exa?.address); const { data: delegate, isLoading: isLoadingDelegate, refetch: refetchDelegate } = useDelegation(); - const { config } = usePrepareDelegate( - delegate !== zeroAddress - ? zeroAddress - : selected === 'add-delegate' && isAddress(input) - ? input - : walletAddress ?? zeroAddress, - ); + const { config } = usePrepareDelegate(isAddress(input) ? input : zeroAddress); const { write, isLoading: submitLoading, data } = useDelegateRegistrySetDelegate(config); - const { config: configClearDelegate } = usePrepareClearDelegate(); + const { config: configClearDelegate } = usePrepareClearDelegate(delegate !== zeroAddress); const { write: writeClearDelegate, isLoading: clearDelegateLoading, data: clearDelegateData, } = useDelegateRegistryClearDelegate(configClearDelegate); const { isLoading: waitingDelegate } = useWaitForTransaction({ - hash: data?.hash ?? clearDelegateData?.hash, + hash: data?.hash, + onSettled: () => { + refetchDelegate(); + fetchVotingPower(); + setOpen(false); + }, + }); + const { isLoading: waitingClearDelegate } = useWaitForTransaction({ + hash: clearDelegateData?.hash, onSettled: () => { refetchDelegate(); fetchVotingPower(); }, }); - const { data: delegateENS } = useEnsName({ address: delegate, chainId: mainnet.id }); + const { data: delegateENS } = useEnsName({ + address: delegate === zeroAddress ? walletAddress : delegate, + chainId: mainnet.id, + }); const { data: delegateENSAvatar, error: ensAvatarError } = useEnsAvatar({ name: delegateENS, chainId: mainnet.id, @@ -62,8 +76,18 @@ const Delegation: FC = ({ fetchVotingPower }) => { const delegateAvatar = useMemo(() => { if (!delegate) return ''; if (delegateENSAvatar && !ensAvatarError) return delegateENSAvatar; - return blockies.create({ seed: delegate.toLocaleLowerCase() }).toDataURL(); - }, [delegate, delegateENSAvatar, ensAvatarError]); + return blockies + .create({ seed: (delegate === zeroAddress ? walletAddress : delegate)?.toLocaleLowerCase() }) + .toDataURL(); + }, [delegate, delegateENSAvatar, ensAvatarError, walletAddress]); + + const openDialog = useCallback(() => { + setOpen(true); + }, []); + + const closeDialog = useCallback(() => { + setOpen(false); + }, []); if (isLoadingDelegate) { return ( @@ -75,14 +99,22 @@ const Delegation: FC = ({ fetchVotingPower }) => { ); } - if (delegate !== zeroAddress) { - return ( - - - - {t('Votes Delegation')} - {exaBalance !== undefined && votingPower !== undefined ? ( - + return ( + + + + + {t('Votes Delegation')} + {votingPower !== undefined ? ( + + {delegate !== zeroAddress ? ( = ({ fetchVotingPower }) => { }} values={{ amount: formatNumber(votingPower, 'USD', true) }} /> - - ) : ( - - )} - - - - - {delegateENS ? delegateENS : formatWallet(delegate)} - - - {chain && chain.id !== displayNetwork.id ? ( - switchNetwork?.(displayNetwork.id)} - loading={switchIsLoading} - > - {t('Please switch to {{network}} network', { network: displayNetwork.name })} - - ) : ( - - {t('Revoke delegation')} - - )} - - ); - } - - return ( - - - - {t('Votes Delegation')} - {exaBalance !== undefined && votingPower !== undefined ? ( - - , - }} - values={{ amount: formatNumber(votingPower, 'USD', true) }} - /> + ) : ( + , + }} + values={{ amount: formatNumber(votingPower, 'USD', true) }} + /> + )} ) : ( - + )} - - `1px solid ${palette.figma.grey[100]}`} - borderRadius="8px" - sx={{ - '&:hover': { - cursor: 'pointer', - bgcolor: 'grey.900', - color: 'grey.50', - }, - bgcolor: selected === 'self-delegate' ? 'grey.900' : '', - color: selected === 'self-delegate' ? 'grey.50' : '', - pointerEvents: submitLoading || waitingDelegate ? 'none' : 'auto', - }} - onClick={() => setSelected('self-delegate')} - > - - - {t('Self Delegate')} - - - {t('Use your voting rights to vote on proposals directly from your connected wallet.')} - - - `1px solid ${palette.figma.grey[100]}`} - borderRadius="8px" - sx={{ - '&:hover': { - cursor: 'pointer', - bgcolor: 'grey.900', - color: 'grey.50', - }, - bgcolor: selected === 'add-delegate' ? 'grey.900' : '', - color: selected === 'add-delegate' ? 'grey.50' : '', - pointerEvents: submitLoading || waitingDelegate ? 'none' : 'auto', - }} - onClick={() => setSelected('add-delegate')} - > - - - {t('Add Delegate')} - - - {t( - 'Delegate your voting rights to a trusted third-party Ethereum address. You never send EXA tokens, only your voting rights and can revoke the delegation at any time.', - )} - - - - - - {t( - 'Enter the address of the third-party you wish to delegate your voting rights to below. You can also check the Delegates List and find someone to represent you.', - )} - - setInput(e.target.value)} - /> - - + + + + {delegateENS ? delegateENS : formatWallet(delegate === zeroAddress ? walletAddress : delegate)} + {impersonateActive ? ( @@ -243,19 +157,111 @@ const Delegation: FC = ({ fetchVotingPower }) => { {t('Please switch to {{network}} network', { network: displayNetwork.name })} ) : ( + + + {t('Delegate Votes')} + + {delegate !== zeroAddress && ( + + {t('Revoke delegation')} + + )} + + )} + + {t( + 'Delegate your voting rights to a trusted third-party Ethereum address. You never send EXA tokens, only your voting rights and can re-delegate or revoke the delegation at any time.', + )} + + + ); +}; + +type DelegateInputDialogProps = { + open: boolean; + onClose: () => void; + input: string; + setInput: (input: string) => void; + onDelegate?: () => void; + isLoading?: boolean; +}; + +const DelegateInputDialog: FC = ({ + open, + onClose, + input, + setInput, + onDelegate, + isLoading, +}) => { + const { walletAddress } = useWeb3(); + const { t } = useTranslation(); + const { breakpoints } = useTheme(); + const isMobile = useMediaQuery(breakpoints.down('md')); + + return ( + + + + + + {t('Votes Delegation')} + + {t( + 'Enter the address of the third-party you wish to delegate your voting rights to below. You can also check the Delegates List and find someone to represent you.', + )} + + setInput(e.target.value)} + /> - {selected === 'add-delegate' && Boolean(input && isAddress(input)) - ? `${t('Delegate Votes to')} ${formatWallet(input)}` - : t('Delegate Votes')} + {t('Delegate Votes')} - )} - + + ); }; diff --git a/components/governance/Proposals/index.tsx b/components/governance/Proposals/index.tsx index ee3289bad..f9af33ea7 100644 --- a/components/governance/Proposals/index.tsx +++ b/components/governance/Proposals/index.tsx @@ -1,7 +1,6 @@ import React, { useMemo } from 'react'; import { Box, Button, Divider, Typography } from '@mui/material'; import { useTranslation } from 'react-i18next'; -import OpenInNewIcon from '@mui/icons-material/OpenInNew'; import { optimism } from 'wagmi/chains'; import { useWeb3 } from 'hooks/useWeb3'; @@ -20,16 +19,15 @@ const Proposals = () => { {t('Proposals')} - - {t('See our proposals on the governance forum via Snapshot.')} - - {t("You can also create your own proposals and vote on other's proposals.")} - - + + {t( + "Use your voting power to participate in discussions, propose enhancements, and cast votes to shape the Protocol's evolution on Snapchat", + )} + - diff --git a/components/governance/VotingPower/index.tsx b/components/governance/VotingPower/index.tsx index b9d1b808a..8eaba6dd9 100644 --- a/components/governance/VotingPower/index.tsx +++ b/components/governance/VotingPower/index.tsx @@ -11,14 +11,14 @@ const VotingPower: FC = ({ votingPower }) => { const { t } = useTranslation(); return ( - + {t('Voting Power')} {votingPower === undefined ? ( ) : ( - {formatNumber(votingPower, 'USD', true)} + {votingPower === 0 ? 0 : formatNumber(votingPower, 'USD', true)} )} diff --git a/hooks/useDelegateRegistry.ts b/hooks/useDelegateRegistry.ts index ff126155d..1d9816c76 100644 --- a/hooks/useDelegateRegistry.ts +++ b/hooks/useDelegateRegistry.ts @@ -30,6 +30,7 @@ export const usePrepareDelegate = (address: Address) => { const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); return usePrepareDelegateRegistrySetDelegate({ + enabled: address !== zeroAddress && address !== walletAddress, chainId: chain.id, address: DELEGATE_REGISTRY_ADDRESS, account: walletAddress ?? zeroAddress, @@ -37,12 +38,13 @@ export const usePrepareDelegate = (address: Address) => { }); }; -export const usePrepareClearDelegate = () => { +export const usePrepareClearDelegate = (enabled: boolean) => { const { chain, walletAddress } = useWeb3(); const space = useMemo(() => (chain.id === optimism.id ? SNAPSHOT_SPACE_OPTIMISM : SNAPSHOT_SPACE_GOERLI), [chain.id]); const encodedSpace = useMemo(() => stringToHex(space, { size: 32 }), [space]); return usePrepareDelegateRegistryClearDelegate({ + enabled, chainId: chain.id, address: DELEGATE_REGISTRY_ADDRESS, account: walletAddress ?? zeroAddress, From 88c9ddc3afd5fc287f9474f9d2868bb59b5caac1 Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 28 Sep 2023 08:40:52 -0300 Subject: [PATCH 08/13] =?UTF-8?q?=F0=9F=8C=90=20governance:=20add=20transl?= =?UTF-8?q?ations?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- i18n/es/translation.json | 11 +++-------- 1 file changed, 3 insertions(+), 8 deletions(-) diff --git a/i18n/es/translation.json b/i18n/es/translation.json index 20f5df019..6c2774b16 100644 --- a/i18n/es/translation.json +++ b/i18n/es/translation.json @@ -303,13 +303,7 @@ "Delegate Votes": "Delegar Votos", "Voting Power": "Poder de Voto", "Governance": "Gobernanza", - "Self Delegate": "Auto Delegar", - "Use your voting rights to vote on proposals directly from your connected wallet.": "Usa tus derechos de voto para votar en propuestas directamente desde tu billetera conectada.", - "Add Delegate": "Añadir Delegado", - "Delegate your voting rights to a trusted third-party Ethereum address. You never send EXA tokens, only your voting rights and can revoke the delegation at any time.": "Delega tus derechos de voto a una dirección de Ethereum de terceros de confianza. Nunca envías tokens EXA, solo tus derechos de voto y puedes revocar la delegación en cualquier momento.", "Enter the address of the third-party you wish to delegate your voting rights to below. You can also check the Delegates List and find someone to represent you.": "Ingresa la dirección del tercero al que deseas delegar tus derechos de voto a continuación. También puedes consultar la Lista de Delegados y encontrar a alguien que te represente.", - "Enter delegate address": "Ingresa la dirección del delegado", - "Delegate Votes to": "Delegar Votos a", "Proposals": "Propuestas", "Expired": "Expirado", "Processing transaction...": "Procesando transacción...", @@ -480,6 +474,7 @@ "Third-Party": "Terceros", "Security": "Seguridad", "The DebtManager contract is responsible for the leverage, deleverage, and rollover functionality of the protocol.": "El contrato DebtManager es responsable de la funcionalidad de apalancamiento, desapalancamiento y refinanciamiento del protocolo.", - "See our proposals on the governance forum via Snapshot.": "Mira nuestras propuestas en el foro de gobernanza a través de Snapshot.", - "You can also create your own proposals and vote on other's proposals.": "También puedes crear tus propias propuestas y votar en las propuestas de otros." + "Delegate your voting rights to a trusted third-party Ethereum address. You never send EXA tokens, only your voting rights and can re-delegate or revoke the delegation at any time.": "Delega tus derechos de voto a una dirección de Ethereum de terceros de confianza. Nunca envías tokens EXA, solo tus derechos de voto y puedes volver a delegar o revocar la delegación en cualquier momento.", + "Use your voting power to participate in discussions, propose enhancements, and cast votes to shape the Protocol's evolution on Snapchat": "Usa tu poder de voto para participar en discusiones, proponer mejoras y emitir votos para dar forma a la evolución del Protocolo en Snapchat", + "View Proposals": "Ver Propuestas" } From aeffc006444420134a6b305571a4c0aee2b28648 Mon Sep 17 00:00:00 2001 From: Jorge Galat Date: Thu, 28 Sep 2023 10:28:08 -0300 Subject: [PATCH 09/13] =?UTF-8?q?=E2=9C=85=20e2e:=20fix=20test?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- hooks/useApprove.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/hooks/useApprove.ts b/hooks/useApprove.ts index 4846b6c1f..79c27aae3 100644 --- a/hooks/useApprove.ts +++ b/hooks/useApprove.ts @@ -108,14 +108,14 @@ function useApprove({ break; case 'repay': case 'repayAtMaturity': - quantity = (parseUnits(qty, marketAccount.decimals) * 1005n) / 1000n; + quantity = (parseUnits(qty, marketAccount.decimals) * 101n) / 100n; break; case 'borrow': case 'borrowAtMaturity': case 'withdraw': case 'withdrawAtMaturity': quantity = - ((await contract.read.previewWithdraw([parseUnits(qty, marketAccount.decimals)], opts)) * 1005n) / 1000n; + ((await contract.read.previewWithdraw([parseUnits(qty, marketAccount.decimals)], opts)) * 101n) / 100n; break; } From c30c000e2047ee2f033da187ae97ada74dc8be6f Mon Sep 17 00:00:00 2001 From: Juan Pablo Rombola Date: Thu, 28 Sep 2023 15:37:42 -0300 Subject: [PATCH 10/13] =?UTF-8?q?=E2=99=BB=EF=B8=8F=20governance:=20refact?= =?UTF-8?q?or=20delegation=20component?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- components/governance/Delegation/index.tsx | 82 ++++++++++++--------- components/governance/VotingPower/index.tsx | 2 +- i18n/es/translation.json | 7 +- pages/governance.tsx | 4 +- 4 files changed, 56 insertions(+), 39 deletions(-) diff --git a/components/governance/Delegation/index.tsx b/components/governance/Delegation/index.tsx index 15ad801ba..fcbf054e1 100644 --- a/components/governance/Delegation/index.tsx +++ b/components/governance/Delegation/index.tsx @@ -15,7 +15,7 @@ import { } from '@mui/material'; import CloseIcon from '@mui/icons-material/Close'; -import { Trans, useTranslation } from 'react-i18next'; +import { useTranslation } from 'react-i18next'; import { LoadingButton } from '@mui/lab'; import { isAddress, zeroAddress } from 'viem'; import { formatWallet } from 'utils/utils'; @@ -27,12 +27,9 @@ import { useDelegation, usePrepareClearDelegate, usePrepareDelegate } from 'hook import formatNumber from 'utils/formatNumber'; import useGovernance from 'hooks/useGovernance'; -type Props = { - fetchVotingPower: () => void; -}; - -const Delegation: FC = ({ fetchVotingPower }) => { - const { votingPower } = useGovernance(false); +const Delegation = () => { + const { votingPower: yourVotes } = useGovernance(false); + const { votingPower, fetchVotingPower } = useGovernance(); const { t } = useTranslation(); const { chain: displayNetwork, walletAddress, impersonateActive, exitImpersonate } = useWeb3(); const [open, setOpen] = useState(false); @@ -89,6 +86,11 @@ const Delegation: FC = ({ fetchVotingPower }) => { setOpen(false); }, []); + const delegatedToYou = useMemo(() => { + if (votingPower === undefined || yourVotes === undefined) return undefined; + return delegate === zeroAddress ? votingPower - yourVotes : votingPower; + }, [delegate, votingPower, yourVotes]); + if (isLoadingDelegate) { return ( @@ -111,37 +113,49 @@ const Delegation: FC = ({ fetchVotingPower }) => { isLoading={submitLoading || waitingDelegate} /> - {t('Votes Delegation')} - {votingPower !== undefined ? ( - - {delegate !== zeroAddress ? ( - , - }} - values={{ amount: formatNumber(votingPower, 'USD', true) }} - /> - ) : ( - , - }} - values={{ amount: formatNumber(votingPower, 'USD', true) }} - /> - )} - + + {t('Voting Power')} + {votingPower === undefined ? ( + + ) : ( + + {votingPower === 0 ? 0 : formatNumber(votingPower, 'USD', true)} + + )} + + {votingPower !== undefined && yourVotes !== undefined && delegatedToYou !== undefined ? ( + yourVotes === 0 && votingPower === 0 ? ( + + {t('There is no voting power in the connected wallet, and no votes have been delegated to you.')} + + ) : ( + + + {t('Delegated to you')} + {delegatedToYou === 0 ? 0 : formatNumber(delegatedToYou, 'USD', true)} + + + {delegate === zeroAddress ? ( + {t('On connected wallet')} + ) : ( + + {t('Delegated to')} + + + + {delegateENS ? delegateENS : formatWallet(delegate === zeroAddress ? walletAddress : delegate)} + + + + )} + {yourVotes === 0 ? 0 : formatNumber(yourVotes, 'USD', true)} + + + ) ) : ( )} - - - - {delegateENS ? delegateENS : formatWallet(delegate === zeroAddress ? walletAddress : delegate)} - - {impersonateActive ? (