new functionality for governance added
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
parent
056177c34b
commit
56c5616d96
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.5.7",
|
||||
"version": "0.5.8",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
72
src/components/Select/Select.jsx
Normal file
72
src/components/Select/Select.jsx
Normal file
@ -0,0 +1,72 @@
|
||||
import { Box, MenuItem, Select as MuiSelect, Typography, useTheme } from "@mui/material";
|
||||
import { styled } from "@mui/material/styles";
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
|
||||
const StyledSelectInput = styled(MuiSelect, {
|
||||
shouldForwardProp: prop => prop !== "inputWidth"
|
||||
})(({ theme, inputWidth }) => ({
|
||||
width: "100%",
|
||||
"& .MuiSelect-select": {
|
||||
padding: 0,
|
||||
height: "24px !important",
|
||||
minHeight: "24px !important",
|
||||
display: "flex",
|
||||
alignItems: "center",
|
||||
fontSize: "18px",
|
||||
fontWeight: 500,
|
||||
paddingRight: "24px",
|
||||
},
|
||||
"& .MuiOutlinedInput-notchedOutline": {
|
||||
border: "none",
|
||||
},
|
||||
"& .MuiSelect-icon": {
|
||||
right: 0,
|
||||
position: "absolute",
|
||||
color: theme.colors.gray[500],
|
||||
}
|
||||
}));
|
||||
|
||||
const Select = ({
|
||||
label,
|
||||
value,
|
||||
onChange,
|
||||
options,
|
||||
inputWidth,
|
||||
width = "100%",
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
width={width}
|
||||
sx={{ backgroundColor: theme.colors.gray[750] }}
|
||||
borderRadius="12px"
|
||||
padding="15px"
|
||||
>
|
||||
{label && (
|
||||
<Typography color={theme.colors.gray[500]} fontSize="12px" marginBottom="8px">
|
||||
{label}
|
||||
</Typography>
|
||||
)}
|
||||
|
||||
<Box display="flex" alignItems="center" justifyContent="space-between">
|
||||
<StyledSelectInput
|
||||
value={value}
|
||||
onChange={onChange}
|
||||
inputWidth={inputWidth}
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
displayEmpty
|
||||
>
|
||||
{options.map((opt) => (
|
||||
<MenuItem key={opt.value} value={opt.value}>
|
||||
{opt.label}
|
||||
</MenuItem>
|
||||
))}
|
||||
</StyledSelectInput>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
};
|
||||
|
||||
export default Select;
|
||||
@ -7,7 +7,7 @@ import Token from "../Token/Token";
|
||||
|
||||
import KeyboardArrowDownIcon from '@mui/icons-material/KeyboardArrowDown';
|
||||
|
||||
const StyledInputBase = styled(InputBase, { shouldForwardProp: prop => prop !== "inputWidth" })(
|
||||
export const StyledInputBase = styled(InputBase, { shouldForwardProp: prop => prop !== "inputWidth" })(
|
||||
({ inputWidth, inputFontSize }) => ({
|
||||
"& .MuiInputBase-input": {
|
||||
padding: 0,
|
||||
|
||||
@ -1,4 +1,4 @@
|
||||
import { Box, Tab, Tabs, Container, useMediaQuery } from "@mui/material";
|
||||
import { Box, Tab, Tabs, Typography, Container, useMediaQuery } from "@mui/material";
|
||||
import { useEffect, useState } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
|
||||
|
||||
@ -1,27 +1,59 @@
|
||||
import { useState } from "react";
|
||||
import { useState, useMemo } from "react";
|
||||
|
||||
import {
|
||||
Box,
|
||||
Container,
|
||||
TableContainer,
|
||||
Table,
|
||||
TableRow,
|
||||
TableBody,
|
||||
TableHead,
|
||||
TableCell,
|
||||
Typography,
|
||||
Link,
|
||||
OutlinedInput,
|
||||
InputLabel,
|
||||
FormControl,
|
||||
useMediaQuery,
|
||||
useTheme
|
||||
} from "@mui/material";
|
||||
|
||||
import GhostStyledIcon from "../../components/Icon/GhostIcon";
|
||||
import ArrowUpIcon from "../../assets/icons/arrow-up.svg?react";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
import { PrimaryButton } from "../../components/Button";
|
||||
import { PrimaryButton, TertiaryButton } from "../../components/Button";
|
||||
|
||||
import ProposalModal from "./components/ProposalModal";
|
||||
import { parseFunctionCalldata } from "./components/functions/index";
|
||||
|
||||
const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
const isSemiSmallScreen = useMediaQuery("(max-width: 745px)");
|
||||
const isSmallScreen = useMediaQuery("(max-width: 650px)");
|
||||
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
||||
|
||||
const [descriptionUrl, setDescriptionUrl] = useState("");
|
||||
const theme = useTheme();
|
||||
|
||||
const [isModalOpened, setIsModalOpened] = useState(false);
|
||||
const [proposalFunctions, setProposalFunctions] = useState([]);
|
||||
|
||||
const addCalldata = (calldata) => setProposalFunctions(prev => [...prev, calldata]);
|
||||
const removeCalldata = (index) => setProposalFunctions(prev => prev.filter((_, i) => i !== index));
|
||||
|
||||
const nativeCurrency = useMemo(() => {
|
||||
const client = config?.getClient();
|
||||
return client?.chain?.nativeCurrency?.symbol;
|
||||
}, [config]);
|
||||
|
||||
const submitProposal = () => {
|
||||
alert("Proposal created");
|
||||
setProposalFunctions([]);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProposalModal addCalldata={addCalldata} isOpened={isModalOpened} closeModal={() => setIsModalOpened(false)} />
|
||||
<Box>
|
||||
<PageTitle name="Create Proposal" subtitle="Cool text describing what's goinf on there" />
|
||||
<Container
|
||||
@ -41,35 +73,81 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
|
||||
<Typography variant="h6">
|
||||
Proposal Overview
|
||||
Proposal Functions
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<BasicInput id="discussion" label="Discussion URL" value={descriptionUrl} eventHandler={setDescriptionUrl} />
|
||||
<Box>
|
||||
<Box>
|
||||
{proposalFunctions.length === 0 && <Box
|
||||
width="100%"
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
marginBottom="50px"
|
||||
marginTop="25px"
|
||||
>
|
||||
<Typography variant="subtitle1">
|
||||
Create new proposal by adding functions below
|
||||
</Typography>
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="https://forum.ghostchain.io"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
>
|
||||
Learn more
|
||||
<GhostStyledIcon
|
||||
style={{ marginTop: "7px" }}
|
||||
viewBox="0 0 30 30"
|
||||
className="external-site-link-icon"
|
||||
component={ArrowUpIcon}
|
||||
/>
|
||||
</Link>
|
||||
</Box>}
|
||||
|
||||
</Box>
|
||||
<Box>
|
||||
<TertiaryButton fullWidth onClick={() => setIsModalOpened(true)}>Add New</TertiaryButton>
|
||||
<PrimaryButton fullWidth onClick={() => submitProposal()}>Submit Proposal</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
</Paper>
|
||||
|
||||
{proposalFunctions.length > 0 && <Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
|
||||
<Typography variant="h6">
|
||||
Proposal Functions
|
||||
</Typography>
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
<TableContainer>
|
||||
<Table aria-label="Available bonds" style={{ tableLayout: "fixed" }}>
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Function</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Target</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{proposalFunctions.map((calldata, index) => {
|
||||
return parseFunctionCalldata(calldata, index, nativeCurrency, removeCalldata);
|
||||
})}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>}
|
||||
</Box>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const BasicInput = ({ id, label, value, eventHandler }) => {
|
||||
return (
|
||||
<Box>
|
||||
<InputLabel htmlFor={id}>{label}</InputLabel>
|
||||
<Box mt="8px">
|
||||
<FormControl variant="outlined" color="primary" fullWidth>
|
||||
<OutlinedInput
|
||||
inputProps={{ "data-testid": `${id}-dex` }}
|
||||
type="text"
|
||||
id={`${id}-dex`}
|
||||
value={value}
|
||||
onChange={event => eventHandler(event.currentTarget.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -2,7 +2,7 @@ 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 { Box, Container, Typography, Link, useMediaQuery, useTheme } from "@mui/material";
|
||||
|
||||
import Paper from "../../components/Paper/Paper";
|
||||
import PageTitle from "../../components/PageTitle/PageTitle";
|
||||
@ -11,12 +11,13 @@ import InfoTooltip from "../../components/Tooltip/InfoTooltip";
|
||||
import Chip from "../../components/Chip/Chip";
|
||||
import { SecondaryButton } from "../../components/Button";
|
||||
|
||||
import GhostStyledIcon from "../../components/Icon/GhostIcon";
|
||||
import ArrowUpIcon from "../../assets/icons/arrow-up.svg?react";
|
||||
|
||||
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";
|
||||
@ -65,7 +66,7 @@ const ProposalDetails = ({ chainId, address }) => {
|
||||
const { proposer: proposalProposer } = useProposalProposer(chainId, id);
|
||||
const { locked: proposalLocked } = useProposalLocked(chainId, id);
|
||||
const { quorum: proposalQuorum } = useProposalQuorum(chainId, id);
|
||||
const { forVotes, againstVotes } = useProposalVotes(chainId, id);
|
||||
const { forVotes, againstVotes, totalVotes } = useProposalVotes(chainId, id);
|
||||
|
||||
useEffect(() => {
|
||||
ReactGA.send({ hitType: "pageview", page: `/governance/${id}` });
|
||||
@ -75,13 +76,22 @@ const ProposalDetails = ({ chainId, address }) => {
|
||||
return selectedDiscussionUrl !== undefined;
|
||||
}, [selectedDiscussionUrl]);
|
||||
|
||||
const quorumPercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return 0;
|
||||
return proposalQuorum / totalSupply * HUNDRED;
|
||||
}, [proposalQuorum, totalSupply]);
|
||||
|
||||
const votePercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return 0;
|
||||
return totalVotes / totalSupply * HUNDRED;
|
||||
}, [totalVotes, totalSupply]);
|
||||
|
||||
const voteWeightPercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return 0;
|
||||
return balance / totalSupply * HUNDRED;
|
||||
}, [balance, totalSupply]);
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Box>
|
||||
<PageTitle name={`GBP: ${id} - NAME`} subtitle={`By: ${proposalProposer} | BONDED: $${proposalLocked} ${ghstSymbol}`} />
|
||||
<Container
|
||||
@ -112,16 +122,31 @@ const ProposalDetails = ({ chainId, address }) => {
|
||||
</Box>
|
||||
}
|
||||
topRight={
|
||||
<ProposalDiscussion onClick={() => setSelectedDiscussionUrl("dicks")} />
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="https://forum.ghostchain.io"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
>
|
||||
View Forum
|
||||
<GhostStyledIcon
|
||||
style={{ marginTop: "7px" }}
|
||||
viewBox="0 0 30 30"
|
||||
className="external-site-link-icon"
|
||||
component={ArrowUpIcon}
|
||||
/>
|
||||
</Link>
|
||||
}
|
||||
>
|
||||
<Box height="200px" display="flex" flexDirection="column" justifyContent="space-between" gap="20px">
|
||||
<Box display="flex" flexDirection="column">
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body2" color={theme.colors.feedback.success}>
|
||||
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.success}>
|
||||
For: {formatNumber(forVotes.toString(), 2)} ({formatNumber(forVotes * HUNDRED / proposalQuorum, 1)}%)
|
||||
</Typography>
|
||||
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body2" color={theme.colors.feedback.error}>
|
||||
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.error}>
|
||||
Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstVotes * HUNDRED / proposalQuorum, 1)}%)
|
||||
</Typography>
|
||||
</Box>
|
||||
@ -138,25 +163,25 @@ const ProposalDetails = ({ chainId, address }) => {
|
||||
<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" />
|
||||
<InfoTooltip message={`Minimum $${ghstSymbol} turnout required for the proposal to become valid, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`} />
|
||||
</Box>
|
||||
<Typography>{formatNumber(proposalQuorum.toString(), 4)}</Typography>
|
||||
<Typography>{formatNumber(proposalQuorum.toString(), 4)} ({formatNumber(quorumPercentage, 1)}%)</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" />
|
||||
<InfoTooltip message={`Total votes for the proposal, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`}/>
|
||||
</Box>
|
||||
<Typography>{formatNumber(totalSupply.toString(), 4)}</Typography>
|
||||
<Typography>{formatNumber(totalSupply.toString(), 4)} ({formatNumber(votePercentage, 1)}%)</Typography>
|
||||
</Box>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Box display="flex" flexDirection="row">
|
||||
<Typography>Votes</Typography>
|
||||
<InfoTooltip message="Voting power of the connected wallet" />
|
||||
<InfoTooltip message={`Your voting power, as percentage of total $${ghstSymbol} at the time when proposal was created`} />
|
||||
</Box>
|
||||
<Typography>{formatNumber(balance.toString(), 4)}</Typography>
|
||||
<Typography>{formatNumber(balance.toString(), 4)} ({formatNumber(voteWeightPercentage, 1)}%)</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -198,7 +223,6 @@ const ProposalDetails = ({ chainId, address }) => {
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@ -37,7 +37,7 @@ export const ProposalThreshold = props => {
|
||||
|
||||
const _props = {
|
||||
...props,
|
||||
label: `$${props.ghstSymbol} Threshold`,
|
||||
label: "Min Collateral",
|
||||
tooltip: `Minimum $${props.ghstSymbol} required to be locked to create a proposal`,
|
||||
};
|
||||
|
||||
@ -56,7 +56,7 @@ export const ProposalsCount = props => {
|
||||
tooltip: `Total proposals created`,
|
||||
};
|
||||
|
||||
if (proposalsCount) _props.metric = proposalsCount.toString();
|
||||
if (proposalsCount) _props.metric = `${formatNumber(proposalsCount.toString(), 0)}`;
|
||||
else _props.isLoading = true;
|
||||
|
||||
return <Metric {..._props} />;
|
||||
|
||||
@ -1,29 +0,0 @@
|
||||
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", fontSize: "14px", marginLeft: "9px" }}
|
||||
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;
|
||||
@ -1,64 +0,0 @@
|
||||
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;
|
||||
100
src/containers/Governance/components/ProposalModal.jsx
Normal file
100
src/containers/Governance/components/ProposalModal.jsx
Normal file
@ -0,0 +1,100 @@
|
||||
import { useState, useEffect, useMemo, useCallback } from "react";
|
||||
import { Box, Typography } from "@mui/material";
|
||||
|
||||
import Modal from "../../../components/Modal/Modal";
|
||||
import Select from "../../../components/Select/Select";
|
||||
import { PrimaryButton, TertiaryButton } from "../../../components/Button";
|
||||
|
||||
import {
|
||||
getFunctionArguments,
|
||||
getFunctionCalldata,
|
||||
getFunctionDescription,
|
||||
allPossibleFunctions
|
||||
} from "./functions";
|
||||
|
||||
const ProposalModal = ({ isOpened, closeModal, addCalldata }) => {
|
||||
const [selectedOption, setSelectedOption] = useState();
|
||||
const [renderArguments, setRenderArguments] = useState(false);
|
||||
|
||||
const handleChange = (event) => {
|
||||
setSelectedOption(event.target.value);
|
||||
};
|
||||
|
||||
const headerLabel = useMemo(() => {
|
||||
const data = allPossibleFunctions.find(obj => obj.value === selectedOption);
|
||||
if (data && renderArguments) {
|
||||
return data.label;
|
||||
}
|
||||
return "Executable Code";
|
||||
}, [selectedOption, renderArguments]);
|
||||
|
||||
const handleCalldata = useCallback(() => {
|
||||
addCalldata(getFunctionCalldata(selectedOption));
|
||||
setSelectedOption(null);
|
||||
closeModal();
|
||||
}, [selectedOption]);
|
||||
|
||||
const handleClose = () => {
|
||||
setSelectedOption(null);
|
||||
setRenderArguments(false);
|
||||
closeModal();
|
||||
}
|
||||
|
||||
const handleAddCalldata = (calldata) => {
|
||||
addCalldata(calldata);
|
||||
handleClose();
|
||||
}
|
||||
|
||||
const ArgumentsSteps = useMemo(() => getFunctionArguments(selectedOption), [selectedOption]);
|
||||
|
||||
return (
|
||||
<Modal
|
||||
headerContent={
|
||||
<Box display="flex" justifyContent="center" alignItems="center" gap="15px">
|
||||
<Typography variant="h4">{headerLabel}</Typography>
|
||||
</Box>
|
||||
}
|
||||
open={isOpened}
|
||||
onClose={handleClose}
|
||||
maxWidth="460px"
|
||||
minHeight="200px"
|
||||
>
|
||||
<Box minHeight="220px" display="flex" alignItems="start" justifyContent="space-between" flexDirection="column">
|
||||
{renderArguments
|
||||
? <ArgumentsSteps
|
||||
addCalldata={handleAddCalldata}
|
||||
toInitialStep={() => setRenderArguments(false)}
|
||||
/>
|
||||
: <InitialStep
|
||||
selectedOption={selectedOption}
|
||||
handleChange={handleChange}
|
||||
handleCalldata={handleCalldata}
|
||||
handleProceed={() => setRenderArguments(true)}
|
||||
ready={ArgumentsSteps !== null}
|
||||
/>
|
||||
}
|
||||
</Box>
|
||||
</Modal>
|
||||
)
|
||||
}
|
||||
|
||||
const InitialStep = ({ selectedOption, handleChange, handleCalldata, handleProceed, ready }) => {
|
||||
const functionDescription = useMemo(() => getFunctionDescription(selectedOption), [selectedOption]);
|
||||
return (
|
||||
<Box minHeight="220px" display="flex" alignItems="start" justifyContent="space-between" flexDirection="column">
|
||||
<Select
|
||||
value={selectedOption ?? ""}
|
||||
onChange={handleChange}
|
||||
options={allPossibleFunctions}
|
||||
inputWidth="100%"
|
||||
/>
|
||||
<Typography align="center" variant="body2">{functionDescription}</Typography>
|
||||
{ready
|
||||
? <TertiaryButton disabled={!selectedOption} onClick={() => handleProceed()} fullWidth>Proceed</TertiaryButton>
|
||||
: <PrimaryButton disabled={!selectedOption} onClick={() => handleCalldata()} fullWidth>Create Function</PrimaryButton>
|
||||
}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export default ProposalModal;
|
||||
@ -18,6 +18,9 @@ import {
|
||||
} from "@mui/material";
|
||||
import { getBlockNumber } from "@wagmi/core";
|
||||
|
||||
import GhostStyledIcon from "../../../components/Icon/GhostIcon";
|
||||
import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react";
|
||||
|
||||
import { networkAvgBlockSpeed } from "../../../constants";
|
||||
import { prettifySecondsInDays } from "../../../helpers/timeUtil";
|
||||
|
||||
@ -27,8 +30,6 @@ 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";
|
||||
|
||||
@ -46,16 +47,11 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
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":
|
||||
@ -77,12 +73,6 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
|
||||
if (isSmallScreen) {
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
@ -99,7 +89,6 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
<ProposalCard
|
||||
key={proposal.id}
|
||||
proposal={proposal}
|
||||
setActive={setSelectedDiscussionUrl}
|
||||
blockNumber={blockNumber}
|
||||
chainId={chainId}
|
||||
openProposal={() => navigate(`/governance/${proposal.id}`)}
|
||||
@ -111,17 +100,10 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
<ProposalInfoText />
|
||||
</Box>}
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
return (
|
||||
<>
|
||||
<ProposalDiscussionModal
|
||||
url={selectedDiscussionUrl}
|
||||
isOpened={isDiscussionModalOpened}
|
||||
closeModal={() => setSelectedDiscussionUrl(undefined)}
|
||||
/>
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
@ -140,13 +122,19 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="https://ghostchain.io/ghostdao_litepaper"
|
||||
href="https://forum.ghostchain.io"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
display="flex"
|
||||
alignItems="center"
|
||||
>
|
||||
<Typography variant="h7">
|
||||
Discussion Forum
|
||||
</Typography>
|
||||
View Forum
|
||||
<GhostStyledIcon
|
||||
style={{ marginTop: "7px" }}
|
||||
viewBox="0 0 30 30"
|
||||
className="external-site-link-icon"
|
||||
component={ArrowUpIcon}
|
||||
/>
|
||||
</Link>
|
||||
</Box>
|
||||
}
|
||||
@ -158,7 +146,6 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
<ProposalRow
|
||||
key={proposal.id}
|
||||
proposal={proposal}
|
||||
setActive={setSelectedDiscussionUrl}
|
||||
blockNumber={blockNumber}
|
||||
chainId={chainId}
|
||||
openProposal={() => navigate(`/governance/${proposal.id}`)}
|
||||
@ -170,7 +157,6 @@ const ProposalsList = ({ chainId, config }) => {
|
||||
<ProposalInfoText />
|
||||
</Box>}
|
||||
</Paper>
|
||||
</>
|
||||
);
|
||||
}
|
||||
|
||||
@ -180,8 +166,7 @@ const ProposalTable = ({ children }) => (
|
||||
<TableHead>
|
||||
<TableRow>
|
||||
<TableCell align="center" 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" }}>Status</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>
|
||||
@ -193,7 +178,7 @@ const ProposalTable = ({ children }) => (
|
||||
</TableContainer>
|
||||
);
|
||||
|
||||
const ProposalRow = ({ proposal, setActive, blockNumber, openProposal, chainId }) => {
|
||||
const ProposalRow = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
return (
|
||||
@ -204,16 +189,12 @@ const ProposalRow = ({ proposal, setActive, blockNumber, openProposal, chainId }
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Chip
|
||||
sx={{ width: "88px" }}
|
||||
sx={{ width: "100px" }}
|
||||
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(
|
||||
@ -257,7 +238,7 @@ const ProposalRow = ({ proposal, setActive, blockNumber, openProposal, chainId }
|
||||
);
|
||||
}
|
||||
|
||||
const ProposalCard = ({ proposal, setActive, blockNumber, openProposal, chainId }) => {
|
||||
const ProposalCard = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 450px)');
|
||||
|
||||
@ -282,12 +263,6 @@ const ProposalCard = ({ proposal, setActive, blockNumber, openProposal, chainId
|
||||
)}
|
||||
</Typography>
|
||||
</Box>
|
||||
<Box width="150px">
|
||||
<ProposalDiscussion
|
||||
isSmallScreen={isSmallScreen}
|
||||
onClick={() => setActive(proposal.discussion)}
|
||||
/>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box marginTop="15px" marginBottom="15px">
|
||||
|
||||
@ -0,0 +1,45 @@
|
||||
import { Box, Typography, TableCell, TableRow, useTheme } from "@mui/material";
|
||||
import { TertiaryButton } from "../../../../components/Button";
|
||||
|
||||
export const prepareAuditReservesCalldata = (chainId) => {
|
||||
return "0xauditReserves";
|
||||
}
|
||||
|
||||
export const prepareAuditReservesDescription = "Explanation about what is audit reserves, pros and cons should be here, we need to fill as much text here as possible to make initial screen usable and not empty.";
|
||||
|
||||
export const AuditReservesSteps = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
export const AuditReservesParsed = (props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isTable && <AuditReservesParsedCell {...props} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const AuditReservesParsedCell = (props) => {
|
||||
return (
|
||||
<TableRow id={props.id + `--proposalFunction`} data-testid={props.id + `--proposalFunction`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>Audit Reserves</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.target}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.value} {props.nativeCoin}</Typography>
|
||||
</TableCell>
|
||||
|
||||
{props.remove && <TableCell>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<TertiaryButton fullWidth onClick={() => alert("Do we need it????")}>Edit</TertiaryButton>
|
||||
<TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>
|
||||
</div>
|
||||
</TableCell>}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
244
src/containers/Governance/components/functions/CreateBond.jsx
Normal file
244
src/containers/Governance/components/functions/CreateBond.jsx
Normal file
@ -0,0 +1,244 @@
|
||||
import { useRef, useState } from "react";
|
||||
import { Box, Typography, TableRow, TableCell, useTheme } from "@mui/material";
|
||||
|
||||
import { PrimaryButton, TertiaryButton } from "../../../../components/Button";
|
||||
import { BooleanTrigger, ArgumentInput } from "./index";
|
||||
|
||||
export const prepareCreateBondCalldata = (chainId) => {
|
||||
return "0xcreateBond";
|
||||
}
|
||||
|
||||
export const prepareCreateBondDescription = "Explanation about what is bond creation, pros and cons should be here, we need to fill as much text here as possible to make initial screen usable and not empty.";
|
||||
|
||||
export const CreateBondParsed = (props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isTable && <CreateBondParsedCell {...props} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CreateBondParsedCell = (props) => {
|
||||
return (
|
||||
<TableRow id={props.id + `--proposalFunction`} data-testid={props.id + `--proposalFunction`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>Create Bond</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.target}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.value} {props.nativeCoin}</Typography>
|
||||
</TableCell>
|
||||
|
||||
{props.remove && <TableCell>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<TertiaryButton fullWidth onClick={() => alert("Do we need it????")}>Edit</TertiaryButton>
|
||||
<TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>
|
||||
</div>
|
||||
</TableCell>}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export const CreateBondSteps = ({ toInitialStep, addCalldata }) => {
|
||||
const [step, setStep] = useState(1);
|
||||
|
||||
const [capacity, setCapacity] = useState();
|
||||
const [initialPrice, setInitialPrice] = useState();
|
||||
const [debtBuffer, setDebtBuffer] = useState();
|
||||
|
||||
const [bondVesting, setBondVesting] = useState();
|
||||
const [bondDuration, setBondDuration] = useState();
|
||||
|
||||
const [tokenAddress, setTokenAddress] = useState("");
|
||||
const [capacityInQuote, setCapacityInQuote] = useState(true);
|
||||
const [fixedTerm, setFixedTerm] = useState(false);
|
||||
|
||||
const [vestingLength, setVestingLength] = useState();
|
||||
const [conclusionTimestamp, setConclusionTimestamp] = useState();
|
||||
|
||||
const handleProceed = () => {
|
||||
addCalldata(prepareCreateBondCalldata())
|
||||
}
|
||||
|
||||
const incrementStep = () => {
|
||||
setStep(prev => prev + 1);
|
||||
}
|
||||
|
||||
const decrementStep = () => {
|
||||
if (step > 1) setStep(prev => prev - 1);
|
||||
else toInitialStep();
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="100%" width="100%" display="flex" flexDirection="column" justifyContent="space-between">
|
||||
{step === 1 && <AddressAndBooleansArguments
|
||||
tokenAddress={tokenAddress}
|
||||
setTokenAddress={setTokenAddress}
|
||||
capacityInQuote={capacityInQuote}
|
||||
setCapacityInQuote={setCapacityInQuote}
|
||||
fixedTerm={fixedTerm}
|
||||
setFixedTerm={setFixedTerm}
|
||||
/>}
|
||||
{step === 2 && <MarketArguments
|
||||
capacity={capacity}
|
||||
setCapacity={setCapacity}
|
||||
initialPrice={initialPrice}
|
||||
setInitialPrice={setInitialPrice}
|
||||
debtBuffer={debtBuffer}
|
||||
setDebtBuffer={setDebtBuffer}
|
||||
/>}
|
||||
{step === 3 && <TermsArguments
|
||||
bondVesting={bondVesting}
|
||||
setBondVesting={setBondVesting}
|
||||
bondDuration={bondDuration}
|
||||
setBondDuration={setBondDuration}
|
||||
/>}
|
||||
{step === 4 && <IntervalsArguments
|
||||
fixedTerm={fixedTerm}
|
||||
vestingLength={vestingLength}
|
||||
setVestingLength={setVestingLength}
|
||||
conclusionTimestamp={conclusionTimestamp}
|
||||
setConclusionTimestamp={setConclusionTimestamp}
|
||||
/>}
|
||||
<Box width="100%" sx={{ marginTop: "20px" }} display="flex" flexDirection="column" gap="5px">
|
||||
<Box display="flex" gap="10px">
|
||||
<TertiaryButton onClick={() => decrementStep()} fullWidth>Back</TertiaryButton>
|
||||
<TertiaryButton disabled={step === 4} onClick={() => incrementStep()} fullWidth>Next</TertiaryButton>
|
||||
</Box>
|
||||
{step === 4 && <PrimaryButton onClick={() => handleProceed()} fullWidth>Create Function</PrimaryButton>}
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
|
||||
const MarketArguments = ({
|
||||
capacity,
|
||||
setCapacity,
|
||||
initialPrice,
|
||||
setInitialPrice,
|
||||
debtBuffer,
|
||||
setDebtBuffer
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<ArgumentInput
|
||||
endString="CSPR or LP"
|
||||
label="Bond Capacity"
|
||||
value={capacity}
|
||||
setValue={setCapacity}
|
||||
tooltip="Bond capacity"
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="???"
|
||||
label="Bond Initial Price"
|
||||
value={initialPrice}
|
||||
setValue={setInitialPrice}
|
||||
tooltip="Bond initial price"
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="%"
|
||||
label="Debt buffer"
|
||||
value={debtBuffer}
|
||||
setValue={setDebtBuffer}
|
||||
tooltip="Debt buffer description"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const TermsArguments = ({
|
||||
bondVesting,
|
||||
setBondVesting,
|
||||
bondDuration,
|
||||
setBondDuration,
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<ArgumentInput
|
||||
endString="seconds"
|
||||
label="Bond Vesting"
|
||||
value={bondVesting}
|
||||
setValue={setBondVesting}
|
||||
tooltip="Bond vesting"
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="seconds"
|
||||
label="Bond Duration"
|
||||
value={bondDuration}
|
||||
setValue={setBondDuration}
|
||||
tooltip="Bond duration"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const IntervalsArguments = ({
|
||||
fixedTerm,
|
||||
vestingLength,
|
||||
setVestingLength,
|
||||
conclusionTimestamp,
|
||||
setConclusionTimestamp,
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<ArgumentInput
|
||||
endString="seconds"
|
||||
label={fixedTerm ? "Vesting Length" : "Vested Timestamp"}
|
||||
value={vestingLength}
|
||||
setValue={setVestingLength}
|
||||
tooltip={fixedTerm ? "Vesting length what is" : "Vested timestamp what is"}
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="seconds"
|
||||
label="Bond Conclusion"
|
||||
value={conclusionTimestamp}
|
||||
setValue={setConclusionTimestamp}
|
||||
tooltip="Bond conclusion timestamp"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const AddressAndBooleansArguments = ({
|
||||
tokenAddress,
|
||||
setTokenAddress,
|
||||
capacityInQuote,
|
||||
setCapacityInQuote,
|
||||
fixedTerm,
|
||||
setFixedTerm,
|
||||
}) => {
|
||||
return (
|
||||
<Box>
|
||||
<BooleanTrigger
|
||||
value={capacityInQuote}
|
||||
label="Capacity in Quote"
|
||||
leftText="CSPR?"
|
||||
rightText="LP?"
|
||||
setLeftValue={() => setCapacityInQuote(true)}
|
||||
setRightValue={() => setCapacityInQuote(false)}
|
||||
tooltip="WTF is this"
|
||||
/>
|
||||
<BooleanTrigger
|
||||
value={fixedTerm}
|
||||
label="Fixed Term"
|
||||
leftText="Yes"
|
||||
rightText="No"
|
||||
setLeftValue={() => setFixedTerm(true)}
|
||||
setRightValue={() => setFixedTerm(false)}
|
||||
tooltip="Fixed term... No idea what it is"
|
||||
/>
|
||||
<ArgumentInput
|
||||
placeholder="0x"
|
||||
inputType="text"
|
||||
label="Token Address"
|
||||
value={tokenAddress}
|
||||
setValue={setTokenAddress}
|
||||
tooltip="Token used as deposit for the bond"
|
||||
/>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@ -0,0 +1,92 @@
|
||||
import { useState } from "react";
|
||||
import { Box, Typography, TableRow, TableCell, useTheme } from "@mui/material";
|
||||
|
||||
import { PrimaryButton, TertiaryButton } from "../../../../components/Button";
|
||||
|
||||
import { BooleanTrigger, ArgumentInput } from "./index";
|
||||
|
||||
export const prepareSetAdjustmentCalldata = (chainId) => {
|
||||
return "0xsetAdjustment";
|
||||
}
|
||||
|
||||
export const prepareSetAdjustmentDescription = "Explanation about what is set adjustment (APY change), pros and cons should be here, we need to fill as much text here as possible to make initial screen usable and not empty.";
|
||||
|
||||
export const SetAdjustmentParsed = (props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isTable && <SetAdjustmentParsedCell {...props} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const SetAdjustmentParsedCell = (props) => {
|
||||
return (
|
||||
<TableRow id={props.id + `--proposalFunction`} data-testid={props.id + `--proposalFunction`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>Set Adjustment</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.target}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.value} {props.nativeCoin}</Typography>
|
||||
</TableCell>
|
||||
|
||||
{props.remove && <TableCell>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
<TertiaryButton fullWidth onClick={() => alert("Do we need it????")}>Edit</TertiaryButton>
|
||||
<TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>
|
||||
</div>
|
||||
</TableCell>}
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
export const SetAdjustmentSteps = ({ toInitialStep, addCalldata }) => {
|
||||
const [rate, setRate] = useState();
|
||||
const [target, setTarget] = useState();
|
||||
const [increase, setIncrease] = useState(false);
|
||||
|
||||
const handleProceed = () => {
|
||||
addCalldata(prepareSetAdjustmentCalldata())
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="100%" width="100%" display="flex" flexDirection="column" justifyContent="space-between">
|
||||
<Box>
|
||||
<BooleanTrigger
|
||||
value={increase}
|
||||
leftText="Add"
|
||||
rightText="Sub"
|
||||
setLeftValue={() => setIncrease(true)}
|
||||
setRightValue={() => setIncrease(false)}
|
||||
label="Direction"
|
||||
tooltip="To add or not to add"
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="%"
|
||||
label="Change Rate"
|
||||
value={rate}
|
||||
setValue={setRate}
|
||||
tooltip="APY will change each time by this value, by basis points"
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="%"
|
||||
label="Target Rate"
|
||||
value={target}
|
||||
setValue={setTarget}
|
||||
tooltip="APY will change until it reaches this value, it's in basis points"
|
||||
/>
|
||||
</Box>
|
||||
<Box width="100%" sx={{ marginTop: "20px" }} display="flex" flexDirection="column" gap="5px">
|
||||
<Box display="flex" gap="10px">
|
||||
<TertiaryButton onClick={toInitialStep} fullWidth>Back</TertiaryButton>
|
||||
<TertiaryButton disabled fullWidth>Next</TertiaryButton>
|
||||
</Box>
|
||||
<PrimaryButton onClick={() => handleProceed()} fullWidth>Create Function</PrimaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
188
src/containers/Governance/components/functions/index.jsx
Normal file
188
src/containers/Governance/components/functions/index.jsx
Normal file
@ -0,0 +1,188 @@
|
||||
import { useRef } from "react";
|
||||
import { Box, Typography, useTheme } from "@mui/material";
|
||||
|
||||
import { StyledInputBase } from "../../../../components/Swap/SwapCard";
|
||||
import InfoTooltip from "../../../../components/Tooltip/InfoTooltip";
|
||||
|
||||
import { prepareAuditReservesDescription, prepareAuditReservesCalldata, AuditReservesSteps, AuditReservesParsed } from "./AuditReserves";
|
||||
import { prepareSetAdjustmentDescription, prepareSetAdjustmentCalldata, SetAdjustmentSteps, SetAdjustmentParsed } from "./SetAdjustment";
|
||||
import { prepareCreateBondDescription, prepareCreateBondCalldata, CreateBondSteps, CreateBondParsed } from "./CreateBond";
|
||||
|
||||
const DEFAULT_DESCRIPTION = "Select function for the proposal to start. Make sure you are ready to fill the proposal, here should be pretty long text just to fill space. Here we are trying to make everything looks cool."
|
||||
|
||||
export const allPossibleFunctions = [
|
||||
{ value: "auditReserves", label: "Audit Reserves" },
|
||||
{ value: "setAdjustment", label: "Change APY" },
|
||||
{ value: "createBond", label: "Create Bond" },
|
||||
];
|
||||
|
||||
export const parseFunctionCalldata = (calldata, index, nativeCoin, removeCalldata) => {
|
||||
const target = "0x5324..123123";
|
||||
const value = 0;
|
||||
|
||||
const remove = () => removeCalldata(index);
|
||||
|
||||
switch (true) {
|
||||
case calldata.includes("auditReserves"):
|
||||
return <AuditReservesParsed
|
||||
isTable
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
case calldata.includes("setAdjustment"):
|
||||
return <SetAdjustmentParsed
|
||||
isTable
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
case calldata.includes("createBond"):
|
||||
return <CreateBondParsed
|
||||
isTable
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const getFunctionArguments = (functionName) => {
|
||||
switch (functionName) {
|
||||
case "auditReserves":
|
||||
return null;
|
||||
case "setAdjustment":
|
||||
return SetAdjustmentSteps;
|
||||
case "createBond":
|
||||
return CreateBondSteps;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
export const getFunctionCalldata = (functionName) => {
|
||||
switch (functionName) {
|
||||
case "auditReserves":
|
||||
return prepareAuditReservesCalldata();
|
||||
case "setAdjustment":
|
||||
return prepareSetAdjustmentCalldata();
|
||||
case "createBond":
|
||||
return prepareCreateBondCalldata();
|
||||
default:
|
||||
return "";
|
||||
}
|
||||
}
|
||||
|
||||
export const getFunctionDescription = (functionName) => {
|
||||
switch (functionName) {
|
||||
case "auditReserves":
|
||||
return prepareAuditReservesDescription;
|
||||
case "setAdjustment":
|
||||
return prepareSetAdjustmentDescription;
|
||||
case "createBond":
|
||||
return prepareCreateBondDescription;
|
||||
default:
|
||||
return DEFAULT_DESCRIPTION;
|
||||
}
|
||||
}
|
||||
|
||||
export const BooleanValue = ({ left, text, isSelected, setSelected }) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
<Box
|
||||
onClick={() => setSelected()}
|
||||
display="flex"
|
||||
justifyContent="center"
|
||||
alignItems="center"
|
||||
sx={{
|
||||
cursor: "pointer",
|
||||
borderRadius: left ? "12px 0 0 12px" : "0 12px 12px 0",
|
||||
flex: 1,
|
||||
border: `1px solid #fff`
|
||||
}}
|
||||
>
|
||||
<Typography color={`${isSelected ? theme.colors.primary[300] : "#fff"}`} align="center">{text}</Typography>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const ArgumentsWrapper = ({ label, tooltip, children }) => {
|
||||
return (
|
||||
<Box sx={{ marginBottom: "18px" }}>
|
||||
<Box sx={{ marginLeft: "5px", marginBottom: "5px" }} display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>{label}</Typography>
|
||||
<InfoTooltip message={tooltip} />
|
||||
</Box>
|
||||
{children}
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
export const BooleanTrigger = ({ value, label, tooltip, leftText, rightText, setLeftValue, setRightValue }) => {
|
||||
return (
|
||||
<ArgumentsWrapper label={label} tooltip={tooltip}>
|
||||
<Box display="flex" width="100%" height="40px">
|
||||
<BooleanValue setSelected={setLeftValue} left isSelected={value} text={leftText} />
|
||||
<BooleanValue setSelected={setRightValue} isSelected={!value} text={rightText} />
|
||||
</Box>
|
||||
</ArgumentsWrapper>
|
||||
)
|
||||
}
|
||||
|
||||
export const ArgumentInput = ({
|
||||
endString,
|
||||
label,
|
||||
tooltip,
|
||||
value,
|
||||
setValue,
|
||||
inputType = "number",
|
||||
placeholder = "0",
|
||||
maxWidth = "100%"
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
const ref = useRef(null);
|
||||
|
||||
return (
|
||||
<Box sx={{ marginBottom: "15px" }}>
|
||||
<Box sx={{ marginLeft: "5px", marginBottom: "5px" }} display="flex" flexDirection="row" alignItems="center">
|
||||
<Typography>{label}</Typography>
|
||||
<InfoTooltip message={tooltip} />
|
||||
</Box>
|
||||
<Box
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
maxWidth={maxWidth}
|
||||
sx={{ backgroundColor: theme.colors.gray[750] }}
|
||||
borderRadius="12px"
|
||||
padding="6px"
|
||||
onClick={() => {
|
||||
ref.current && ref.current.focus();
|
||||
}}
|
||||
>
|
||||
<Box width="100%" display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
|
||||
<StyledInputBase
|
||||
placeholder={placeholder}
|
||||
type={inputType}
|
||||
fontSize="20px"
|
||||
value={value}
|
||||
onChange={(e) => setValue(e.target.value)}
|
||||
sx={{ flex: 1 }}
|
||||
/>
|
||||
{endString && (
|
||||
<Box sx={{ paddingRight: "10px", color: theme.colors.gray[500] }} fontSize="12px" lineHeight="15px">
|
||||
{endString}
|
||||
</Box>
|
||||
)}
|
||||
</Box>
|
||||
</Box>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
@ -52,7 +52,8 @@ 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 }
|
||||
const totalVotes = new DecimalBigNumber(489_000_000_000_000_000_000n, decimals);
|
||||
return { forVotes, againstVotes, totalVotes }
|
||||
}
|
||||
|
||||
export const useProposalSnapshot = (chainId, proposalId) => {
|
||||
|
||||
Loading…
Reference in New Issue
Block a user