draft implementation of governance page
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
a2a4b86ccc
commit
20f2e78ae7
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.4.4",
|
||||
"version": "0.5.1",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
@ -16,6 +16,7 @@
|
||||
"@ethersproject/bignumber": "^5.8.0",
|
||||
"@ethersproject/units": "^5.8.0",
|
||||
"@mui/icons-material": "^6.4.7",
|
||||
"@mui/lab": "6.0.1-beta.36",
|
||||
"@mui/material": "^6.4.7",
|
||||
"@mui/utils": "^6.4.6",
|
||||
"@polkadot-api/metadata-builders": "0.13.0",
|
||||
|
||||
218
pnpm-lock.yaml
218
pnpm-lock.yaml
@ -26,6 +26,9 @@ importers:
|
||||
'@mui/icons-material':
|
||||
specifier: ^6.4.7
|
||||
version: 6.4.7(@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
'@mui/lab':
|
||||
specifier: 6.0.1-beta.36
|
||||
version: 6.0.1-beta.36(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@mui/material':
|
||||
specifier: ^6.4.7
|
||||
version: 6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
@ -525,6 +528,21 @@ packages:
|
||||
'@ethersproject/units@5.8.0':
|
||||
resolution: {integrity: sha512-lxq0CAnc5kMGIiWW4Mr041VT8IhNM+Pn5T3haO74XZWFulk7wH1Gv64HqE96hT4a7iiNMdOCFEBgaxWuk8ETKQ==}
|
||||
|
||||
'@floating-ui/core@1.7.4':
|
||||
resolution: {integrity: sha512-C3HlIdsBxszvm5McXlB8PeOEWfBhcGBTZGkGlWc2U0KFY5IwG5OQEuQ8rq52DZmcHDlPLd+YFBK+cZcytwIFWg==}
|
||||
|
||||
'@floating-ui/dom@1.7.5':
|
||||
resolution: {integrity: sha512-N0bD2kIPInNHUHehXhMke1rBGs1dwqvC9O9KYMyyjK7iXt7GAhnro7UlcuYcGdS/yYOlq0MAVgrow8IbWJwyqg==}
|
||||
|
||||
'@floating-ui/react-dom@2.1.7':
|
||||
resolution: {integrity: sha512-0tLRojf/1Go2JgEVm+3Frg9A3IW8bJgKgdO0BN5RkF//ufuz2joZM63Npau2ff3J6lUVYgDSNzNkR+aH3IVfjg==}
|
||||
peerDependencies:
|
||||
react: '>=16.8.0'
|
||||
react-dom: '>=16.8.0'
|
||||
|
||||
'@floating-ui/utils@0.2.10':
|
||||
resolution: {integrity: sha512-aGTxbpbg8/b5JfU1HXSrbH3wXZuLPJcNEcZQFMxLs3oSzgtVu6nFPkbbGGUvBcUjKV2YyB9Wxxabo+HEH9tcRQ==}
|
||||
|
||||
'@humanfs/core@0.19.1':
|
||||
resolution: {integrity: sha512-5DyQ4+1JEUzejeK1JGICcideyfUbGixgS9jNgex5nqkW+cY7WZhxBigmieN5Qnw9ZosSNVC9KQKyb+GUaGyKUA==}
|
||||
engines: {node: '>=18.18.0'}
|
||||
@ -638,6 +656,18 @@ packages:
|
||||
resolution: {integrity: sha512-w8CVbdkDrVXFJbfBSlDfafDR6BAkpDmv1bC1UJVCoVny5tW2RKAdn9i68Xf7asYT4TnUhl/hN4zfUiKQq9II4g==}
|
||||
engines: {node: '>=16.0.0'}
|
||||
|
||||
'@mui/base@5.0.0-beta.70':
|
||||
resolution: {integrity: sha512-Tb/BIhJzb0pa5zv/wu7OdokY9ZKEDqcu1BDFnohyvGCoHuSXbEr90rPq1qeNW3XvTBIbNWHEF7gqge+xpUo6tQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
deprecated: This package has been replaced by @base-ui/react
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.7':
|
||||
resolution: {integrity: sha512-XjJrKFNt9zAKvcnoIIBquXyFyhfrHYuttqMsoDS7lM7VwufYG4fAPw4kINjBFg++fqXM2BNAuWR9J7XVIuKIKg==}
|
||||
|
||||
@ -652,6 +682,27 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/lab@6.0.1-beta.36':
|
||||
resolution: {integrity: sha512-af9lDmA9SZGEWF1XXk0EVBpfCITk9IKsvh9lLOZGdYaaHfQeCsqxGEDMvNO66j0P8EYoxpyry84LFCJYuLVtVw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@mui/material': ^6.5.0
|
||||
'@mui/material-pigment-css': ^6.5.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react-dom: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@mui/material-pigment-css':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/material@6.4.7':
|
||||
resolution: {integrity: sha512-K65StXUeGAtFJ4ikvHKtmDCO5Ab7g0FZUu2J5VpoKD+O6Y3CjLYzRi+TMlI3kaL4CL158+FccMoOd/eaddmeRQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -682,6 +733,16 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/private-theming@6.4.9':
|
||||
resolution: {integrity: sha512-LktcVmI5X17/Q5SkwjCcdOLBzt1hXuc14jYa7NPShog0GBDCDvKtcnP0V7a2s6EiVRlv7BzbWEJzH6+l/zaCxw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/styled-engine@6.4.6':
|
||||
resolution: {integrity: sha512-vSWYc9ZLX46be5gP+FCzWVn5rvDr4cXC5JBZwSIkYk9xbC7GeV+0kCvB8Q6XLFQJy+a62bbqtmdwS4Ghi9NBlQ==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -695,6 +756,19 @@ packages:
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
|
||||
'@mui/styled-engine@6.5.0':
|
||||
resolution: {integrity: sha512-8woC2zAqF4qUDSPIBZ8v3sakj+WgweolpyM/FXf8jAx6FMls+IE4Y8VDZc+zS805J7PRz31vz73n2SovKGaYgw==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.4.1
|
||||
'@emotion/styled': ^11.3.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
|
||||
'@mui/system@6.4.7':
|
||||
resolution: {integrity: sha512-7wwc4++Ak6tGIooEVA9AY7FhH2p9fvBMORT4vNLMAysH3Yus/9B9RYMbrn3ANgsOyvT3Z7nE+SP8/+3FimQmcg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -711,6 +785,22 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/system@6.5.0':
|
||||
resolution: {integrity: sha512-XcbBYxDS+h/lgsoGe78ExXFZXtuIlSBpn/KsZq8PtZcIkUNJInkuDqcLd2rVBQrDC1u+rvVovdaWPf2FHKJf3w==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@emotion/react': ^11.5.0
|
||||
'@emotion/styled': ^11.3.0
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@emotion/react':
|
||||
optional: true
|
||||
'@emotion/styled':
|
||||
optional: true
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/types@7.2.21':
|
||||
resolution: {integrity: sha512-6HstngiUxNqLU+/DPqlUJDIPbzUBxIVHb1MmXP0eTWDIROiCR2viugXpEif0PPe2mLqqakPzzRClWAnK+8UJww==}
|
||||
peerDependencies:
|
||||
@ -719,6 +809,14 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/types@7.2.24':
|
||||
resolution: {integrity: sha512-3c8tRt/CbWZ+pEg7QpSwbdxOk36EfmhbKf6AGZsD1EcLDLTSZoxxJ86FVtcjxvjuhdyBiWKSTGZFaXCnidO2kw==}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/utils@6.4.6':
|
||||
resolution: {integrity: sha512-43nZeE1pJF2anGafNydUcYFPtHwAqiBiauRtaMvurdrZI3YrUjHkAu43RBsxef7OFtJMXGiHFvq43kb7lig0sA==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
@ -729,6 +827,16 @@ packages:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@mui/utils@6.4.9':
|
||||
resolution: {integrity: sha512-Y12Q9hbK9g+ZY0T3Rxrx9m2m10gaphDuUMgWxyV5kNJevVxXYCLclYUCC9vXaIk1/NdNDTcW2Yfr2OGvNFNmHg==}
|
||||
engines: {node: '>=14.0.0'}
|
||||
peerDependencies:
|
||||
'@types/react': ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
react: ^17.0.0 || ^18.0.0 || ^19.0.0
|
||||
peerDependenciesMeta:
|
||||
'@types/react':
|
||||
optional: true
|
||||
|
||||
'@noble/ciphers@1.2.1':
|
||||
resolution: {integrity: sha512-rONPWMC7PeExE077uLE4oqWrZ1IvAfz3oH9LibVAcVCopJiA9R62uavnbEzdkVmJYI6M6Zgkbeb07+tWjlq2XA==}
|
||||
engines: {node: ^14.21.3 || >=16}
|
||||
@ -3423,6 +3531,23 @@ snapshots:
|
||||
'@ethersproject/constants': 5.8.0
|
||||
'@ethersproject/logger': 5.8.0
|
||||
|
||||
'@floating-ui/core@1.7.4':
|
||||
dependencies:
|
||||
'@floating-ui/utils': 0.2.10
|
||||
|
||||
'@floating-ui/dom@1.7.5':
|
||||
dependencies:
|
||||
'@floating-ui/core': 1.7.4
|
||||
'@floating-ui/utils': 0.2.10
|
||||
|
||||
'@floating-ui/react-dom@2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@floating-ui/dom': 1.7.5
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
|
||||
'@floating-ui/utils@0.2.10': {}
|
||||
|
||||
'@humanfs/core@0.19.1': {}
|
||||
|
||||
'@humanfs/node@0.16.6':
|
||||
@ -3615,6 +3740,20 @@ snapshots:
|
||||
transitivePeerDependencies:
|
||||
- supports-color
|
||||
|
||||
'@mui/base@5.0.0-beta.70(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@floating-ui/react-dom': 2.1.7(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@mui/types': 7.2.24(@types/react@19.0.10)
|
||||
'@mui/utils': 6.4.9(@types/react@19.0.10)(react@19.0.0)
|
||||
'@popperjs/core': 2.11.8
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/core-downloads-tracker@6.4.7': {}
|
||||
|
||||
'@mui/icons-material@6.4.7(@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)':
|
||||
@ -3625,6 +3764,23 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/lab@6.0.1-beta.36(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@mui/base': 5.0.0-beta.70(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@mui/material': 6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)
|
||||
'@mui/system': 6.5.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
'@mui/types': 7.2.24(@types/react@19.0.10)
|
||||
'@mui/utils': 6.4.9(@types/react@19.0.10)(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
react-dom: 19.0.0(react@19.0.0)
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.0.10)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/material@6.4.7(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react-dom@19.0.0(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
@ -3648,16 +3804,38 @@ snapshots:
|
||||
|
||||
'@mui/private-theming@6.4.6(@types/react@19.0.10)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
'@babel/runtime': 7.27.6
|
||||
'@mui/utils': 6.4.6(@types/react@19.0.10)(react@19.0.0)
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/private-theming@6.4.9(@types/react@19.0.10)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@mui/utils': 6.4.9(@types/react@19.0.10)(react@19.0.0)
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/styled-engine@6.4.6(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
'@babel/runtime': 7.27.6
|
||||
'@emotion/cache': 11.14.0
|
||||
'@emotion/serialize': 1.3.3
|
||||
'@emotion/sheet': 1.4.0
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.0.10)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
|
||||
'@mui/styled-engine@6.5.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@emotion/cache': 11.14.0
|
||||
'@emotion/serialize': 1.3.3
|
||||
'@emotion/sheet': 1.4.0
|
||||
@ -3684,10 +3862,30 @@ snapshots:
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/system@6.5.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@mui/private-theming': 6.4.9(@types/react@19.0.10)(react@19.0.0)
|
||||
'@mui/styled-engine': 6.5.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@emotion/styled@11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0))(react@19.0.0)
|
||||
'@mui/types': 7.2.24(@types/react@19.0.10)
|
||||
'@mui/utils': 6.4.9(@types/react@19.0.10)(react@19.0.0)
|
||||
clsx: 2.1.1
|
||||
csstype: 3.1.3
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
optionalDependencies:
|
||||
'@emotion/react': 11.14.0(@types/react@19.0.10)(react@19.0.0)
|
||||
'@emotion/styled': 11.14.0(@emotion/react@11.14.0(@types/react@19.0.10)(react@19.0.0))(@types/react@19.0.10)(react@19.0.0)
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/types@7.2.21(@types/react@19.0.10)':
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/types@7.2.24(@types/react@19.0.10)':
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/utils@6.4.6(@types/react@19.0.10)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
@ -3700,6 +3898,18 @@ snapshots:
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@mui/utils@6.4.9(@types/react@19.0.10)(react@19.0.0)':
|
||||
dependencies:
|
||||
'@babel/runtime': 7.27.6
|
||||
'@mui/types': 7.2.24(@types/react@19.0.10)
|
||||
'@types/prop-types': 15.7.14
|
||||
clsx: 2.1.1
|
||||
prop-types: 15.8.1
|
||||
react: 19.0.0
|
||||
react-is: 19.0.0
|
||||
optionalDependencies:
|
||||
'@types/react': 19.0.10
|
||||
|
||||
'@noble/ciphers@1.2.1': {}
|
||||
|
||||
'@noble/ciphers@1.3.0': {}
|
||||
@ -5042,7 +5252,7 @@ snapshots:
|
||||
|
||||
babel-plugin-macros@3.1.0:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
'@babel/runtime': 7.27.6
|
||||
cosmiconfig: 7.1.0
|
||||
resolve: 1.22.10
|
||||
|
||||
@ -5238,7 +5448,7 @@ snapshots:
|
||||
|
||||
dom-helpers@5.2.1:
|
||||
dependencies:
|
||||
'@babel/runtime': 7.26.9
|
||||
'@babel/runtime': 7.27.6
|
||||
csstype: 3.1.3
|
||||
|
||||
dot-case@3.0.4:
|
||||
|
||||
@ -31,6 +31,8 @@ const Wrapper = lazy(() => import("./containers/WethWrapper/WethWrapper"));
|
||||
const Dex = lazy(() => import("./containers/Dex/Dex"));
|
||||
const Bridge = lazy(() => import("./containers/Bridge/Bridge"));
|
||||
const NotFound = lazy(() => import("./containers/NotFound/NotFound"));
|
||||
const Governance = lazy(() => import("./containers/Governance/Governance"));
|
||||
const ProposalDetails = lazy(() => import("./containers/Governance/ProposalDetails"));
|
||||
|
||||
const PREFIX = "App";
|
||||
|
||||
@ -213,6 +215,8 @@ function App() {
|
||||
}
|
||||
<Route path="/bridge" element={<Bridge config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
|
||||
<Route path="/dex/:name" element={<Dex connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
|
||||
<Route path="/governance" element={<Governance config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
|
||||
<Route path="/governance/:id" element={<ProposalDetails config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
|
||||
</>
|
||||
}
|
||||
<Route path="/empty" element={<NotFound
|
||||
|
||||
39
src/components/Progress/LinearProgressBar.jsx
Normal file
39
src/components/Progress/LinearProgressBar.jsx
Normal file
@ -0,0 +1,39 @@
|
||||
import { Box, LinearProgress as MuiLinearProgress } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
|
||||
const PREFIX = "MuiLinearProgress";
|
||||
|
||||
const classes = {
|
||||
chip: `${PREFIX}-bar`,
|
||||
};
|
||||
|
||||
const StyledMuiLinearProgress = styled(MuiLinearProgress, {
|
||||
shouldForwardProp: (prop) => prop !== "barBackground" && prop !== 'barColor' && prop !== 'height'
|
||||
})(({ theme, barColor, barBackground, height }) => ({
|
||||
height: height || 8,
|
||||
borderRadius: 4,
|
||||
backgroundColor: barBackground || theme.palette.grey[300],
|
||||
'& .MuiLinearProgress-bar': {
|
||||
backgroundColor: barColor || theme.palette.primary.main
|
||||
}
|
||||
}));
|
||||
|
||||
const LinearProgressBar = (props) => {
|
||||
return (
|
||||
<Box sx={{ position: 'relative', width: '100%', py: 1 }}>
|
||||
<StyledMuiLinearProgress {...props} />
|
||||
{props.target && <Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: `${props.target}%`,
|
||||
top: props.targetTop || 0,
|
||||
bottom: props.targetBottom || 0,
|
||||
width: props.targetWidth || "2px",
|
||||
backgroundColor: props.targetBackgroundColor || 'white',
|
||||
}}
|
||||
></Box>}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default LinearProgressBar;
|
||||
@ -24,6 +24,8 @@ import TelegramIcon from '@mui/icons-material/Telegram';
|
||||
import HowToVoteIcon from '@mui/icons-material/HowToVote';
|
||||
import HubIcon from '@mui/icons-material/Hub';
|
||||
import PublicIcon from '@mui/icons-material/Public';
|
||||
import ForkRightIcon from '@mui/icons-material/ForkRight';
|
||||
import GavelIcon from '@mui/icons-material/Gavel';
|
||||
import ForumIcon from '@mui/icons-material/Forum';
|
||||
import ErrorOutlineIcon from '@mui/icons-material/ErrorOutline';
|
||||
import BookIcon from '@mui/icons-material/Book';
|
||||
@ -177,7 +179,8 @@ const NavContent = ({ chainId, addressChainId }) => {
|
||||
}
|
||||
/>
|
||||
<NavItem icon={StakeIcon} label={`Stake`} to="/stake" />
|
||||
<NavItem icon={PublicIcon} label={`Bridge`} to="/bridge" />
|
||||
<NavItem icon={ForkRightIcon} label={`Bridge`} to="/bridge" />
|
||||
<NavItem icon={GavelIcon} label={`Governance`} to="/governance" />
|
||||
<Box className="menu-divider">
|
||||
<Divider />
|
||||
</Box>
|
||||
|
||||
@ -1,6 +1,5 @@
|
||||
import { Box, Tab, Tabs, Container, useMediaQuery } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
import ReactGA from "react-ga4";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
@ -21,7 +20,6 @@ import { useTokenSymbol } from "../../hooks/tokens";
|
||||
|
||||
const Bonds = ({ chainId, address, connect }) => {
|
||||
const [isZoomed] = useState(false);
|
||||
const navigate = useNavigate();
|
||||
const [secondsTo, setSecondsTo] = useState(0);
|
||||
|
||||
const isSmallScreen = useMediaQuery("(max-width: 650px)");
|
||||
@ -29,7 +27,7 @@ const Bonds = ({ chainId, address, connect }) => {
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: "pageview", page: "/bonds" });
|
||||
}, [])
|
||||
}, []);
|
||||
|
||||
const { liveBonds } = useLiveBonds(chainId);
|
||||
const totalReserves = useTotalReserves(chainId);
|
||||
|
||||
92
src/containers/Governance/Governance.jsx
Normal file
92
src/containers/Governance/Governance.jsx
Normal file
@ -0,0 +1,92 @@
|
||||
import { useEffect } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
|
||||
import { Box, Container, Grid, Divider, Typography, useMediaQuery } from "@mui/material";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
import { PrimaryButton } from "../../components/Button";
|
||||
|
||||
import GovernanceInfoText from "./components/GovernanceInfoText";
|
||||
import ProposalsList from "./components/ProposalsList";
|
||||
import { ProposalsCount, MinQuorumPercentage, ProposalThreshold } from "./components/Metric";
|
||||
|
||||
import { useTokenSymbol } from "../../hooks/tokens";
|
||||
|
||||
const Governance = ({ connect, config, address, chainId }) => {
|
||||
const isSemiSmallScreen = useMediaQuery("(max-width: 745px)");
|
||||
const isSmallScreen = useMediaQuery("(max-width: 650px)");
|
||||
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
||||
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
|
||||
const handleModal = () => {
|
||||
alert("proposal modal here");
|
||||
}
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: "pageview", page: "/governance" });
|
||||
}, []);
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<PageTitle name="ghostDAO Governance" subtitle={`Vote for proposals and suggest new with $${ghstSymbol}`} />
|
||||
<Container
|
||||
style={{
|
||||
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
||||
paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
||||
minHeight: "calc(100vh - 128px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mt: "15px" }}>
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
|
||||
<Typography variant="h6">
|
||||
Proposal Requirements
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<Grid container spacing={1}>
|
||||
<Grid item xs={isSmallScreen ? 12 : 4}>
|
||||
<ProposalsCount chainId={chainId} />
|
||||
</Grid>
|
||||
<Grid item xs={isSmallScreen ? 12 : 4}>
|
||||
<MinQuorumPercentage chainId={chainId} />
|
||||
</Grid>
|
||||
<Grid item xs={isSmallScreen ? 12 : 4}>
|
||||
<ProposalThreshold chainId={chainId} ghstSymbol={ghstSymbol} />
|
||||
</Grid>
|
||||
</Grid>
|
||||
|
||||
<Divider sx={{ marginTop: "30px" }} />
|
||||
<Box display="flex" justifyContent="center">Claimes for locked funds could be here</Box>
|
||||
<Divider />
|
||||
|
||||
<Box mt="15px" display="flex" flexDirection="column" alignItems="center" justifyContent="center">
|
||||
<PrimaryButton
|
||||
fullWidth
|
||||
onClick={() => handleModal(true)}
|
||||
sx={{ maxWidth: isSemiSmallScreen ? "100%" : "350px" }}
|
||||
>
|
||||
Create Proposal
|
||||
</PrimaryButton>
|
||||
<Box textAlign="center" mt="15px">
|
||||
<GovernanceInfoText />
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
<ProposalsList config={config} chainId={chainId} />
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default Governance;
|
||||
244
src/containers/Governance/ProposalDetails.jsx
Normal file
244
src/containers/Governance/ProposalDetails.jsx
Normal file
@ -0,0 +1,244 @@
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
import { useParams } from 'react-router-dom';
|
||||
|
||||
import { Box, Container, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
import LinearProgressBar from "../../components/Progress/LinearProgressBar";
|
||||
import InfoTooltip from "../../components/Tooltip/InfoTooltip";
|
||||
import Chip from "../../components/Chip/Chip";
|
||||
import { SecondaryButton } from "../../components/Button";
|
||||
|
||||
import { formatNumber } from "../../helpers";
|
||||
import { prettifySecondsInDays } from "../../helpers/timeUtil";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
|
||||
import ProposalDiscussionModal from "./components/ProposalDiscussionModal";
|
||||
import ProposalDiscussion from "./components/ProposalDiscussion";
|
||||
import { convertStatusToTemplate } from "./helpers";
|
||||
|
||||
import { useTokenSymbol, useTotalSupply, useBalance } from "../../hooks/tokens";
|
||||
import {
|
||||
useProposalStatus,
|
||||
useProposalProposer,
|
||||
useProposalLocked,
|
||||
useProposalQuorum,
|
||||
useProposalVotes,
|
||||
useProposalSnapshot,
|
||||
useProposalDeadline,
|
||||
useProposalVotingDelay
|
||||
} from "../../hooks/governance";
|
||||
|
||||
///////////////////////////////////////////////////////
|
||||
import Timeline from '@mui/lab/Timeline';
|
||||
import TimelineItem from '@mui/lab/TimelineItem';
|
||||
import TimelineSeparator from '@mui/lab/TimelineSeparator';
|
||||
import TimelineConnector from '@mui/lab/TimelineConnector';
|
||||
import TimelineContent from '@mui/lab/TimelineContent';
|
||||
import TimelineOppositeContent from '@mui/lab/TimelineOppositeContent';
|
||||
import TimelineDot from '@mui/lab/TimelineDot';
|
||||
///////////////////////////
|
||||
import FastfoodIcon from '@mui/icons-material/Fastfood';
|
||||
import LaptopMacIcon from '@mui/icons-material/LaptopMac';
|
||||
import HotelIcon from '@mui/icons-material/Hotel';
|
||||
import RepeatIcon from '@mui/icons-material/Repeat';
|
||||
|
||||
const HUNDRED = new DecimalBigNumber(100n, 0);
|
||||
|
||||
const ProposalDetails = ({ chainId, address }) => {
|
||||
const { id } = useParams();
|
||||
const [selectedDiscussionUrl, setSelectedDiscussionUrl] = useState(undefined);
|
||||
|
||||
const isSemiSmallScreen = useMediaQuery("(max-width: 745px)");
|
||||
const isSmallScreen = useMediaQuery("(max-width: 650px)");
|
||||
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
const { balance } = useBalance(chainId, "GHST", address);
|
||||
const { totalSupply } = useTotalSupply(chainId, "GHST"); // TODO: revisit
|
||||
|
||||
const { status: proposalStatus } = useProposalStatus(chainId, id);
|
||||
const { proposer: proposalProposer } = useProposalProposer(chainId, id);
|
||||
const { locked: proposalLocked } = useProposalLocked(chainId, id);
|
||||
const { quorum: proposalQuorum } = useProposalQuorum(chainId, id);
|
||||
const { forVotes, againstVotes } = useProposalVotes(chainId, id);
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: "pageview", page: `/governance/${id}` });
|
||||
}, []);
|
||||
|
||||
const isDiscussionModalOpened = useMemo(() => {
|
||||
return selectedDiscussionUrl !== undefined;
|
||||
}, [selectedDiscussionUrl]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Box>
|
||||
<PageTitle name={`GBP: ${id} - NAME`} subtitle={`By: ${proposalProposer} | BONDED: $${proposalLocked} ${ghstSymbol}`} />
|
||||
<Container
|
||||
style={{
|
||||
paddingLeft: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
||||
paddingRight: isSmallScreen || isVerySmallScreen ? "0" : "3.3rem",
|
||||
minHeight: "calc(100vh - 128px)",
|
||||
display: "flex",
|
||||
flexDirection: "column",
|
||||
justifyContent: "center"
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mt: "15px" }}>
|
||||
<Box display="flex" justifyContent="space-between" gap="20px">
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="10px">
|
||||
<Typography variant="h6">
|
||||
Progress
|
||||
</Typography>
|
||||
<Chip
|
||||
sx={{ marginTop: "4px", width: "88px" }}
|
||||
label={proposalStatus}
|
||||
template={convertStatusToTemplate(proposalStatus)}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
topRight={
|
||||
<ProposalDiscussion onClick={() => setSelectedDiscussionUrl("dicks")} />
|
||||
}
|
||||
>
|
||||
<Box height="220px" display="flex" flexDirection="column" justifyContent="space-between" gap="20px">
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography variant="body2" color={theme.colors.feedback.success}>
|
||||
For: {formatNumber(forVotes.toString(), 2)} ({formatNumber(forVotes * HUNDRED / proposalQuorum, 1)}%)
|
||||
</Typography>
|
||||
<Typography variant="body2" color={theme.colors.feedback.error}>
|
||||
Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstVotes * HUNDRED / proposalQuorum, 1)}%)
|
||||
</Typography>
|
||||
</Box>
|
||||
<LinearProgressBar
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={69}
|
||||
target={Math.floor(Math.random() * 101)}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Box display="flex" flexDirection="row">
|
||||
<Typography>Quorum</Typography>
|
||||
<InfoTooltip message="Minimum number of voting power required to be present to make proposal executable" />
|
||||
</Box>
|
||||
<Typography>{formatNumber(proposalQuorum.toString(), 4)}</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Box display="flex" flexDirection="row">
|
||||
<Typography>Total</Typography>
|
||||
<InfoTooltip message="Total number of votes available for proposal" />
|
||||
</Box>
|
||||
<Typography>{formatNumber(totalSupply.toString(), 4)}</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Box display="flex" flexDirection="row">
|
||||
<Typography>Votes</Typography>
|
||||
<InfoTooltip message="Voting power of the connected wallet" />
|
||||
</Box>
|
||||
<Typography>{formatNumber(balance.toString(), 4)}</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" gap="20px">
|
||||
<SecondaryButton fullWidth onClick={() => alert("For vote casted")}>For</SecondaryButton>
|
||||
<SecondaryButton fullWidth onClick={() => alert("Against vote casted")}>Against</SecondaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
|
||||
<Typography variant="h6">
|
||||
Timeline
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<VotingTimeline chainId={chainId} proposalId={id} />
|
||||
</Paper>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
|
||||
<Typography variant="h6">
|
||||
Executable Code
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
Here will be a list of decoded calldatas
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const VotingTimeline = ({ proposalId, chainId }) => {
|
||||
const { delay: propsalVotingDelay } = useProposalVotingDelay(chainId, proposalId);
|
||||
const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId);
|
||||
const { deadline: proposalDeadline } = useProposalDeadline(chainId, proposalId);
|
||||
|
||||
const voteStarted = useMemo(() => {
|
||||
if (proposalSnapshot && propsalVotingDelay) {
|
||||
return proposalSnapshot > propsalVotingDelay ? proposalSnapshot - propsalVotingDelay : 0;
|
||||
}
|
||||
return 0;
|
||||
}, [proposalSnapshot, propsalVotingDelay]);
|
||||
|
||||
return (
|
||||
<Timeline sx={{ margin: 0, padding: 0 }}>
|
||||
<VotingTimelineItem time={voteStarted} message="Proposed on:" isFirst />
|
||||
<VotingTimelineItem time={proposalSnapshot} message="Voting started:" />
|
||||
<VotingTimelineItem time={proposalDeadline} message="Voting ends:" />
|
||||
</Timeline>
|
||||
)
|
||||
}
|
||||
|
||||
const VotingTimelineItem = ({ isFirst, isLast, time, message }) => {
|
||||
return (
|
||||
<TimelineItem>
|
||||
<TimelineOppositeContent sx={{ display: "none" }} />
|
||||
<TimelineSeparator>
|
||||
{!isFirst && <TimelineConnector sx={{ background: "#fff" }} />}
|
||||
<TimelineDot sx={{ width: "15px", height: "15px", background: "#fff" }}></TimelineDot>
|
||||
{!isLast && <TimelineConnector sx={{ background: "#fff" }} />}
|
||||
</TimelineSeparator>
|
||||
|
||||
<TimelineContent>
|
||||
<Typography>{message}</Typography>
|
||||
<Typography component="span">{new Date(time * 1000).toLocaleString()}</Typography>
|
||||
</TimelineContent>
|
||||
</TimelineItem>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProposalDetails;
|
||||
23
src/containers/Governance/components/GovernanceInfoText.jsx
Normal file
23
src/containers/Governance/components/GovernanceInfoText.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { Link, Typography, useTheme } from "@mui/material";
|
||||
|
||||
const GovernanceInfoText = () => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
fontSize="0.875em"
|
||||
lineHeight="15px"
|
||||
>
|
||||
ghostDAO's adaptive governance system algorithmically sets proposal threshold based on activity.
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="https://ghostchain.io/ghostdao_litepaper"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Learn more here.</Link>
|
||||
</Typography>
|
||||
)
|
||||
};
|
||||
|
||||
export default GovernanceInfoText;
|
||||
53
src/containers/Governance/components/Metric.jsx
Normal file
53
src/containers/Governance/components/Metric.jsx
Normal file
@ -0,0 +1,53 @@
|
||||
import Metric from "../../../components/Metric/Metric";
|
||||
|
||||
import { formatCurrency, formatNumber } from "../../../helpers";
|
||||
import {
|
||||
useMinQuorum,
|
||||
useProposalThreshold,
|
||||
useProposalCount
|
||||
} from "../../../hooks/governance";
|
||||
|
||||
export const MinQuorumPercentage = props => {
|
||||
const { percentage } = useMinQuorum(props.chainId);
|
||||
|
||||
const _props = {
|
||||
...props,
|
||||
label: `Min Quorum`,
|
||||
tooltip: `Minimum quorum needed for proposal to be succeeded.`,
|
||||
};
|
||||
|
||||
if (percentage) _props.metric = `${formatNumber(percentage, 2)}%`;
|
||||
else _props.isLoading = true;
|
||||
|
||||
return <Metric {..._props} />;
|
||||
};
|
||||
|
||||
export const ProposalThreshold = props => {
|
||||
const { threshold } = useProposalThreshold(props.chainId, props.ghstSymbol);
|
||||
|
||||
const _props = {
|
||||
...props,
|
||||
label: `$${props.ghstSymbol} Threshold`,
|
||||
tooltip: `Minimum $${props.ghstSymbol} amount to be locked to create proposal.`,
|
||||
};
|
||||
|
||||
if (threshold) _props.metric = `${formatCurrency(threshold.toString(), 0, props.ghstSymbol)}`;
|
||||
else _props.isLoading = true;
|
||||
|
||||
return <Metric {..._props} />;
|
||||
}
|
||||
|
||||
export const ProposalsCount = props => {
|
||||
const { proposalsCount } = useProposalCount(props.chainId);
|
||||
|
||||
const _props = {
|
||||
...props,
|
||||
label: `Proposals Count`,
|
||||
tooltip: `How much proposals already passed.`,
|
||||
};
|
||||
|
||||
if (proposalsCount) _props.metric = proposalsCount.toString();
|
||||
else _props.isLoading = true;
|
||||
|
||||
return <Metric {..._props} />;
|
||||
}
|
||||
29
src/containers/Governance/components/ProposalDiscussion.jsx
Normal file
29
src/containers/Governance/components/ProposalDiscussion.jsx
Normal file
@ -0,0 +1,29 @@
|
||||
import { Link } from "@mui/material";
|
||||
import GhostStyledIcon from "../../../components/Icon/GhostIcon";
|
||||
import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react";
|
||||
|
||||
const ProposalDiscussion = (linkProps) => {
|
||||
return (
|
||||
<Link
|
||||
{...linkProps}
|
||||
underline="hover"
|
||||
sx={{ fontFamily: "Ubuntu" }}
|
||||
display="flex"
|
||||
flexDirection="row"
|
||||
alignItems="center"
|
||||
alignContent="center"
|
||||
justifyContent={linkProps.isSmallScreen ? "start" : "center"}
|
||||
className="link-container"
|
||||
>
|
||||
Learn more
|
||||
<GhostStyledIcon
|
||||
style={{ marginTop: "7px" }}
|
||||
viewBox="0 0 30 30"
|
||||
className="external-site-link-icon"
|
||||
component={ArrowUpIcon}
|
||||
/>
|
||||
</Link>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProposalDiscussion;
|
||||
@ -0,0 +1,64 @@
|
||||
import { useState } from "react";
|
||||
import { Box, Typography, Link } from "@mui/material";
|
||||
|
||||
import Modal from "../../../components/Modal/Modal";
|
||||
import { PrimaryButton } from "../../../components/Button";
|
||||
|
||||
const ProposalDiscussionModal = ({ isOpened, closeModal, url }) => {
|
||||
const [isCopied, setIsCopied] = useState(false);
|
||||
|
||||
const copyToClipboard = () => {
|
||||
navigator.clipboard.writeText(url)
|
||||
.then(() => {
|
||||
isCopied(true);
|
||||
setTimeout(() => setIsCopied(false), 2000);
|
||||
})
|
||||
.catch(err => console.error(err));
|
||||
}
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerContent={
|
||||
<Box display="flex" justifyContent="center" alignItems="center" gap="15px">
|
||||
<Typography variant="h4">Discussion URL</Typography>
|
||||
</Box>
|
||||
}
|
||||
open={isOpened}
|
||||
onClose={closeModal}
|
||||
maxWidth="460px"
|
||||
minHeight="200px"
|
||||
>
|
||||
<Box display="flex" alignItems="center" justifyContent="center" flexDirection="column">
|
||||
<Box marginBottom="20px" display="flex" flexDirection="column" alignItems="center" gap="10px">
|
||||
<Typography align="center">
|
||||
You are leaving the ghost dao app. Check the link on your own, we are not in charge of your destiny.
|
||||
</Typography>
|
||||
<Link
|
||||
onClick={copyToClipboard}
|
||||
underline="hover"
|
||||
sx={{
|
||||
maxWidth: '360px',
|
||||
overflow: 'hidden',
|
||||
textOverflow: 'ellipsis',
|
||||
whiteSpace: 'nowrap',
|
||||
display: 'inline-block',
|
||||
verticalAlign: 'middle',
|
||||
fontStyle: 'italic'
|
||||
}}
|
||||
>
|
||||
{url}
|
||||
</Link>
|
||||
</Box>
|
||||
|
||||
<PrimaryButton
|
||||
fullWidth
|
||||
onClick={() => window.open(url, '_blank', 'noopener,noreferrer')}
|
||||
>
|
||||
Open
|
||||
</PrimaryButton>
|
||||
</Box>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProposalDiscussionModal;
|
||||
23
src/containers/Governance/components/ProposalInfoText.jsx
Normal file
23
src/containers/Governance/components/ProposalInfoText.jsx
Normal file
@ -0,0 +1,23 @@
|
||||
import { Link, Typography, useTheme } from "@mui/material";
|
||||
|
||||
const ProposalInfoText = () => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Typography
|
||||
variant="body2"
|
||||
color="textSecondary"
|
||||
fontSize="0.875em"
|
||||
lineHeight="15px"
|
||||
>
|
||||
Important: We display only the 10 most recent proposals. Only one proposal can be active at a time.
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="https://ghostchain.io/ghostdao_litepaper"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Learn more here.</Link>
|
||||
</Typography>
|
||||
)
|
||||
};
|
||||
|
||||
export default ProposalInfoText;
|
||||
316
src/containers/Governance/components/ProposalsList.jsx
Normal file
316
src/containers/Governance/components/ProposalsList.jsx
Normal file
@ -0,0 +1,316 @@
|
||||
import { useState, useMemo } from "react";
|
||||
import { useNavigate } from "react-router-dom";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Link,
|
||||
Tabs,
|
||||
Tab,
|
||||
Table,
|
||||
TableBody,
|
||||
TableCell,
|
||||
TableContainer,
|
||||
TableHead,
|
||||
TableRow,
|
||||
Typography,
|
||||
useTheme,
|
||||
useMediaQuery
|
||||
} from "@mui/material";
|
||||
import { NavLink } from "react-router-dom";
|
||||
import { getBlockNumber } from "@wagmi/core";
|
||||
|
||||
import { networkAvgBlockSpeed } from "../../../constants";
|
||||
import { prettifySecondsInDays } from "../../../helpers/timeUtil";
|
||||
|
||||
import Chip from "../../../components/Chip/Chip";
|
||||
import Modal from "../../../components/Modal/Modal";
|
||||
import Paper from "../../../components/Paper/Paper";
|
||||
import LinearProgressBar from "../../../components/Progress/LinearProgressBar";
|
||||
import { PrimaryButton, TertiaryButton } from "../../../components/Button";
|
||||
|
||||
import ProposalDiscussionModal from "./ProposalDiscussionModal";
|
||||
import ProposalDiscussion from "./ProposalDiscussion";
|
||||
import ProposalInfoText from "./ProposalInfoText";
|
||||
import { convertStatusToTemplate } from "../helpers";
|
||||
|
||||
import { useScreenSize } from "../../../hooks/useScreenSize";
|
||||
|
||||
import {
|
||||
useProposals,
|
||||
} from "../../../hooks/governance";
|
||||
|
||||
const MAX_PROPOSALS_TO_SHOW = 10;
|
||||
|
||||
const ProposalsList = ({ chainId, config }) => {
|
||||
const isSmallScreen = useScreenSize("md");
|
||||
const navigate = useNavigate();
|
||||
|
||||
const [blockNumber, setBlockNumber] = useState(0n);
|
||||
const [selectedDiscussionUrl, setSelectedDiscussionUrl] = useState(undefined);
|
||||
const [proposalsFilter, setProposalFilter] = useState("active");
|
||||
const { proposals } = useProposals(chainId, MAX_PROPOSALS_TO_SHOW);
|
||||
|
||||
getBlockNumber(config).then(block => setBlockNumber(block));
|
||||
|
||||
const isDiscussionModalOpened = useMemo(() => {
|
||||
return selectedDiscussionUrl !== undefined;
|
||||
}, [selectedDiscussionUrl]);
|
||||
|
||||
const filteredProposals = useMemo(() => {
|
||||
switch (proposalsFilter) {
|
||||
case "voted":
|
||||
return proposals.filter(obj => obj.status === "Succeeded" || obj.status === "Defeated");
|
||||
case "created":
|
||||
return proposals.filter(obj => obj.status === "Executed");
|
||||
default:
|
||||
return proposals;
|
||||
}
|
||||
}, [proposals, proposalsFilter]);
|
||||
|
||||
if (proposals?.length === 0) {
|
||||
return (
|
||||
<Box display="flex" justifyContent="center">
|
||||
<Typography variant="h4">No proposals yet</Typography>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
if (isSmallScreen) {
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Paper headerText="Proposals" fullWidth enableBackground>
|
||||
<Box my="24px" textAlign="center">
|
||||
<ProposalInfoText />
|
||||
</Box>
|
||||
|
||||
<Box display="flex" flexDirection="column" gap="40px">
|
||||
{filteredProposals?.map(proposal => (
|
||||
<ProposalCard
|
||||
key={proposal.id}
|
||||
proposal={proposal}
|
||||
setActive={setSelectedDiscussionUrl}
|
||||
blockNumber={blockNumber}
|
||||
chainId={chainId}
|
||||
openProposal={() => navigate(`/governance/${proposal.id}`)}
|
||||
/>
|
||||
))}
|
||||
</Box>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Paper headerText="Proposals" fullWidth enableBackground>
|
||||
<ProposalFilterTrigger trigger={proposalsFilter} setTrigger={setProposalFilter} />
|
||||
|
||||
<ProposalTable>
|
||||
{filteredProposals?.map(proposal => (
|
||||
<ProposalRow
|
||||
key={proposal.id}
|
||||
proposal={proposal}
|
||||
setActive={setSelectedDiscussionUrl}
|
||||
blockNumber={blockNumber}
|
||||
chainId={chainId}
|
||||
openProposal={() => navigate(`/governance/${proposal.id}`)}
|
||||
/>
|
||||
))}
|
||||
</ProposalTable>
|
||||
|
||||
<Box mt="24px" textAlign="center" width="70%" mx="auto">
|
||||
<ProposalInfoText />
|
||||
</Box>
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
const ProposalTable = ({ children }) => (
|
||||
<TableContainer>
|
||||
<Table aria-label="Available bonds" style={{ tableLayout: "fixed" }}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell style={{ width: "100px", padding: "8px 0" }}>Proposal ID</TableCell>
|
||||
<TableCell align="center" style={{ width: "120px", padding: "8px 0" }}>Status</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Discussion</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Vote Ends</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Voting Stats</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
<TableBody>{children}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
);
|
||||
|
||||
const ProposalRow = ({ proposal, setActive, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
<TableRow id={proposal.id + `--proposal`} data-testid={proposal.id + `--proposal`}>
|
||||
<TableCell style={{ padding: "8px 0" }}>
|
||||
<Typography>GDP-{proposal.id}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Chip
|
||||
sx={{ width: "88px" }}
|
||||
label={proposal.status}
|
||||
template={convertStatusToTemplate(proposal.status)}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<ProposalDiscussion onClick={() => setActive(proposal.discussion)} />
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>
|
||||
{convertVoteEnds(
|
||||
proposal.id % 2n === 0n,
|
||||
proposal.voteEnds,
|
||||
blockNumber,
|
||||
chainId
|
||||
)}
|
||||
</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Box marginLeft="15px" marginRight="15px">
|
||||
<LinearProgressBar
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={69}
|
||||
target={Math.floor(Math.random() * 101)}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
{(proposal.status === "Active" || proposal.status === "Succeeded") && <PrimaryButton
|
||||
fullWidth
|
||||
onClick={() => openProposal()}
|
||||
sx={{ maxWidth: "150px" }}
|
||||
>
|
||||
{proposal.status === "Succeeded" ? "Execute" : "Vote"}
|
||||
</PrimaryButton>}
|
||||
{(proposal.status !== "Active" && proposal.status !== "Succeeded") && <TertiaryButton
|
||||
fullWidth
|
||||
onClick={() => openProposal()}
|
||||
sx={{ maxWidth: "150px" }}
|
||||
>
|
||||
View
|
||||
</TertiaryButton>}
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
);
|
||||
}
|
||||
|
||||
const ProposalCard = ({ proposal, setActive, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 450px)');
|
||||
|
||||
return (
|
||||
<Box id={proposal.id + `--proposal`} data-testid={proposal.id + `--proposal`}>
|
||||
<Box display="flex" flexDirection={isSmallScreen ? "column" : "row"} justifyContent="space-between">
|
||||
<Box display="flex" flexDirection="column" width="100%">
|
||||
<Box display="flex" flexDirection="row" alignItems="center" width="100%" gap="10px">
|
||||
<Typography variant="h3">GIP-{proposal.id}</Typography>
|
||||
<Chip
|
||||
sx={{ width: "88px" }}
|
||||
label={proposal.status}
|
||||
template={convertStatusToTemplate(proposal.status)}
|
||||
/>
|
||||
</Box>
|
||||
<Typography>
|
||||
{convertVoteEnds(
|
||||
proposal.id % 2n === 0n,
|
||||
proposal.voteEnds,
|
||||
blockNumber,
|
||||
chainId
|
||||
)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box width="150px">
|
||||
<ProposalDiscussion
|
||||
isSmallScreen={isSmallScreen}
|
||||
onClick={() => setActive(proposal.discussion)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box marginTop="15px" marginBottom="15px">
|
||||
<LinearProgressBar
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={69}
|
||||
target={Math.floor(Math.random() * 101)}
|
||||
/>
|
||||
</Box>
|
||||
<Box marginBottom="20px">
|
||||
{(proposal.status === "Active" || proposal.status === "Succeeded") && <PrimaryButton
|
||||
fullWidth
|
||||
onClick={() => openProposal()}
|
||||
>
|
||||
{proposal.status === "Succeeded" ? "Execute" : "Vote"}
|
||||
</PrimaryButton>}
|
||||
{(proposal.status !== "Active" && proposal.status !== "Succeeded") && <TertiaryButton
|
||||
fullWidth
|
||||
onClick={() => openProposal()}
|
||||
>
|
||||
View
|
||||
</TertiaryButton>}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
const ProposalFilterTrigger = ({ trigger, setTrigger }) => {
|
||||
return (
|
||||
<Tabs
|
||||
centered
|
||||
textColor="primary"
|
||||
indicatorColor="primary"
|
||||
value={trigger}
|
||||
aria-label="Proposal filter tabs"
|
||||
onChange={(_, view) => setTrigger(view)}
|
||||
TabIndicatorProps={{ style: { display: "none" } }}
|
||||
>
|
||||
<Tab aria-label="proposal-filter-active-button" value="active" label="Active" style={{ fontSize: "1rem" }} />
|
||||
<Tab aria-label="proposal-filter-voted-button" value="voted" label="Voted" style={{ fontSize: "1rem" }} />
|
||||
<Tab aria-label="proposal-filter-created-button" value="created" label="Created" style={{ fontSize: "1rem" }} />
|
||||
</Tabs>
|
||||
)
|
||||
}
|
||||
|
||||
const convertVoteEnds = (tmp, voteEnds, blockNumber, chainId) => {
|
||||
const tmpVoteSeconds = Number(voteEnds * networkAvgBlockSpeed(chainId));
|
||||
const tmpSeconds = (tmp ? tmpVoteSeconds : -tmpVoteSeconds);
|
||||
|
||||
const result = prettifySecondsInDays(tmpSeconds);
|
||||
if (result === "now") {
|
||||
return new Date(Date.now()).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
month: 'short',
|
||||
day: 'numeric'
|
||||
});
|
||||
}
|
||||
|
||||
return `in ${result}`;
|
||||
}
|
||||
|
||||
export default ProposalsList;
|
||||
14
src/containers/Governance/helpers.js
Normal file
14
src/containers/Governance/helpers.js
Normal file
@ -0,0 +1,14 @@
|
||||
export const convertStatusToTemplate = (status) => {
|
||||
switch (status.toUpperCase()) {
|
||||
case "EXECUTED":
|
||||
return 'info';
|
||||
case "CANCELED":
|
||||
return 'warning';
|
||||
case "SUCCEEDED":
|
||||
return 'success';
|
||||
case "DEFEATED":
|
||||
return 'error';
|
||||
default:
|
||||
return 'info';
|
||||
}
|
||||
}
|
||||
@ -1,5 +1,5 @@
|
||||
import { Dispatch, SetStateAction, useState, useEffect } from "react";
|
||||
import { Box, Container, Grid, Divider, Typography, Link, useMediaQuery, useTheme } from "@mui/material";
|
||||
import { Box, Container, Grid, Divider, Typography, useMediaQuery, useTheme } from "@mui/material";
|
||||
import ReactGA from "react-ga4";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
|
||||
108
src/hooks/governance/index.js
Normal file
108
src/hooks/governance/index.js
Normal file
@ -0,0 +1,108 @@
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { getTokenDecimals } from "../helpers";
|
||||
|
||||
export const useMinQuorum = (chainId) => {
|
||||
const numerator = 69n;
|
||||
const denominator = 100n;
|
||||
|
||||
let percentage = 0;
|
||||
|
||||
if (numerator && denominator && denominator > 0n) {
|
||||
percentage = Number(100n * numerator / denominator) / 100;
|
||||
}
|
||||
|
||||
return { numerator, denominator, percentage }
|
||||
}
|
||||
|
||||
export const useProposalThreshold = (chainId, name) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const threshold = new DecimalBigNumber(420_000_000_000_000_000_000n, decimals);
|
||||
|
||||
return { threshold };
|
||||
}
|
||||
|
||||
export const useProposalCount = (chainId) => {
|
||||
const proposalsCount = 1337n;
|
||||
return { proposalsCount };
|
||||
}
|
||||
|
||||
export const useProposalStatus = (chainId, proposalId) => {
|
||||
const status = "Succeeded";
|
||||
return { status };
|
||||
}
|
||||
|
||||
export const useProposalProposer = (chainId, proposalId) => {
|
||||
const proposer = "0x71C7656EC7ab88b098defB751B7401B5f6d8976F";
|
||||
return { proposer };
|
||||
}
|
||||
|
||||
export const useProposalLocked = (chainId, proposalId) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const locked = new DecimalBigNumber(420_000_000_000_000_000_000n, decimals);
|
||||
return { locked }
|
||||
}
|
||||
|
||||
export const useProposalQuorum = (chainId, proposalId) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const quorum = new DecimalBigNumber(1337_000_000_000_000_000_000n, decimals);
|
||||
return { quorum }
|
||||
}
|
||||
|
||||
export const useProposalVotes = (chainId, proposalId) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const forVotes = new DecimalBigNumber(420_000_000_000_000_000_000n, decimals);
|
||||
const againstVotes = new DecimalBigNumber(69_000_000_000_000_000_000n, decimals);
|
||||
return { forVotes, againstVotes }
|
||||
}
|
||||
|
||||
export const useProposalSnapshot = (chainId, proposalId) => {
|
||||
const snapshot = Math.floor((Date.now() - (3 * 24 * 60 * 60 * 1000)) / 1000);
|
||||
return { snapshot };
|
||||
}
|
||||
|
||||
export const useProposalDeadline = (chainId, proposalId) => {
|
||||
const deadline = Math.floor(Date.now() / 1000);
|
||||
return { deadline };
|
||||
}
|
||||
|
||||
export const useProposalVotingDelay = (chainId, proposalId) => {
|
||||
const delay = 1;
|
||||
return { delay };
|
||||
}
|
||||
|
||||
export const useProposals = (chainId, depth) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const { proposalsCount } = useProposalCount(chainId);
|
||||
|
||||
let iterator = proposalsCount ? proposalsCount : 0n;
|
||||
const bigIntDepth = BigInt(depth);
|
||||
const edgeProposalId = iterator > bigIntDepth ? iterator - bigIntDepth : 0n;
|
||||
|
||||
const statuses = [
|
||||
"Active",
|
||||
"Executed",
|
||||
"Canceled",
|
||||
"Succeeded",
|
||||
"Defeated"
|
||||
];
|
||||
let proposals = [];
|
||||
|
||||
while (iterator > proposalsCount - bigIntDepth) {
|
||||
iterator -= 1n;
|
||||
|
||||
const voteEnds = 50n;
|
||||
const yesVotes = new DecimalBigNumber(1337_000_000_000_000_000_000, decimals);
|
||||
const noVotes = new DecimalBigNumber(420_000_000_000_000_000_000, decimals);
|
||||
|
||||
proposals.push({
|
||||
id: iterator,
|
||||
discussion: "https://google.com",
|
||||
status: statuses[Number(iterator) % statuses.length],
|
||||
voteEnds,
|
||||
yesVotes,
|
||||
noVotes
|
||||
});
|
||||
}
|
||||
|
||||
return { proposals };
|
||||
}
|
||||
Loading…
Reference in New Issue
Block a user