Compare commits
No commits in common. "main" and "dao-governance" have entirely different histories.
main
...
dao-govern
@ -1,7 +1,7 @@
|
||||
{
|
||||
"name": "ghost-dao-interface",
|
||||
"private": true,
|
||||
"version": "0.6.3",
|
||||
"version": "0.5.20",
|
||||
"type": "module",
|
||||
"scripts": {
|
||||
"dev": "vite",
|
||||
|
||||
@ -9,12 +9,12 @@ const classes = {
|
||||
|
||||
const StyledMuiChip = styled(MuiChip, {
|
||||
shouldForwardProp: prop => prop !== "template" && prop !== "strong",
|
||||
})(({ theme, template, background, strong }) => {
|
||||
})(({ theme, template, strong }) => {
|
||||
return {
|
||||
[`&.${classes.chip}`]: {
|
||||
height: "21px",
|
||||
borderRadius: "16px",
|
||||
backgroundColor: background ? background : template
|
||||
backgroundColor: template
|
||||
? template === "purple"
|
||||
? theme.colors.special["olyZaps"]
|
||||
: template === "gray"
|
||||
@ -42,8 +42,8 @@ const StyledMuiChip = styled(MuiChip, {
|
||||
};
|
||||
});
|
||||
|
||||
const Chip = ({ background, template, strong = false, ...props }) => {
|
||||
return <StyledMuiChip className={classes.chip} background={background} template={template} strong={strong} {...props} />;
|
||||
const Chip = ({ template, strong = false, ...props }) => {
|
||||
return <StyledMuiChip className={classes.chip} template={template} strong={strong} {...props} />;
|
||||
};
|
||||
|
||||
export default Chip;
|
||||
|
||||
@ -25,7 +25,7 @@ const LinearProgressBar = (props) => {
|
||||
{props.target && <Box
|
||||
sx={{
|
||||
position: 'absolute',
|
||||
left: `${Math.min(Math.max(props.target, 0), 100)}%`,
|
||||
left: `${props.target}%`,
|
||||
top: props.targetTop || 0,
|
||||
bottom: props.targetBottom || 0,
|
||||
width: props.targetWidth || "2px",
|
||||
|
||||
@ -34,7 +34,6 @@ const Select = ({
|
||||
inputWidth,
|
||||
renderValue = null,
|
||||
width = "100%",
|
||||
maxWidth = "100%"
|
||||
}) => {
|
||||
const theme = useTheme();
|
||||
return (
|
||||
@ -42,7 +41,6 @@ const Select = ({
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
width={width}
|
||||
maxWidth={maxWidth}
|
||||
sx={{ backgroundColor: theme.colors.gray[750] }}
|
||||
borderRadius="12px"
|
||||
padding="15px"
|
||||
@ -61,15 +59,6 @@ const Select = ({
|
||||
IconComponent={KeyboardArrowDownIcon}
|
||||
renderValue={renderValue}
|
||||
displayEmpty
|
||||
MenuProps={{
|
||||
PaperProps: {
|
||||
sx: {
|
||||
maxHeight: 160,
|
||||
overflowY: 'auto',
|
||||
},
|
||||
},
|
||||
disableScrollLock: true,
|
||||
}}
|
||||
>
|
||||
{options.map((opt) => (
|
||||
<MenuItem key={opt.value} value={opt.value}>
|
||||
|
||||
@ -11,9 +11,6 @@ export const isGovernanceAvailable = (chainId, addressChainId) => {
|
||||
case 11155111:
|
||||
exists = true
|
||||
break;
|
||||
case 63:
|
||||
exists = true
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@ -40,27 +37,15 @@ export const isNetworkAvailable = (chainId, addressChainId) => {
|
||||
}
|
||||
|
||||
export const isNetworkLegacy = (chainId) => {
|
||||
let isLegacy = false;
|
||||
let exists = false;
|
||||
switch (chainId) {
|
||||
case 560048:
|
||||
isLegacy = true
|
||||
exists = true
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return isLegacy;
|
||||
}
|
||||
|
||||
export const isNetworkLegacyType = (chainId) => {
|
||||
let isLegacyType = false;
|
||||
switch (chainId) {
|
||||
case 63:
|
||||
isLegacyType = true;
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
return isLegacyType
|
||||
return exists;
|
||||
}
|
||||
|
||||
export const networkAvgBlockSpeed = (chainId) => {
|
||||
|
||||
@ -3,29 +3,29 @@ import { NetworkId } from "../constants";
|
||||
export const STAKING_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xC2C579631Bf6daA93252154080fecfd68c6aa506",
|
||||
[NetworkId.TESTNET_HOODI]: "0x25F62eDc6C89FF84E957C22336A35d2dfc861a86",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xC2C579631Bf6daA93252154080fecfd68c6aa506",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xC25C9C56a89ebd6ef291b415d00ACfa7913c55e7",
|
||||
};
|
||||
|
||||
export const BOND_DEPOSITORY_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x46BF6F7c3e96351eab7542f2B14c9f2ac6d08dF0",
|
||||
[NetworkId.TESTNET_HOODI]: "0x6Ad50B1E293E68B2fC230c576220a93A9D311571",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x46BF6F7c3e96351eab7542f2B14c9f2ac6d08dF0",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x7C85cDEddBAd0f50453d373F7332BEa11ECa7BAf",
|
||||
};
|
||||
|
||||
export const DAO_TREASURY_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x05D797f9F34844594C956da58f1785997397f02E",
|
||||
[NetworkId.TESTNET_HOODI]: "0x1a1b29b18f714fac9dDabEf530dFc4f85b56A6e8",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x05D797f9F34844594C956da58f1785997397f02E",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x5883C8e2259556B534036c7fDF4555E09dE9f243",
|
||||
};
|
||||
|
||||
export const FTSO_DAI_LP_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xCd1505E5d169525e0241c177aF5929A92E02276D",
|
||||
[NetworkId.TESTNET_HOODI]: "0xf7B2d44209E70782d93A70F7D8eC50010dF7ae50",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x53B13C4722081c405ce25c7A7629fC326A49a469",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xE6546D12665dB5B22Cb92FB9e0221aE51A57aeaa",
|
||||
};
|
||||
|
||||
export const FTSO_STNK_LP_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000",
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000", // TBD
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x0000000000000000000000000000000000000000",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x0000000000000000000000000000000000000000",
|
||||
}
|
||||
@ -45,41 +45,40 @@ export const WETH_ADDRESSES = {
|
||||
export const GHST_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x1eCee8BfceC44e535B3Ee92Aca70507668781392",
|
||||
[NetworkId.TESTNET_HOODI]: "0xE98f7426457E6533B206e91B7EcA97aa8A258B46",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x1eCee8BfceC44e535B3Ee92Aca70507668781392",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x14b5787F8a1E62786F50A7998A9b14aa24298423",
|
||||
};
|
||||
|
||||
export const STNK_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xa31cf59baC26Dd8A8b422b999eB1Ba541C941EA7",
|
||||
[NetworkId.TESTNET_HOODI]: "0xF07e9303A9f16Afd82f4f57Fd6fca68Aa0AB6D7F",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xa31cf59baC26Dd8A8b422b999eB1Ba541C941EA7",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x137bA9403885D8ECEa95AaFBb8734F5a16121bAC",
|
||||
};
|
||||
|
||||
export const FTSO_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x7ebd1224D36d64eA09312073e60f352d1383801A",
|
||||
[NetworkId.TESTNET_HOODI]: "0xb184e423811b644A1924334E63985c259F5D0033",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x7ebd1224D36d64eA09312073e60f352d1383801A",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xeA170CC0faceC531a6a9e93a28C4330Ac50343a1",
|
||||
};
|
||||
|
||||
export const DISTRIBUTOR_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xfa524772eec78FAeD0db2cF8A831FDDa9F5B0544",
|
||||
[NetworkId.TESTNET_HOODI]: "0xdF49dC81c457c6f92e26cf6d686C7a8715255842",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xfa524772eec78FAeD0db2cF8A831FDDa9F5B0544",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xaf5e76706520db7fb01096E322940206bf3fce57",
|
||||
};
|
||||
|
||||
export const GHOST_GOVERNANCE_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xaf7Ad1b83C47405BB9aa96868bCFbb6D65e4C2a1",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xF950101af53733Ccf9309Ef4CC374B300dd43010",
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0x4823F1DC785D721eAdD2bD218E1eeD63aF67fBF4",
|
||||
};
|
||||
|
||||
export const BONDING_CALCULATOR_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xfA821181de76D3EAdb404dDe971A6d28289F22b3",
|
||||
[NetworkId.TESTNET_HOODI]: "0x2635d526Ad24b98082563937f7b996075052c6Fd",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xfA821181de76D3EAdb404dDe971A6d28289F22b3",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x0c4C7C49a173E2a3f9Eed93125F3F146D8e17bCb",
|
||||
}
|
||||
|
||||
export const GATEKEEPER_ADDRESSES = {
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xd735cA07984a16911222c08411A80e24EB38869B",
|
||||
[NetworkId.TESTNET_MORDOR]: "0x4823F1DC785D721eAdD2bD218E1eeD63aF67fBF4",
|
||||
[NetworkId.TESTNET_SEPOLIA]: "0xc85129A097773B7F8970a7364c928C05f265E6A1",
|
||||
[NetworkId.TESTNET_MORDOR]: "0xA59cB4ff90bE2206121aE61eEB68d0AeC7BA095f",
|
||||
}
|
||||
|
||||
export const UNISWAP_V2_ROUTER = {
|
||||
|
||||
@ -1,5 +1,5 @@
|
||||
import { CheckBoxOutlineBlank, CheckBoxOutlined } from "@mui/icons-material";
|
||||
import { Box, Checkbox, FormControlLabel, useMediaQuery } from "@mui/material";
|
||||
import { Box, Checkbox, FormControlLabel } from "@mui/material";
|
||||
import { useState, useMemo } from "react";
|
||||
import { useLocation } from "react-router-dom";
|
||||
|
||||
@ -32,8 +32,6 @@ const BondInputArea = ({
|
||||
address,
|
||||
connect
|
||||
}) => {
|
||||
const isSemiSmallScreen = useMediaQuery("(max-width: 480px)");
|
||||
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
||||
const { pathname } = useLocation();
|
||||
|
||||
const { currentIndex } = useCurrentIndex(chainId);
|
||||
@ -97,7 +95,7 @@ const BondInputArea = ({
|
||||
UpperSwapCard={
|
||||
<SwapCard
|
||||
maxWidth="476px"
|
||||
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
|
||||
inputWidth="280px"
|
||||
id="from"
|
||||
token={<TokenStack tokens={bond.quoteToken.icons} sx={{ fontSize: "21px" }} />}
|
||||
tokenName={bond.quoteToken.name}
|
||||
@ -112,7 +110,7 @@ const BondInputArea = ({
|
||||
LowerSwapCard={
|
||||
<SwapCard
|
||||
maxWidth="476px"
|
||||
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
|
||||
inputWidth="280px"
|
||||
id="to"
|
||||
token={<TokenStack tokens={bond.baseToken.icons} sx={{ fontSize: "21px" }} />}
|
||||
tokenName={bond.baseToken.name}
|
||||
@ -179,12 +177,12 @@ const BondInputArea = ({
|
||||
)}
|
||||
</span>
|
||||
}
|
||||
tooltip={`Total amount of ${ftsoSymbol} you will receive from this bond purchase`}
|
||||
tooltip={`The total amount of payout asset you will receive from this bond purchase`}
|
||||
/>
|
||||
|
||||
<DataRow
|
||||
title="Max You Can Buy"
|
||||
tooltip={`Maximum ${ftsoSymbol} that can be purchased in a single transaction. Prevents whales from buying entire bond supply at once.`}
|
||||
tooltip={`The maximum quantity of payout token offered via bonds at this moment in time`}
|
||||
balance={
|
||||
<span>
|
||||
{bond.baseToken.tokenAddress.toUpperCase() === bond.quoteToken.quoteTokenAddress.toUpperCase()
|
||||
@ -197,14 +195,14 @@ const BondInputArea = ({
|
||||
<DataRow
|
||||
title="Discount"
|
||||
balance={<BondDiscount discount={bond.discount} textOnly />}
|
||||
tooltip={`The discount (or premium) between ${ftsoSymbol} market price and bond price. Higher discount = better deal for bond buyers.`}
|
||||
/>
|
||||
tooltip={`The bond discount is the percentage difference between ${ftsoSymbol} market value and the bond's price`}
|
||||
/>
|
||||
|
||||
<DataRow
|
||||
title={`Vesting Term`}
|
||||
balance={<BondVesting vesting={bond.vesting} />}
|
||||
tooltip={"Time until bond fully vests and becomes claimable. Vesting period aligns bond buyer with the protocol by encouraging longer-term holding."}
|
||||
/>
|
||||
tooltip={"The duration of the Bond whereby the bond can be claimed in its entirety"}
|
||||
/>
|
||||
|
||||
{recipientAddress !== address && (
|
||||
<DataRow title={`Recipient`} balance={shorten(recipientAddress)} />
|
||||
|
||||
@ -10,7 +10,6 @@ import {
|
||||
useMediaQuery,
|
||||
} from "@mui/material";
|
||||
import { decodeAddress } from "@polkadot/util-crypto";
|
||||
import { fromHex } from "@polkadot-api/utils";
|
||||
import { getBlockNumber } from "@wagmi/core";
|
||||
import { useTransaction } from "wagmi";
|
||||
import { keccak256 } from "viem";
|
||||
@ -43,8 +42,6 @@ import {
|
||||
useCurrentSlot,
|
||||
useGenesisSlot,
|
||||
useErasTotalStake,
|
||||
useLatestBlockNumber,
|
||||
useEraIndex,
|
||||
} from "../../hooks/ghost";
|
||||
|
||||
import { ValidatorTable } from "./ValidatorTable";
|
||||
@ -105,28 +102,24 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
const networkIdEncoded = u64.enc(BigInt(chainId));
|
||||
const amountEncoded = u128.enc(BigInt(watchTransaction.amount));
|
||||
const addressEncoded = decodeAddress(watchTransaction.receiverAddress, false, 1996);
|
||||
const transactionHashEncoded = fromHex(watchTransaction.transactionHash);
|
||||
const blockNumber = u64.enc(watchTransactionInfo?.blockNumber ?? 0n);
|
||||
|
||||
const clapArgsStr = new Uint8Array([
|
||||
...addressEncoded,
|
||||
...amountEncoded,
|
||||
...blockNumber,
|
||||
...transactionHashEncoded,
|
||||
...networkIdEncoded
|
||||
]);
|
||||
return keccak256(clapArgsStr)
|
||||
}, [watchTransaction, watchTransactionInfo])
|
||||
|
||||
const latestBlockNumber = useLatestBlockNumber();
|
||||
const eraIndex = useEraIndex();
|
||||
const currentSlot = useCurrentSlot();
|
||||
const genesisSlot = useGenesisSlot();
|
||||
const currentSession = useCurrentIndex();
|
||||
const applauseThreshold = useApplauseThreshold();
|
||||
const evmNetwork = useEvmNetwork({ evmChainId: chainId });
|
||||
const totalStakedAmount = useErasTotalStake({
|
||||
epochIndex: eraIndex?.index ?? 0,
|
||||
eraIndex: Math.floor((watchTransaction?.sessionIndex ?? currentSession) / 6)
|
||||
});
|
||||
const authorities = useAuthorities({
|
||||
currentSession: watchTransaction?.sessionIndex ?? currentSession
|
||||
@ -212,20 +205,16 @@ const Bridge = ({ chainId, address, config, connect }) => {
|
||||
|
||||
const latestCommits = useMemo(() => {
|
||||
return validators?.map((validator, index) => {
|
||||
const lastUpdatedNumber = Number(blockCommitments?.at(index)?.last_updated ?? 0);
|
||||
const timestampDelta = (latestBlockNumber - lastUpdatedNumber) * 6000; // ideal 6 seconds for block
|
||||
const lastUpdatedTimestamp = Math.floor(currentTime - timestampDelta);
|
||||
|
||||
return {
|
||||
validator: validator,
|
||||
lastActive: timestampDelta,
|
||||
lastUpdated: lastUpdatedTimestamp,
|
||||
lastActive: currentTime - Number(blockCommitments?.at(index)?.last_updated ?? 0),
|
||||
lastUpdated: blockCommitments?.at(index)?.last_updated,
|
||||
lastStoredBlock: blockCommitments?.at(index)?.last_stored_block,
|
||||
storedBlockTime: (blockCommitments?.at(index)?.last_stored_block ?? 0n) * networkAvgBlockSpeed(chainId),
|
||||
disabled: disabledValidators?.includes(index),
|
||||
}
|
||||
})
|
||||
}, [blockCommitments, disabledValidators, validators, latestBlockNumber, chainId]);
|
||||
}, [blockCommitments, disabledValidators, validators, chainId]);
|
||||
|
||||
const latestUpdate = useMemo(() => {
|
||||
const validCommits = latestCommits?.filter(commit =>
|
||||
|
||||
@ -55,8 +55,6 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
const [secondsToWait, setSecondsToWait] = useState(localStorage.getItem("dex-deadline") || "60");
|
||||
const [slippage, setSlippage] = useState(localStorage.getItem("dex-slippage") || "5");
|
||||
const [formatDecimals, setFormatDecimals] = useState(localStorage.getItem("dex-decimals") || "5");
|
||||
const [actualDestinationAddress, setActualDestinationAddress] = useState(localStorage.getItem("dex-destination"));
|
||||
const [destinationAddress, setDestinationAddress] = useState(actualDestinationAddress);
|
||||
|
||||
const [tokenAddressTop, setTokenAddressTop] = useState(RESERVE_ADDRESSES[chainId]);
|
||||
const [tokenAddressBottom, setTokenAddressBottom] = useState(FTSO_ADDRESSES[chainId]);
|
||||
@ -164,24 +162,6 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
}
|
||||
}
|
||||
|
||||
const setDestinationAddressInner = (value) => {
|
||||
const cleanedValue = value.trim();
|
||||
const isEvmAddress = /^0x[a-fA-F0-9]{40}$/.test(cleanedValue);
|
||||
if (isEvmAddress) {
|
||||
localStorage.setItem("dex-destination", value);
|
||||
setActualDestinationAddress(value);
|
||||
} else if (!isEvmAddress && actualDestinationAddress) {
|
||||
localStorage.removeItem("dex-destination");
|
||||
setActualDestinationAddress(undefined);
|
||||
}
|
||||
setDestinationAddress(value);
|
||||
}
|
||||
|
||||
const handleCloseSetting = () => {
|
||||
setDestinationAddress(undefined);
|
||||
handleSettingsOpen(false);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="calc(100vh - 43px)">
|
||||
<Helmet>
|
||||
@ -223,7 +203,7 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
minHeight="200px"
|
||||
open={settingsOpen}
|
||||
headerText={"Settings"}
|
||||
onClose={() => handleCloseSetting()}
|
||||
onClose={() => handleSettingsOpen(false)}
|
||||
>
|
||||
<Box>
|
||||
<InputLabel htmlFor="slippage">Slippage</InputLabel>
|
||||
@ -287,31 +267,6 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
<Box mt="32px">
|
||||
<InputLabel htmlFor="recipient">
|
||||
{`${actualDestinationAddress ? "Custom" : "Default"} destination address`}
|
||||
</InputLabel>
|
||||
<Box mt="8px">
|
||||
<FormControl variant="outlined" color="primary" fullWidth>
|
||||
<OutlinedInput
|
||||
inputProps={{ "data-testid": "decimals-to-wait" }}
|
||||
type="text"
|
||||
id="destination-to-wait"
|
||||
value={destinationAddress
|
||||
? destinationAddress
|
||||
: actualDestinationAddress ?? address
|
||||
}
|
||||
onChange={event => setDestinationAddressInner(event.currentTarget.value)}
|
||||
/>
|
||||
</FormControl>
|
||||
</Box>
|
||||
<Box mt="8px">
|
||||
<Typography variant="body2" color="textSecondary">
|
||||
Recipient address of swapped assets and liquidity tokens
|
||||
</Typography>
|
||||
</Box>
|
||||
</Box>
|
||||
</Modal>
|
||||
|
||||
<TokenModal
|
||||
@ -367,7 +322,6 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
dexAddresses={dexAddresses}
|
||||
connect={connect}
|
||||
slippage={slippage}
|
||||
destination={actualDestinationAddress ? actualDestinationAddress : address}
|
||||
secondsToWait={secondsToWait}
|
||||
setTopTokenListOpen={setTopTokenListOpen}
|
||||
setBottomTokenListOpen={setBottomTokenListOpen}
|
||||
@ -384,7 +338,6 @@ const Dex = ({ chainId, address, connect }) => {
|
||||
dexAddresses={dexAddresses}
|
||||
connect={connect}
|
||||
slippage={slippage}
|
||||
destination={actualDestinationAddress ? actualDestinationAddress : address}
|
||||
secondsToWait={secondsToWait}
|
||||
setTopTokenListOpen={setTopTokenListOpen}
|
||||
setBottomTokenListOpen={setBottomTokenListOpen}
|
||||
|
||||
@ -9,7 +9,7 @@ import { TokenAllowanceGuard } from "../../components/TokenAllowanceGuard/TokenA
|
||||
import { SecondaryButton } from "../../components/Button";
|
||||
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { formatNumber, formatCurrency, bigIntSqrt } from "../../helpers";
|
||||
import { formatNumber, formatCurrency } from "../../helpers";
|
||||
|
||||
import { useBalance, useTotalSupply } from "../../hooks/tokens";
|
||||
import { useUniswapV2Pair, useUniswapV2PairReserves, addLiquidity } from "../../hooks/uniswapv2";
|
||||
@ -23,7 +23,6 @@ const PoolContainer = ({
|
||||
dexAddresses,
|
||||
connect,
|
||||
slippage,
|
||||
destination,
|
||||
secondsToWait,
|
||||
setTopTokenListOpen,
|
||||
setBottomTokenListOpen,
|
||||
@ -76,6 +75,34 @@ const PoolContainer = ({
|
||||
const setMaxTop = () => setAmountTop(balanceTop.toString());
|
||||
const setMaxBottom = () => setAmountBottom(balanceBottom.toString());
|
||||
|
||||
const bigIntSqrt = (n) => {
|
||||
if (n < 0n) {
|
||||
throw new Error("Cannot compute the square root of a negative number.");
|
||||
}
|
||||
if (n < 2n) {
|
||||
return n; // The square root of 0 or 1 is the number itself
|
||||
}
|
||||
|
||||
let low = 0n;
|
||||
let high = n;
|
||||
let mid;
|
||||
|
||||
while (low <= high) {
|
||||
mid = (low + high) / 2n;
|
||||
const midSquared = mid * mid;
|
||||
|
||||
if (midSquared === n) {
|
||||
return mid; // Found the exact square root
|
||||
} else if (midSquared < n) {
|
||||
low = mid + 1n; // Move to the right half
|
||||
} else {
|
||||
high = mid - 1n; // Move to the left half
|
||||
}
|
||||
}
|
||||
|
||||
return high; // The integer part of the square root
|
||||
}
|
||||
|
||||
const estimatedAmountOut = useMemo(() => {
|
||||
const pairReserves0 = addressTop.toUpperCase() === tokenAddresses.token0.toUpperCase()
|
||||
? pairReserves.reserve0
|
||||
@ -130,6 +157,8 @@ const PoolContainer = ({
|
||||
setIsPending(true);
|
||||
|
||||
const deadline = Math.floor(Date.now() / 1000) + secondsToWait;
|
||||
const destination = address;
|
||||
|
||||
const shares = 100000;
|
||||
const one = BigInt(shares * 100);
|
||||
const floatSlippage = slippage === "" ? 0 : parseFloat(slippage);
|
||||
|
||||
@ -27,7 +27,6 @@ const SwapContainer = ({
|
||||
setTopTokenListOpen,
|
||||
setBottomTokenListOpen,
|
||||
slippage,
|
||||
destination,
|
||||
secondsToWait,
|
||||
setIsSwap,
|
||||
formatDecimals
|
||||
@ -109,6 +108,8 @@ const SwapContainer = ({
|
||||
setIsPending(true);
|
||||
|
||||
const deadline = Math.floor(Date.now() / 1000) + secondsToWait;
|
||||
const destination = address;
|
||||
|
||||
const shares = 100000;
|
||||
const one = BigInt(shares * 100);
|
||||
const floatSlippage = slippage === "" ? 0 : parseFloat(slippage);
|
||||
|
||||
@ -43,7 +43,7 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
|
||||
const theme = useTheme();
|
||||
|
||||
const myStoredProposals = localStorage.getItem(`${MY_PROPOSALS_PREFIX}-${chainId}-${address}`);
|
||||
const myStoredProposals = localStorage.getItem(MY_PROPOSALS_PREFIX);
|
||||
const [myProposals, setMyProposals] = useState(
|
||||
myStoredProposals ? JSON.parse(myStoredProposals).map(id => BigInt(id)) : []
|
||||
);
|
||||
@ -63,7 +63,7 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
|
||||
useEffect(() => {
|
||||
const toStore = JSON.stringify(myProposals.map(id => id.toString()));
|
||||
localStorage.setItem(`${MY_PROPOSALS_PREFIX}-${chainId}-${address}`, toStore);
|
||||
localStorage.setItem(MY_PROPOSALS_PREFIX, toStore);
|
||||
}, [myProposals]);
|
||||
|
||||
useEffect(() => {
|
||||
@ -163,14 +163,6 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
isVertical
|
||||
>
|
||||
<Box display="flex" flexDirection="column" alignItems="center">
|
||||
<TertiaryButton
|
||||
sx={{ maxWidth: isSemiSmallScreen ? "100%" : "350px" }}
|
||||
fullWidth
|
||||
disabled={isPending}
|
||||
onClick={() => setIsModalOpened(true)}
|
||||
>
|
||||
Add New Function
|
||||
</TertiaryButton>
|
||||
<PrimaryButton
|
||||
disabled={
|
||||
proposalFunctions.length === 0 ||
|
||||
@ -182,6 +174,14 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
>
|
||||
{isPending ? "Submitting..." : "Submit Proposal"}
|
||||
</PrimaryButton>
|
||||
<TertiaryButton
|
||||
sx={{ maxWidth: isSemiSmallScreen ? "100%" : "350px" }}
|
||||
fullWidth
|
||||
disabled={isPending}
|
||||
onClick={() => setIsModalOpened(true)}
|
||||
>
|
||||
Add New
|
||||
</TertiaryButton>
|
||||
</Box>
|
||||
</TokenAllowanceGuard>
|
||||
</Box>
|
||||
@ -200,41 +200,21 @@ const NewProposal = ({ config, address, connect, chainId }) => {
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
{isSmallScreen
|
||||
? <Box display="flex" flexDirection="column">
|
||||
{proposalFunctions?.map((metadata, index) => {
|
||||
return parseFunctionCalldata({
|
||||
metadata,
|
||||
index,
|
||||
chainId,
|
||||
removeCalldata,
|
||||
nativeCoin: nativeCurrency,
|
||||
isTable: false,
|
||||
});
|
||||
})}
|
||||
</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" }}>Calldata</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{proposalFunctions?.map((metadata, index) => {
|
||||
return parseFunctionCalldata({
|
||||
metadata,
|
||||
index,
|
||||
chainId,
|
||||
removeCalldata,
|
||||
nativeCoin: nativeCurrency,
|
||||
});
|
||||
})}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
}
|
||||
<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" }}>Calldata</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{proposalFunctions.map((metadata, index) => {
|
||||
return parseFunctionCalldata(metadata, index, chainId, nativeCurrency, removeCalldata);
|
||||
})}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>}
|
||||
</Box>
|
||||
</Container>
|
||||
|
||||
@ -1,8 +1,7 @@
|
||||
import { useEffect, useState, useMemo } from "react";
|
||||
import ReactGA from "react-ga4";
|
||||
import { useParams } from 'react-router-dom';
|
||||
import { useQueryClient } from '@tanstack/react-query';
|
||||
import { useBlock, useBlockNumber } from "wagmi";
|
||||
import { useBlock, useBlockNumber } from 'wagmi'
|
||||
|
||||
import {
|
||||
Box,
|
||||
@ -33,15 +32,13 @@ import { formatNumber, formatCurrency, shorten } from "../../helpers";
|
||||
import { prettifySecondsInDays } from "../../helpers/timeUtil";
|
||||
import { DecimalBigNumber, } from "../../helpers/DecimalBigNumber";
|
||||
|
||||
import { convertStatusToColor, convertStatusToLabel } from "./helpers";
|
||||
import { convertStatusToTemplate, convertStatusToLabel } from "./helpers";
|
||||
import { parseFunctionCalldata } from "./components/functions";
|
||||
|
||||
import { networkAvgBlockSpeed } from "../../constants";
|
||||
|
||||
import { useTokenSymbol, usePastTotalSupply, usePastVotes, useBalance } from "../../hooks/tokens";
|
||||
import {
|
||||
getVoteValue,
|
||||
getVoteTarget,
|
||||
useProposalState,
|
||||
useProposalProposer,
|
||||
useProposalLocked,
|
||||
@ -75,7 +72,6 @@ import RepeatIcon from '@mui/icons-material/Repeat';
|
||||
|
||||
const HUNDRED = new DecimalBigNumber(100n, 0);
|
||||
|
||||
|
||||
const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
const { id } = useParams();
|
||||
const proposalId = BigInt(id);
|
||||
@ -88,7 +84,6 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
const isVerySmallScreen = useMediaQuery("(max-width: 379px)");
|
||||
|
||||
const theme = useTheme();
|
||||
const queryClient = useQueryClient();
|
||||
|
||||
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
|
||||
const { balance } = useBalance(chainId, "GHST", address);
|
||||
@ -117,24 +112,48 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
|
||||
const votePercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return 0;
|
||||
return totalVotes * HUNDRED / totalSupply;
|
||||
const value = (totalVotes?._value ?? 0n) * 100n / (totalSupply?._value ?? 1n);
|
||||
return new DecimalBigNumber(value, 0);
|
||||
}, [totalVotes, totalSupply]);
|
||||
|
||||
const forPercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
|
||||
return forVotes * HUNDRED / totalSupply;
|
||||
const value = (forVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
|
||||
return new DecimalBigNumber(value, 2);
|
||||
}, [forVotes, totalSupply]);
|
||||
|
||||
const againstPercentage= useMemo(() => {
|
||||
if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
|
||||
return againstVotes * HUNDRED / totalSupply;
|
||||
const value = (againstVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
|
||||
return new DecimalBigNumber(value, 2);
|
||||
}, [againstVotes, totalSupply]);
|
||||
|
||||
const voteWeightPercentage = useMemo(() => {
|
||||
if (totalSupply._value === 0n) return new DecimalBigNumber(0n, 2);
|
||||
return pastVotes * HUNDRED / totalSupply;
|
||||
const value = (pastVotes?._value ?? 0n) * 10000n / (totalSupply?._value ?? 1n);
|
||||
return new DecimalBigNumber(value, 2);
|
||||
}, [pastVotes, totalSupply]);
|
||||
|
||||
const voteValue = useMemo(() => {
|
||||
if (totalVotes?._value == 0n) {
|
||||
return 0;
|
||||
}
|
||||
return Number(forVotes._value * 100n / totalVotes._value);
|
||||
}, [forVotes, totalVotes]);
|
||||
|
||||
const voteTarget = useMemo(() => {
|
||||
const first = (5n * againstVotes._value + forVotes._value);
|
||||
const second = BigInt(Math.floor(Math.sqrt(Number(totalVotes._value))));
|
||||
const bias = 3n * first + second;
|
||||
const denominator = totalVotes._value + bias;
|
||||
|
||||
if (denominator === 0n) {
|
||||
return 80;
|
||||
}
|
||||
|
||||
return Number(totalVotes?._value * 100n / denominator);
|
||||
}, [againstVotes, forVotes, totalVotes]);
|
||||
|
||||
const nativeCurrency = useMemo(() => {
|
||||
const client = config?.getClient();
|
||||
return client?.chain?.nativeCurrency?.symbol;
|
||||
@ -149,42 +168,38 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
return url;
|
||||
}, [proposalProposer, config]);
|
||||
|
||||
const handleVote = async (support) => {
|
||||
const handleVote = async (against) => {
|
||||
setIsPending(true);
|
||||
const support = against ? 0 : 1;
|
||||
const result = await castVote(chainId, address, proposalId, support);
|
||||
|
||||
if (result) {
|
||||
const storedVotedProposals = localStorage.getItem(`${VOTED_PROPOSALS_PREFIX}-${chainId}-${address}`);
|
||||
const proposals = JSON.parse(storedVotedProposals || "[]").map(id => BigInt(id));
|
||||
proposals.push(proposalId);
|
||||
const toStore = JSON.stringify(proposals.map(id => id.toString()));
|
||||
localStorage.setItem(`${VOTED_PROPOSALS_PREFIX}-${chainId}-${address}`, toStore);
|
||||
await queryClient.invalidateQueries();
|
||||
const toStore = JSON.stringify(proposalId.toString());
|
||||
localStorage.setItem(VOTED_PROPOSALS_PREFIX, toStore);
|
||||
}
|
||||
setIsPending(false);
|
||||
|
||||
setIsPending(true);
|
||||
}
|
||||
|
||||
const handleExecute = async () => {
|
||||
setIsPending(true);
|
||||
await executeProposal(chainId, address, proposalId);
|
||||
await queryClient.invalidateQueries();
|
||||
setIsPending(false);
|
||||
setIsPending(true);
|
||||
}
|
||||
|
||||
const handleRelease = async () => {
|
||||
const handleRelease = async (proposalId) => {
|
||||
setIsPending(true);
|
||||
await releaseLocked(chainId, address, proposalId);
|
||||
await queryClient.invalidateQueries();
|
||||
setIsPending(false);
|
||||
setIsPending(true);
|
||||
}
|
||||
|
||||
return (
|
||||
<Box>
|
||||
<PageTitle
|
||||
name={`GDP-${id.slice(-5)}`}
|
||||
name={`GBP-${id.slice(-5)}`}
|
||||
subtitle={
|
||||
<Typography component="span">
|
||||
{`Cast $${ghstSymbol} to shape this proposal's outcome`}
|
||||
Proposal details, need more in-depth description
|
||||
</Typography>
|
||||
}
|
||||
/>
|
||||
@ -199,24 +214,19 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
}}
|
||||
>
|
||||
<Box sx={{ mt: "15px" }}>
|
||||
<Box
|
||||
gap="20px"
|
||||
display="flex"
|
||||
justifyContent={"space-between"}
|
||||
flexDirection={isSmallScreen ? "column" : "row"}
|
||||
>
|
||||
<Box display="flex" justifyContent="space-between" gap="20px">
|
||||
<Paper
|
||||
fullWidth
|
||||
enableBackground
|
||||
headerContent={
|
||||
<Box display="flex" alignItems="center" flexDirection="row" gap="10px">
|
||||
{!isSmallScreen &&<Typography variant="h6">
|
||||
<Typography variant="h6">
|
||||
Progress
|
||||
</Typography>}
|
||||
</Typography>
|
||||
<Chip
|
||||
sx={{ marginTop: "4px", width: "88px" }}
|
||||
label={convertStatusToLabel(proposalState)}
|
||||
background={convertStatusToColor(proposalState)}
|
||||
template={convertStatusToTemplate(proposalState)}
|
||||
/>
|
||||
</Box>
|
||||
}
|
||||
@ -240,8 +250,8 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={getVoteValue(forVotes?._value ?? 0n, totalVotes?._value ?? 0n)}
|
||||
target={getVoteTarget(totalVotes?._value ?? 0n, totalSupply?._value ?? 0n)}
|
||||
value={voteValue}
|
||||
target={voteTarget}
|
||||
/>
|
||||
</Box>
|
||||
|
||||
@ -294,14 +304,14 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
|
||||
onClick={() => handleVote(1)}
|
||||
>
|
||||
{isPending ? "Voting..." : "For"}
|
||||
For
|
||||
</SecondaryButton>
|
||||
<SecondaryButton
|
||||
fullWidth
|
||||
disabled={proposalState !== 1 || pastVotes?._value === 0n || isPending}
|
||||
onClick={() => handleVote(0)}
|
||||
>
|
||||
{isPending ? "Voting..." : "Against"}
|
||||
Against
|
||||
</SecondaryButton>
|
||||
</>
|
||||
: <SecondaryButton
|
||||
@ -336,7 +346,6 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
isProposer={proposalProposer === address}
|
||||
chainId={chainId}
|
||||
proposalId={id}
|
||||
isPending={isPending}
|
||||
/>
|
||||
</Paper>
|
||||
</Box>
|
||||
@ -353,46 +362,28 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
|
||||
</Box>
|
||||
}
|
||||
>
|
||||
{isSmallScreen
|
||||
? <Box display="flex" flexDirection="column">
|
||||
{proposalDetails?.map((metadata, index) => {
|
||||
return parseFunctionCalldata({
|
||||
metadata,
|
||||
index,
|
||||
chainId,
|
||||
nativeCoin: nativeCurrency,
|
||||
isTable: false,
|
||||
});
|
||||
})}
|
||||
</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" }}>Calldata</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{proposalDetails?.map((metadata, index) => {
|
||||
return parseFunctionCalldata({
|
||||
metadata,
|
||||
index,
|
||||
chainId,
|
||||
nativeCoin: nativeCurrency,
|
||||
});
|
||||
})}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
}
|
||||
<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" }}>Calldata</TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>Value</TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
<TableBody>{proposalDetails?.map((metadata, index) => {
|
||||
return parseFunctionCalldata(metadata, index, chainId, nativeCurrency);
|
||||
})}</TableBody>
|
||||
</Table>
|
||||
</TableContainer>
|
||||
</Paper>
|
||||
</Container>
|
||||
</Box>
|
||||
)
|
||||
}
|
||||
|
||||
const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked, proposalId, chainId, state, address, isProposer, isPending }) => {
|
||||
const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked, proposalId, chainId, state, address, isProposer }) => {
|
||||
const { delay: propsalVotingDelay } = useProposalVotingDelay(chainId, proposalId);
|
||||
const { snapshot: proposalSnapshot } = useProposalSnapshot(chainId, proposalId);
|
||||
const { deadline: proposalDeadline } = useProposalDeadline(chainId, proposalId);
|
||||
@ -412,24 +403,19 @@ const VotingTimeline = ({ connect, handleExecute, handleRelease, proposalLocked,
|
||||
<VotingTimelineItem chainId={chainId} occured={proposalDeadline} message="Voting ends" />
|
||||
</Timeline>
|
||||
<Box width="100%" display="flex" gap="10px">
|
||||
{(isProposer && (proposalLocked?._value ?? 0n) > 0n) && <SecondaryButton
|
||||
{isProposer && <SecondaryButton
|
||||
fullWidth
|
||||
disabled={isPending || state < 2}
|
||||
disabled={(proposalLocked?._value ?? 0n) === 0n || state < 2}
|
||||
onClick={() => address === "" ? connect() : handleRelease()}
|
||||
>
|
||||
{address === "" ? "Connect" : "Release"}
|
||||
</SecondaryButton>}
|
||||
<SecondaryButton
|
||||
fullWidth
|
||||
disabled={isPending || state !== 4}
|
||||
disabled={state !== 4}
|
||||
onClick={() => address === "" ? connect() : handleExecute()}
|
||||
>
|
||||
{address === ""
|
||||
? "Connect"
|
||||
: state !== 4
|
||||
? convertStatusToLabel(state)
|
||||
: isPending ? "Executing..." : "Execute"
|
||||
}
|
||||
{address === "" ? "Connect" : "Execute"}
|
||||
</SecondaryButton>
|
||||
</Box>
|
||||
</Box>
|
||||
|
||||
@ -12,7 +12,7 @@ const GovernanceInfoText = () => {
|
||||
ghostDAO’s adaptive governance system algorithmically sets minimum collateral based on activity.
|
||||
<Link
|
||||
color={theme.colors.primary[300]}
|
||||
href="http://forum.ghostchain.io/"
|
||||
href="https://docs.dao.ghostchain.io/"
|
||||
target="_blank"
|
||||
rel="noopener noreferrer"
|
||||
>Learn more here.</Link>
|
||||
|
||||
@ -103,11 +103,7 @@ const InitialStep = ({ selectedOption, handleChange, handleCalldata, handleProce
|
||||
return allPossibleFunctions.find(opt => opt.value === selected)?.label || selected;
|
||||
}}
|
||||
/>
|
||||
<Typography align="center" variant="body2">
|
||||
<b>{selectedOption}</b>
|
||||
{" "}
|
||||
{functionDescription}
|
||||
</Typography>
|
||||
<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>
|
||||
|
||||
@ -23,7 +23,6 @@ import ArrowUpIcon from "../../../assets/icons/arrow-up.svg?react";
|
||||
|
||||
import { networkAvgBlockSpeed } from "../../../constants";
|
||||
import { prettifySecondsInDays, prettifySeconds } from "../../../helpers/timeUtil";
|
||||
import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
|
||||
|
||||
import Chip from "../../../components/Chip/Chip";
|
||||
import Modal from "../../../components/Modal/Modal";
|
||||
@ -33,7 +32,7 @@ import { PrimaryButton, TertiaryButton } from "../../../components/Button";
|
||||
|
||||
import ProposalInfoText from "./ProposalInfoText";
|
||||
import {
|
||||
convertStatusToColor,
|
||||
convertStatusToTemplate,
|
||||
convertStatusToLabel,
|
||||
MY_PROPOSALS_PREFIX,
|
||||
VOTED_PROPOSALS_PREFIX
|
||||
@ -41,7 +40,9 @@ import {
|
||||
|
||||
import { useScreenSize } from "../../../hooks/useScreenSize";
|
||||
|
||||
import { useProposals } from "../../../hooks/governance";
|
||||
import {
|
||||
useProposals,
|
||||
} from "../../../hooks/governance";
|
||||
|
||||
const MAX_PROPOSALS_TO_SHOW = 10;
|
||||
|
||||
@ -52,12 +53,12 @@ const ProposalsList = ({ chainId, address, config }) => {
|
||||
|
||||
const [proposalsFilter, setProposalFilter] = useState("active");
|
||||
|
||||
const myStoredProposals = localStorage.getItem(`${MY_PROPOSALS_PREFIX}-${chainId}-${address}`);
|
||||
const myStoredProposals = localStorage.getItem(MY_PROPOSALS_PREFIX);
|
||||
const [myProposals, setMyProposals] = useState(
|
||||
myStoredProposals ? JSON.parse(myStoredProposals).map(id => BigInt(id)) : []
|
||||
);
|
||||
|
||||
const storedVotedProposals = localStorage.getItem(`${VOTED_PROPOSALS_PREFIX}-${chainId}-${address}`);
|
||||
const storedVotedProposals = localStorage.getItem(VOTED_PROPOSALS_PREFIX);
|
||||
const [votedProposals, setVotedProposals] = useState(
|
||||
storedVotedProposals ? JSON.parse(storedVotedProposals).map(id => BigInt(id)) : []
|
||||
);
|
||||
@ -174,7 +175,7 @@ const ProposalTable = ({ children }) => (
|
||||
<TableCell align="center" style={{ width: "130px", 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={{ width: "180px", padding: "8px 0" }}></TableCell>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}></TableCell>
|
||||
</TableRow>
|
||||
</TableHead>
|
||||
|
||||
@ -185,6 +186,34 @@ const ProposalTable = ({ children }) => (
|
||||
|
||||
const ProposalRow = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
|
||||
const voteValue = useMemo(() => {
|
||||
const againstVotes = proposal?.votes?.at(0)?._value ?? 0n;
|
||||
const forVotes = proposal?.votes?.at(1)?._value ?? 0n;
|
||||
const totalVotes = againstVotes + forVotes;
|
||||
if (totalVotes == 0) {
|
||||
return 0;
|
||||
}
|
||||
return Number(forVotes * 100n / totalVotes);
|
||||
}, [proposal]);
|
||||
|
||||
const voteTarget = useMemo(() => {
|
||||
const againstVotes = proposal?.votes?.at(0)?._value ?? 0n;
|
||||
const forVotes = proposal?.votes?.at(1)?._value ?? 0n;
|
||||
const totalVotes = againstVotes + forVotes;
|
||||
|
||||
const first = (5n * againstVotes + forVotes);
|
||||
const second = BigInt(Math.floor(Math.sqrt(Number(totalVotes))));
|
||||
const bias = 3n * first + second;
|
||||
const denominator = totalVotes + bias;
|
||||
|
||||
if (denominator === 0n) {
|
||||
return 80;
|
||||
}
|
||||
|
||||
return Number(totalVotes * 100n / denominator);
|
||||
}, [proposal]);
|
||||
|
||||
return (
|
||||
<TableRow id={proposal.hashes.short + `--proposal`} data-testid={proposal.hashes.short + `--proposal`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
@ -195,7 +224,7 @@ const ProposalRow = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
<Chip
|
||||
sx={{ width: "100px" }}
|
||||
label={convertStatusToLabel(proposal.state)}
|
||||
background={convertStatusToColor(proposal.state)}
|
||||
template={convertStatusToTemplate(proposal.state)}
|
||||
/>
|
||||
</TableCell>
|
||||
|
||||
@ -215,8 +244,8 @@ const ProposalRow = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={proposal?.voteValue ?? 0}
|
||||
target={proposal?.voteTarget ?? 50}
|
||||
value={voteValue}
|
||||
target={voteTarget}
|
||||
/>
|
||||
</Box>
|
||||
</TableCell>
|
||||
@ -244,6 +273,7 @@ const ProposalRow = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
const ProposalCard = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
const theme = useTheme();
|
||||
const isSmallScreen = useMediaQuery('(max-width: 450px)');
|
||||
|
||||
return (
|
||||
<Box id={proposal.hashes.short + `--proposal`} data-testid={proposal.hashes.short + `--proposal`}>
|
||||
<Box display="flex" flexDirection={isSmallScreen ? "column" : "row"} justifyContent="space-between">
|
||||
@ -253,7 +283,7 @@ const ProposalCard = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
<Chip
|
||||
sx={{ width: "88px" }}
|
||||
label={convertStatusToLabel(proposal.state)}
|
||||
background={convertStatusToColor(proposal.state)}
|
||||
template={convertStatusToTemplate(proposal.state)}
|
||||
/>
|
||||
</Box>
|
||||
<Typography>
|
||||
@ -271,8 +301,8 @@ const ProposalCard = ({ proposal, blockNumber, openProposal, chainId }) => {
|
||||
barColor={theme.colors.feedback.success}
|
||||
barBackground={theme.colors.feedback.error}
|
||||
variant="determinate"
|
||||
value={proposal?.voteValue ?? 0}
|
||||
target={proposal?.voteTarget ?? 50}
|
||||
value={69}
|
||||
target={Math.floor(Math.random() * 101)}
|
||||
/>
|
||||
</Box>
|
||||
<Box marginBottom="20px">
|
||||
@ -312,11 +342,10 @@ const ProposalFilterTrigger = ({ trigger, setTrigger }) => {
|
||||
}
|
||||
|
||||
const convertDeadline = (deadline, blockNumber, chainId) => {
|
||||
const alreadyHappened = blockNumber > deadline;
|
||||
const diff = alreadyHappened ? blockNumber - deadline : deadline - blockNumber;
|
||||
const diff = blockNumber > deadline ? blockNumber - deadline : deadline - blockNumber;
|
||||
const voteSeconds = Number(diff * networkAvgBlockSpeed(chainId));
|
||||
|
||||
const result = prettifySeconds(voteSeconds, "min");
|
||||
const result = prettifySeconds(voteSeconds, "mins");
|
||||
if (result === "now") {
|
||||
return new Date(Date.now()).toLocaleDateString('en-US', {
|
||||
year: 'numeric',
|
||||
@ -325,10 +354,7 @@ const convertDeadline = (deadline, blockNumber, chainId) => {
|
||||
});
|
||||
}
|
||||
|
||||
const prefix = alreadyHappened ? "" : "in ";
|
||||
const postfix = alreadyHappened ? " ago" : "";
|
||||
|
||||
return `${prefix}${result}${postfix}`;
|
||||
return `in ${result}`;
|
||||
}
|
||||
|
||||
export default ProposalsList;
|
||||
|
||||
@ -20,12 +20,20 @@ export const prepareAuditReservesCalldata = (chainId) => {
|
||||
return { label, target, calldata, value };
|
||||
}
|
||||
|
||||
export const prepareAuditReservesDescription = "function audits and updates the protocol's total reserve value. It sums the value of all approved reserve and liquidity tokens, then stores and logs the new total.";
|
||||
export const prepareAuditReservesDescription = "Audit Reserves function audits and updates the protocol's total reserve value. It sums the value of all approved reserve and liquidity tokens, then stores and logs the new total.";
|
||||
|
||||
export const AuditReservesSteps = () => {
|
||||
return null;
|
||||
}
|
||||
|
||||
export const AuditReservesParsed = (props) => {
|
||||
return (
|
||||
<>
|
||||
{props.isTable && <AuditReservesParsedCell {...props} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const AuditReservesParsedCell = (props) => {
|
||||
return <ParsedCell {...props} />
|
||||
}
|
||||
|
||||
@ -29,7 +29,7 @@ export const prepareCreateBondCalldata = (chainId, markets, terms, quoteToken, i
|
||||
return { label, target, calldata, value };
|
||||
}
|
||||
|
||||
export const prepareCreateBondDescription = "function creates a new bond market by processing pricing, capacity, and term inputs. It initializes and stores the new bond market's complete configuration in the protocol.";
|
||||
export const prepareCreateBondDescription = "Create Bond function creates a new bond market by processing pricing, capacity, and term inputs. It initializes and stores the new bond market's complete configuration in the protocol.";
|
||||
|
||||
export const CreateBondParsed = (props) => {
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
@ -51,11 +51,15 @@ export const CreateBondParsed = (props) => {
|
||||
<CreateBondSteps args={props.args} ftsoSymbol={ftsoSymbol} nativeCurrency={props.nativeCoin} {...props} />
|
||||
</Box>
|
||||
</Modal>
|
||||
<ParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props} />
|
||||
{props.isTable && <CreateBondParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props}/>}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const CreateBondParsedCell = (props) => {
|
||||
return <ParsedCell {...props} />
|
||||
}
|
||||
|
||||
export const CreateBondSteps = ({ nativeCurrency, ftsoSymbol, chainId, toInitialStep, addCalldata, args }) => {
|
||||
const createMode = args === undefined;
|
||||
|
||||
@ -114,7 +118,7 @@ export const CreateBondSteps = ({ nativeCurrency, ftsoSymbol, chainId, toInitial
|
||||
}, [step, tokenAddress, capacity, initialPrice, debtBuffer, depositInterval, tuneInterval]);
|
||||
|
||||
const possibleTokens = [
|
||||
{ value: FTSO_DAI_LP_ADDRESSES[chainId], symbol: `${ftsoSymbol}-${reserveSymbol} LP`, label: `${ftsoSymbol}-${reserveSymbol} LP: ${shorten(FTSO_DAI_LP_ADDRESSES[chainId])}` },
|
||||
{ value: FTSO_DAI_LP_ADDRESSES[chainId], symbol: `${ftsoSymbol}-${nativeCurrency} LP`, label: `${ftsoSymbol}-${nativeCurrency} LP: ${shorten(FTSO_DAI_LP_ADDRESSES[chainId])}` },
|
||||
{ value: RESERVE_ADDRESSES[chainId], symbol: reserveSymbol, label: `${reserveSymbol}: ${shorten(RESERVE_ADDRESSES[chainId])}` },
|
||||
];
|
||||
|
||||
@ -125,7 +129,7 @@ export const CreateBondSteps = ({ nativeCurrency, ftsoSymbol, chainId, toInitial
|
||||
createMode={createMode}
|
||||
possibleTokens={possibleTokens}
|
||||
setTokenAddress={createMode ? setTokenAddress : empty}
|
||||
nativeCurrency={reserveSymbol}
|
||||
nativeCurrency={nativeCurrency}
|
||||
ftsoSymbol={ftsoSymbol}
|
||||
capacityInQuote={capacityInQuote}
|
||||
setCapacityInQuote={createMode ? setCapacityInQuote : empty}
|
||||
@ -222,7 +226,7 @@ const IntervalsArguments = ({
|
||||
/>
|
||||
<ArgumentInput
|
||||
endString="seconds"
|
||||
label="_intervals[1]"
|
||||
label="_intervals[0]"
|
||||
value={tuneInterval ?? ""}
|
||||
setValue={setTuneInterval}
|
||||
tooltip="Time in seconds between bond price tuning events that evaluate actual vs. expected bond sales, adjusting how aggressively price decays in the next interval."
|
||||
@ -292,7 +296,7 @@ const TokenAndBooleansArguments = ({
|
||||
rightText={"False"}
|
||||
setLeftValue={() => setCapacityInQuote(true)}
|
||||
setRightValue={() => setCapacityInQuote(false)}
|
||||
tooltip={`Determines how the bond market capacity is measured. True = measured in _quoteToken, False = measured in ${ftsoSymbol}.`}
|
||||
tooltip={`Determines how the bond market capacity is measured. True = measured in ${nativeCurrency}, False = measured in ${ftsoSymbol}.`}
|
||||
/>
|
||||
<BooleanTrigger
|
||||
value={fixedTerm}
|
||||
@ -311,7 +315,6 @@ const TokenAndBooleansArguments = ({
|
||||
value={selectedOption ?? ""}
|
||||
onChange={handleChange}
|
||||
options={possibleTokens}
|
||||
maxWidth="calc(100vw - 70px)"
|
||||
inputWidth="100%"
|
||||
renderValue={(selected) => {
|
||||
if (!selected || selected.length === 0) {
|
||||
|
||||
@ -1,85 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { encodeFunctionData } from 'viem';
|
||||
import { Box, Typography, TableRow, TableCell, useTheme } from "@mui/material";
|
||||
|
||||
import Modal from "../../../../components/Modal/Modal";
|
||||
import { PrimaryButton, TertiaryButton } from "../../../../components/Button";
|
||||
|
||||
import { GHOST_GOVERNANCE_ADDRESSES } from "../../../../constants/addresses";
|
||||
import { abi as GovernorAbi } from "../../../../abi/GhostGovernor.json";
|
||||
|
||||
import { ArgumentInput, ParsedCell } from "./index";
|
||||
|
||||
export const prepareSetLateQuorumExtensionCalldata = (chainId, lateQuorumVoteExtension) => {
|
||||
const value = 0n;
|
||||
const label = "setLateQuorumVoteExtension";
|
||||
const target = GHOST_GOVERNANCE_ADDRESSES[chainId];
|
||||
const calldata = encodeFunctionData({
|
||||
abi: GovernorAbi,
|
||||
functionName: 'setLateQuorumVoteExtension',
|
||||
args: [lateQuorumVoteExtension]
|
||||
});
|
||||
return { label, target, calldata, value };
|
||||
}
|
||||
|
||||
export const prepareSetLateQuorumExtensionDescription = "updates the current value of the vote extension parameter: the number of blocks that are required to pass from the time a proposal reaches quorum until its voting period ends.";
|
||||
|
||||
export const SetLateQuorumExtensionParsed = (props) => {
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
headerContent={
|
||||
<Box display="flex" justifyContent="center" alignItems="center" gap="15px">
|
||||
<Typography variant="h4">View setLateQuorumVoteExtension</Typography>
|
||||
</Box>
|
||||
}
|
||||
open={isOpened}
|
||||
onClose={() => setIsOpened(false)}
|
||||
maxWidth="460px"
|
||||
minHeight="80px"
|
||||
>
|
||||
<Box minHeight="100px" display="flex" alignItems="start" justifyContent="space-between" flexDirection="column">
|
||||
<SetLateQuorumExtensionSteps {...props} />
|
||||
</Box>
|
||||
</Modal>
|
||||
<ParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const SetLateQuorumExtensionSteps = ({ chainId, toInitialStep, addCalldata, args }) => {
|
||||
const createMode = args === undefined;
|
||||
|
||||
const [lateQuorumVoteExtension, setlateQuorumVoteExtension] = useState(args?.at(0));
|
||||
|
||||
const handleProceed = () => {
|
||||
addCalldata(prepareSetLateQuorumExtensionCalldata(chainId, lateQuorumVoteExtension));
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="100%" width="100%" display="flex" flexDirection="column" justifyContent={"space-between"}>
|
||||
<ArgumentInput
|
||||
disabled={!createMode}
|
||||
endString="blocks"
|
||||
label="lateQuorumVoteExtension"
|
||||
value={lateQuorumVoteExtension ?? ""}
|
||||
setValue={createMode ? setlateQuorumVoteExtension : () => {}}
|
||||
tooltip="Vote extension defines extension in blocks if a vote causes quorum to be reached, the proposal's voting period may be extended so that it does not end before at least a specified time has passed"
|
||||
/>
|
||||
{createMode && <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
|
||||
disabled={!lateQuorumVoteExtension}
|
||||
onClick={() => handleProceed()}
|
||||
fullWidth
|
||||
>
|
||||
Create Function
|
||||
</PrimaryButton>
|
||||
</Box>}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -1,85 +0,0 @@
|
||||
import { useState } from "react";
|
||||
import { encodeFunctionData } from 'viem';
|
||||
import { Box, Typography, TableRow, TableCell, useTheme } from "@mui/material";
|
||||
|
||||
import Modal from "../../../../components/Modal/Modal";
|
||||
import { PrimaryButton, TertiaryButton } from "../../../../components/Button";
|
||||
|
||||
import { GHOST_GOVERNANCE_ADDRESSES } from "../../../../constants/addresses";
|
||||
import { abi as GovernorAbi } from "../../../../abi/GhostGovernor.json";
|
||||
|
||||
import { ArgumentInput, ParsedCell } from "./index";
|
||||
|
||||
export const prepareSetProposalThresholdCalldata = (chainId, newThreshold) => {
|
||||
const value = 0n;
|
||||
const label = "setProposalThreshold";
|
||||
const target = GHOST_GOVERNANCE_ADDRESSES[chainId];
|
||||
const calldata = encodeFunctionData({
|
||||
abi: GovernorAbi,
|
||||
functionName: 'setProposalThreshold',
|
||||
args: [newThreshold]
|
||||
});
|
||||
return { label, target, calldata, value };
|
||||
}
|
||||
|
||||
export const prepareSetProposalThresholdDescription = "updates the minimum amount required to create a proposal. This threshold applies only when there are no Active or Pending proposals; otherwise, a higher threshold is enforced.";
|
||||
|
||||
export const SetProposalThresholdParsed = (props) => {
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
return (
|
||||
<>
|
||||
<Modal
|
||||
headerContent={
|
||||
<Box display="flex" justifyContent="center" alignItems="center" gap="15px">
|
||||
<Typography variant="h4">View setProposalThreshold</Typography>
|
||||
</Box>
|
||||
}
|
||||
open={isOpened}
|
||||
onClose={() => setIsOpened(false)}
|
||||
maxWidth="460px"
|
||||
minHeight="80px"
|
||||
>
|
||||
<Box minHeight="100px" display="flex" alignItems="start" justifyContent="space-between" flexDirection="column">
|
||||
<SetProposalThresholdSteps {...props} />
|
||||
</Box>
|
||||
</Modal>
|
||||
<ParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props} />
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
export const SetProposalThresholdSteps = ({ chainId, toInitialStep, addCalldata, args }) => {
|
||||
const createMode = args === undefined;
|
||||
|
||||
const [newThreshold, setNewThreshold] = useState(args?.at(0));
|
||||
|
||||
const handleProceed = () => {
|
||||
addCalldata(prepareSetProposalThresholdCalldata(chainId, newThreshold));
|
||||
}
|
||||
|
||||
return (
|
||||
<Box height="100%" width="100%" display="flex" flexDirection="column" justifyContent={"space-between"}>
|
||||
<ArgumentInput
|
||||
disabled={!createMode}
|
||||
endString="!CSPR units"
|
||||
label="newProposalThreshold"
|
||||
value={newThreshold ?? ""}
|
||||
setValue={createMode ? setNewThreshold : () => {}}
|
||||
tooltip="The new proposal threshold defines the minimum amount each proposer must lock for the duration of the proposal. This locked amount serves as the required deposit to create a proposal."
|
||||
/>
|
||||
{createMode && <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
|
||||
disabled={!newThreshold}
|
||||
onClick={() => handleProceed()}
|
||||
fullWidth
|
||||
>
|
||||
Create Function
|
||||
</PrimaryButton>
|
||||
</Box>}
|
||||
</Box>
|
||||
);
|
||||
}
|
||||
@ -24,7 +24,7 @@ export const prepareSetAdjustmentCalldata = (chainId, rateChange, targetRate, in
|
||||
return { label, target, calldata, value };
|
||||
}
|
||||
|
||||
export const prepareSetAdjustmentDescription = "function schedules a gradual change to the staking APY. It increases or decreases the staking APY by a specified rate per epoch until a target rate reached.";
|
||||
export const prepareSetAdjustmentDescription = "Set Adjustment function schedules a gradual change to the staking APY. It increases or decreases the staking APY by a specified rate per epoch until a target rate reached.";
|
||||
|
||||
export const SetAdjustmentParsed = (props) => {
|
||||
const [isOpened, setIsOpened] = useState(false);
|
||||
@ -45,17 +45,21 @@ export const SetAdjustmentParsed = (props) => {
|
||||
<SetAdjustmentSteps {...props} />
|
||||
</Box>
|
||||
</Modal>
|
||||
<ParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props} />
|
||||
{props.isTable && <SetAdjustmentParsedCell isOpened={isOpened} setIsOpened={setIsOpened} {...props} />}
|
||||
</>
|
||||
)
|
||||
}
|
||||
|
||||
const SetAdjustmentParsedCell = (props) => {
|
||||
return <ParsedCell {...props} />
|
||||
}
|
||||
|
||||
export const SetAdjustmentSteps = ({ chainId, toInitialStep, addCalldata, args }) => {
|
||||
const createMode = args === undefined;
|
||||
|
||||
const [rate, setRate] = useState(args?.at(0));
|
||||
const [target, setTarget] = useState(args?.at(1));
|
||||
const [increase, setIncrease] = useState(args === undefined ? true : args.at(2));
|
||||
const [increase, setIncrease] = useState(args?.at(1) ?? true);
|
||||
|
||||
const handleProceed = () => {
|
||||
addCalldata(prepareSetAdjustmentCalldata(chainId, rate, target, increase));
|
||||
|
||||
@ -10,7 +10,6 @@ import InfoTooltip from "../../../../components/Tooltip/InfoTooltip";
|
||||
import { abi as TreasuryAbi } from "../../../../abi/GhostTreasury.json";
|
||||
import { abi as DistributorAbi } from "../../../../abi/GhostDistributor.json";
|
||||
import { abi as DepositoryAbi } from "../../../../abi/GhostBondDepository.json";
|
||||
import { abi as GovernorAbi } from "../../../../abi/GhostGovernor.json";
|
||||
|
||||
import { config } from "../../../../config";
|
||||
import { shorten, formatCurrency } from "../../../../helpers";
|
||||
@ -18,20 +17,16 @@ import { shorten, formatCurrency } from "../../../../helpers";
|
||||
import { prepareAuditReservesDescription, prepareAuditReservesCalldata, AuditReservesSteps, AuditReservesParsed } from "./AuditReserves";
|
||||
import { prepareSetAdjustmentDescription, prepareSetAdjustmentCalldata, SetAdjustmentSteps, SetAdjustmentParsed } from "./SetAdjustment";
|
||||
import { prepareCreateBondDescription, prepareCreateBondCalldata, CreateBondSteps, CreateBondParsed } from "./CreateBond";
|
||||
import { prepareSetProposalThresholdDescription, prepareSetProposalThresholdCalldata, SetProposalThresholdSteps, SetProposalThresholdParsed } from "./ProposalThreshold";
|
||||
import { prepareSetLateQuorumExtensionDescription, prepareSetLateQuorumExtensionCalldata, SetLateQuorumExtensionSteps, SetLateQuorumExtensionParsed } from "./LateQuorumExtension";
|
||||
|
||||
const DEFAULT_DESCRIPTION = "Please select the function to include in your proposal. Multi-functional proposals are allowed, but each included function should be clearly specified."
|
||||
|
||||
export const allPossibleFunctions = [
|
||||
{ value: "auditReserves", label: "auditReserves" },
|
||||
{ value: "setAdjustment", label: "setAdjustment" },
|
||||
{ value: "create", label: "create" },
|
||||
{ value: "setProposalThreshold", label: "setProposalThreshold" },
|
||||
{ value: "setLateQuorumVoteExtension", label: "setLateQuorumVoteExtension" },
|
||||
{ value: "auditReserves", label: "auditReserves" },
|
||||
{ value: "setAdjustment", label: "setAdjustment" },
|
||||
{ value: "create", label: "create" },
|
||||
];
|
||||
|
||||
const allAbis = [TreasuryAbi, DistributorAbi, DepositoryAbi, GovernorAbi];
|
||||
const allAbis = [TreasuryAbi, DistributorAbi, DepositoryAbi];
|
||||
|
||||
const identifyAction = (calldata) => {
|
||||
let decoded = { functionName: "Unknown", args: [] };
|
||||
@ -49,58 +44,78 @@ const identifyAction = (calldata) => {
|
||||
return decoded;
|
||||
}
|
||||
|
||||
export const parseFunctionCalldata = ({
|
||||
metadata,
|
||||
index,
|
||||
chainId,
|
||||
nativeCoin,
|
||||
removeCalldata,
|
||||
isTable = true,
|
||||
}) => {
|
||||
export const parseFunctionCalldata = (metadata, index, chainId, nativeCoin, removeCalldata) => {
|
||||
const { label, calldata, target, value } = metadata;
|
||||
const { functionName, args } = identifyAction(calldata);
|
||||
const labelOrName = label ?? (functionName ?? "Unknown");
|
||||
|
||||
const remove = removeCalldata && (() => removeCalldata(index));
|
||||
const props = {
|
||||
isTable,
|
||||
calldata,
|
||||
label: labelOrName,
|
||||
chainId: chainId,
|
||||
remove: removeCalldata,
|
||||
nativeCoin,
|
||||
value,
|
||||
target,
|
||||
args,
|
||||
id: index,
|
||||
};
|
||||
|
||||
switch (functionName) {
|
||||
case allPossibleFunctions[0].value:
|
||||
return <AuditReservesParsed key={index} {...props} />;
|
||||
case allPossibleFunctions[1].value:
|
||||
return <SetAdjustmentParsed key={index} {...props} />;
|
||||
case allPossibleFunctions[2].value:
|
||||
return <CreateBondParsed {...props} />;
|
||||
case allPossibleFunctions[3].value:
|
||||
return <SetProposalThresholdParsed key={index} {...props} />;
|
||||
case allPossibleFunctions[4].value:
|
||||
return <SetLateQuorumExtensionParsed key={index} {...props} />;
|
||||
case "auditReserves":
|
||||
return <AuditReservesParsed
|
||||
isTable
|
||||
key={index}
|
||||
calldata={calldata}
|
||||
label={labelOrName}
|
||||
chainId={chainId}
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
case "setAdjustment":
|
||||
return <SetAdjustmentParsed
|
||||
isTable
|
||||
args={args}
|
||||
key={index}
|
||||
calldata={calldata}
|
||||
label={labelOrName}
|
||||
chainId={chainId}
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
case "create":
|
||||
return <CreateBondParsed
|
||||
isTable
|
||||
args={args}
|
||||
key={index}
|
||||
calldata={calldata}
|
||||
label={labelOrName}
|
||||
chainId={chainId}
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
default:
|
||||
return <ParsedCell key={index} {...props} />;
|
||||
return <ParsedCell
|
||||
isTable
|
||||
key={index}
|
||||
calldata={calldata}
|
||||
label="Unknown"
|
||||
remove={remove}
|
||||
nativeCoin={nativeCoin}
|
||||
value={value}
|
||||
target={target}
|
||||
id={index}
|
||||
/>;
|
||||
}
|
||||
}
|
||||
|
||||
export const getFunctionArguments = (functionName) => {
|
||||
switch (functionName) {
|
||||
case allPossibleFunctions[1].value:
|
||||
case "auditReserves":
|
||||
return null;
|
||||
case "setAdjustment":
|
||||
return SetAdjustmentSteps;
|
||||
case allPossibleFunctions[2].value:
|
||||
case "create":
|
||||
return CreateBondSteps;
|
||||
case allPossibleFunctions[3].value:
|
||||
return SetProposalThresholdSteps;
|
||||
case allPossibleFunctions[4].value:
|
||||
return SetLateQuorumExtensionSteps;
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -108,16 +123,12 @@ export const getFunctionArguments = (functionName) => {
|
||||
|
||||
export const getFunctionCalldata = (functionName, chainId) => {
|
||||
switch (functionName) {
|
||||
case allPossibleFunctions[0].value:
|
||||
case "auditReserves":
|
||||
return prepareAuditReservesCalldata(chainId);
|
||||
case allPossibleFunctions[1].value:
|
||||
case "setAdjustment":
|
||||
return prepareSetAdjustmentCalldata(chainId);
|
||||
case allPossibleFunctions[2].value:
|
||||
case "create":
|
||||
return prepareCreateBondCalldata(chainId);
|
||||
case allPossibleFunctions[3].value:
|
||||
return prepareSetProposalThresholdCalldata(chainId);
|
||||
case allPossibleFunctions[4].value:
|
||||
return prepareSetLateQuorumExtensionCalldata(chainId);
|
||||
default:
|
||||
return null;
|
||||
}
|
||||
@ -125,16 +136,12 @@ export const getFunctionCalldata = (functionName, chainId) => {
|
||||
|
||||
export const getFunctionDescription = (functionName) => {
|
||||
switch (functionName) {
|
||||
case allPossibleFunctions[0].value:
|
||||
case "auditReserves":
|
||||
return prepareAuditReservesDescription;
|
||||
case allPossibleFunctions[1].value:
|
||||
case "setAdjustment":
|
||||
return prepareSetAdjustmentDescription;
|
||||
case allPossibleFunctions[2].value:
|
||||
case "create":
|
||||
return prepareCreateBondDescription;
|
||||
case allPossibleFunctions[3].value:
|
||||
return prepareSetProposalThresholdDescription;
|
||||
case allPossibleFunctions[4].value:
|
||||
return prepareSetLateQuorumExtensionDescription;
|
||||
default:
|
||||
return DEFAULT_DESCRIPTION;
|
||||
}
|
||||
@ -268,86 +275,34 @@ export const ParsedCell = (props) => {
|
||||
}
|
||||
};
|
||||
|
||||
if (props.isTable) {
|
||||
return (
|
||||
<TableRow id={props.id + `--proposalFunction`} data-testid={props.id + `--proposalFunction`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.label}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Link href={etherscanLink} target="_blank" rel="noopener noreferrer">
|
||||
<Typography>{shorten(props.target)}</Typography>
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Link onClick={handleCalldataCopy} target="_blank" rel="noopener noreferrer">
|
||||
<Typography>{shorten(props.calldata)}</Typography>
|
||||
</Link>
|
||||
</TableCell>
|
||||
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{formatCurrency(props.value, 4, props.nativeCoin)}</Typography>
|
||||
</TableCell>
|
||||
|
||||
<TableCell>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
{props.args && <TertiaryButton fullWidth onClick={() => props.setIsOpened(!props.isOpened)}>View</TertiaryButton>}
|
||||
{props.remove && <TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
}
|
||||
|
||||
return (
|
||||
<Box
|
||||
id={props.id + `--proposalFunction`}
|
||||
data-testid={props.id + `--proposalFunction`}
|
||||
display="flex"
|
||||
flexDirection="column"
|
||||
>
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography>{`Function ${props.id + 1}`}</Typography>
|
||||
<TableRow id={props.id + `--proposalFunction`} data-testid={props.id + `--proposalFunction`}>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{props.label}</Typography>
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography>Target</Typography>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Link href={etherscanLink} target="_blank" rel="noopener noreferrer">
|
||||
<Typography>{shorten(props.target)}</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography>Calldata</Typography>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Link onClick={handleCalldataCopy} target="_blank" rel="noopener noreferrer">
|
||||
<Typography>{shorten(props.calldata)}</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
</TableCell>
|
||||
|
||||
<Box display="flex" justifyContent="space-between">
|
||||
<Typography>Value</Typography>
|
||||
<Link onClick={handleCalldataCopy} target="_blank" rel="noopener noreferrer">
|
||||
<Typography>{formatCurrency(props.value, 4, props.nativeCoin)}</Typography>
|
||||
</Link>
|
||||
</Box>
|
||||
<TableCell align="center" style={{ padding: "8px 0" }}>
|
||||
<Typography>{formatCurrency(props.value, 4, props.nativeCoin)}</Typography>
|
||||
</TableCell>
|
||||
|
||||
{(props.args || props.remove) && <Box
|
||||
display="flex"
|
||||
flexDirection="row"
|
||||
justifyContent="space-between"
|
||||
gap="10px"
|
||||
marginTop="10px"
|
||||
>
|
||||
{props.args && <TertiaryButton fullWidth onClick={() => props.setIsOpened(!props.isOpened)}>View</TertiaryButton>}
|
||||
{props.remove && <TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>}
|
||||
</Box>}
|
||||
|
||||
<hr width="100%" style={{ margin: "20px 0" }} />
|
||||
|
||||
</Box>
|
||||
<TableCell>
|
||||
<div style={{ display: 'flex', gap: '8px' }}>
|
||||
{props.args && <TertiaryButton fullWidth onClick={() => props.setIsOpened(!props.isOpened)}>View</TertiaryButton>}
|
||||
{props.remove && <TertiaryButton fullWidth onClick={() => props.remove()}>Delete</TertiaryButton>}
|
||||
</div>
|
||||
</TableCell>
|
||||
</TableRow>
|
||||
)
|
||||
|
||||
}
|
||||
|
||||
@ -1,24 +1,18 @@
|
||||
export const MY_PROPOSALS_PREFIX = "MY_PROPOSALS";
|
||||
export const VOTED_PROPOSALS_PREFIX = "VOTED_PROPOSALS";
|
||||
|
||||
export const convertStatusToColor = (status) => {
|
||||
export const convertStatusToTemplate = (status) => {
|
||||
switch (status) {
|
||||
case 1:
|
||||
return "#f2e370";
|
||||
case 2:
|
||||
return "#f06f73";
|
||||
case 3:
|
||||
return "#f06f73";
|
||||
case 4:
|
||||
return "#60c45b";
|
||||
case 5:
|
||||
return "#60c45b";
|
||||
case 6:
|
||||
return "#f06f73";
|
||||
case 7:
|
||||
return "#f2e370";
|
||||
return 'darkGray';
|
||||
case 2:
|
||||
return 'warning';
|
||||
case 4:
|
||||
return 'success';
|
||||
case 3:
|
||||
return 'error';
|
||||
default:
|
||||
return "#a2b7ce";
|
||||
return 'darkGray';
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -52,7 +52,7 @@ export const TotalDeposit = props => {
|
||||
const _props = {
|
||||
...props,
|
||||
label: "Total Deposit",
|
||||
tooltip: `Total reserves of native coins, LP tokens, and other assets in the ghostDAO treasury backing the entire circulating supply of ${props.stnkSymbol}.`,
|
||||
tooltip: `The total stablecoin reserves in the ghostDAO treasury backing the entire circulating supply of ${props.stnkSymbol}.`,
|
||||
};
|
||||
|
||||
if (deposit) _props.metric = `${formatCurrency(deposit, 2)}`;
|
||||
|
||||
@ -84,7 +84,7 @@ export const FatsoBacking = props => {
|
||||
const _props = {
|
||||
...props,
|
||||
label: `Backing per ${props.ftsoSymbol}`,
|
||||
tooltip: `The total amount of native coins, LP tokens, and other assets held by the ghostDAO treasury to support the value of each ${props.ftsoSymbol} in circulation.`
|
||||
tooltip: `The total amount of stablecoins held by the ghostDAO treasury to support the value of each ${props.ftsoSymbol} in circulation.`
|
||||
};
|
||||
|
||||
if (backing) _props.metric = formatCurrency(backing, 2);
|
||||
|
||||
@ -138,7 +138,7 @@ const TokenInfo = ({ chainId, isMobileScreen }) => {
|
||||
tokenName={ghstSymbol}
|
||||
balance={ghstBalance}
|
||||
price={ghstPrice}
|
||||
description={`${ghstSymbol} is the governance token enabling pure Web3 cross-chain magic. 1 ${ghstSymbol} = 1 ${ftsoSymbol} x Current Index.`}
|
||||
description={`${ghstSymbol} enables ghostDAO to have on-chain governance and to be truly cross-chain. ${ghstSymbol} Price = ${ftsoSymbol} Price x Current Index.`}
|
||||
/>
|
||||
<TokenTab
|
||||
isMobileScreen={isMobileScreen}
|
||||
|
||||
@ -52,16 +52,3 @@ export const timeConverter = (time, max = 7200, maxText = "long ago") => {
|
||||
return `${mins}m ${secs < 10 ? '0' : ''}${secs}s`;
|
||||
}
|
||||
}
|
||||
|
||||
export const bigIntSqrt = (n) => {
|
||||
if (n < 0n) return 0n;
|
||||
if (n < 2n) return n;
|
||||
|
||||
let x = n / 2n + 1n;
|
||||
let y = (x + n / x) / 2n;
|
||||
while (y < x) {
|
||||
x = y;
|
||||
y = (x + n / x) / 2n;
|
||||
}
|
||||
return x;
|
||||
}
|
||||
|
||||
@ -2,7 +2,6 @@ import { useReadContract, useReadContracts } from "wagmi";
|
||||
import { simulateContract, writeContract, waitForTransactionReceipt } from "@wagmi/core";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { isNetworkLegacyType } from "../../constants";
|
||||
import { config } from "../../config";
|
||||
|
||||
import {
|
||||
@ -14,17 +13,14 @@ import { abi as BondAbi } from "../../abi/GhostBondDepository.json";
|
||||
import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
|
||||
import { abi as BondingCalculatorAbi } from "../../abi/GhostBondingCalculator.json";
|
||||
|
||||
import { useReservePrice, useFtsoPrice } from "../prices";
|
||||
import { useOrinalCoefficient } from "../treasury";
|
||||
import { useFtsoPrice } from "../prices";
|
||||
import { useTokenSymbol, useTokenSymbols } from "../tokens";
|
||||
import { getTokenAddress, getTokenIcons, getBondNameDisplayName, getTokenPurchaseLink } from "../helpers";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { shorten } from "../../helpers";
|
||||
|
||||
export const useLiveBonds = (chainId) => {
|
||||
const ftsoPrice = useFtsoPrice(chainId);
|
||||
const baseTokenPerUsd = useReservePrice(chainId);
|
||||
const originalCoefficient = useOrinalCoefficient(chainId);
|
||||
const baseTokenPerUsd = useFtsoPrice(chainId);
|
||||
|
||||
const { data: liveIndexesRaw, refetch } = useReadContract({
|
||||
abi: BondAbi,
|
||||
@ -130,11 +126,10 @@ export const useLiveBonds = (chainId) => {
|
||||
const quoteTokenSymbol = quoteTokenSymbols?.at(index).result ? quoteTokenSymbols.at(index).result : "";
|
||||
|
||||
const quoteTokenPerBaseToken = new DecimalBigNumber(marketPrice, 9);
|
||||
const priceInUsd = quoteTokenPerBaseToken.mul(baseTokenPerUsd).mul(quoteTokenPerUsd).mul(markdown).div(originalCoefficient);
|
||||
|
||||
const discount = ftsoPrice._value > 0n
|
||||
? ftsoPrice.sub(priceInUsd).div(ftsoPrice)
|
||||
: new DecimalBigNumber("0", ftsoPrice._decimals);
|
||||
const priceInUsd = quoteTokenPerUsd.mul(quoteTokenPerBaseToken).mul(markdown);
|
||||
const discount = baseTokenPerUsd._value > 0n
|
||||
? baseTokenPerUsd.sub(priceInUsd).div(baseTokenPerUsd)
|
||||
: new DecimalBigNumber("0", baseTokenPerUsd._decimals);
|
||||
|
||||
const capacityInBaseToken = capacityInQuote
|
||||
? new DecimalBigNumber(marketPrice > 0n ? marketCapacity / marketPrice : 0n, 9)
|
||||
@ -176,7 +171,7 @@ export const useLiveBonds = (chainId) => {
|
||||
price: {
|
||||
inUsd: priceInUsd,
|
||||
inBaseToken: quoteTokenPerBaseToken,
|
||||
marketPriceInUsd: ftsoPrice
|
||||
marketPriceInUsd: baseTokenPerUsd
|
||||
},
|
||||
capacity: {
|
||||
inBaseToken: capacityInBaseToken,
|
||||
@ -322,8 +317,7 @@ const executeOnChainTransaction = async (
|
||||
functionName,
|
||||
args,
|
||||
account,
|
||||
chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
@ -8,24 +8,15 @@ import { useUnstableProvider } from "./UnstableProvider"
|
||||
const MetadataProvider = createContext(null)
|
||||
export const useMetadata = () => useContext(MetadataProvider)
|
||||
|
||||
const CACHE_VERSION = "v2"
|
||||
|
||||
export const MetadataProviderProvider = ({ children }) => {
|
||||
const { client, chainId } = useUnstableProvider()
|
||||
const { data: metadata } = useSWR(
|
||||
client && chainId ? ["metadata", client, chainId] : null,
|
||||
async ([_, client]) => {
|
||||
const storageKey = `metadata-${chainId}-${CACHE_VERSION}`
|
||||
const storageKey = `metadata-${chainId}`
|
||||
const storedMetadata = sessionStorage.getItem(storageKey)
|
||||
|
||||
if (storedMetadata) return unifyMetadata(decAnyMetadata(storedMetadata))
|
||||
|
||||
Object.keys(sessionStorage).forEach(key => {
|
||||
if (key.startsWith("metadata-")) {
|
||||
sessionStorage.removeItem(key);
|
||||
}
|
||||
});
|
||||
|
||||
const metadata = await new Promise((resolve, reject) =>
|
||||
client._request("state_getMetadata", [], {
|
||||
onSuccess: resolve,
|
||||
|
||||
@ -12,5 +12,3 @@ export * from "./useBlockCommitments";
|
||||
export * from "./useApplauseDetails";
|
||||
export * from "./useBabeSlots";
|
||||
export * from "./useErasTotalStaked";
|
||||
export * from "./useLatestBlockNumber";
|
||||
export * from "./useEraIndex";
|
||||
|
||||
@ -1,41 +0,0 @@
|
||||
import useSWRSubscription from "swr/subscription"
|
||||
import { getDynamicBuilder, getLookupFn } from "@polkadot-api/metadata-builders"
|
||||
import { distinct, filter, map, mergeMap } from "rxjs"
|
||||
|
||||
import { useUnstableProvider } from "./UnstableProvider"
|
||||
import { useMetadata } from "./MetadataProvider"
|
||||
|
||||
export const useEraIndex = () => {
|
||||
const { chainHead$, chainId } = useUnstableProvider()
|
||||
const metadata = useMetadata()
|
||||
const { data: eraIndex } = useSWRSubscription(
|
||||
chainHead$ && chainId && metadata
|
||||
? ["eraIndex", chainHead$, chainId, metadata]
|
||||
: null,
|
||||
([_, chainHead$, chainId, metadata], { next }) => {
|
||||
const { finalized$, storage$ } = chainHead$
|
||||
const subscription = finalized$.pipe(
|
||||
filter(Boolean),
|
||||
mergeMap((blockInfo) => {
|
||||
const builder = getDynamicBuilder(getLookupFn(metadata))
|
||||
const eraIndex = builder.buildStorage("Staking", "ActiveEra")
|
||||
return storage$(blockInfo?.hash, "value", () =>
|
||||
eraIndex?.keys.enc()
|
||||
).pipe(
|
||||
filter(Boolean),
|
||||
distinct(),
|
||||
map((value) => eraIndex?.value.dec(value))
|
||||
)
|
||||
}),
|
||||
)
|
||||
.subscribe({
|
||||
next(eraIndex) {
|
||||
next(null, eraIndex)
|
||||
},
|
||||
error: next,
|
||||
})
|
||||
return () => subscription.unsubscribe()
|
||||
}
|
||||
)
|
||||
return eraIndex;
|
||||
}
|
||||
@ -5,14 +5,14 @@ import { distinct, filter, map, mergeMap } from "rxjs"
|
||||
import { useUnstableProvider } from "./UnstableProvider"
|
||||
import { useMetadata } from "./MetadataProvider"
|
||||
|
||||
export const useErasTotalStake = ({ epochIndex }) => {
|
||||
export const useErasTotalStake = ({ eraIndex }) => {
|
||||
const { chainHead$, chainId } = useUnstableProvider()
|
||||
const metadata = useMetadata()
|
||||
const { data: eraTotalStake } = useSWRSubscription(
|
||||
chainHead$ && chainId && metadata
|
||||
? ["eraTotalStake", chainHead$, epochIndex, chainId, metadata]
|
||||
? ["eraTotalStake", chainHead$, eraIndex, chainId, metadata]
|
||||
: null,
|
||||
([_, chainHead$, epochIndex, chainId, metadata], { next }) => {
|
||||
([_, chainHead$, eraIndex, chainId, metadata], { next }) => {
|
||||
const { finalized$, storage$ } = chainHead$
|
||||
const subscription = finalized$.pipe(
|
||||
filter(Boolean),
|
||||
@ -20,7 +20,7 @@ export const useErasTotalStake = ({ epochIndex }) => {
|
||||
const builder = getDynamicBuilder(getLookupFn(metadata))
|
||||
const eraTotalStake = builder.buildStorage("Staking", "ErasTotalStake")
|
||||
return storage$(blockInfo?.hash, "value", () =>
|
||||
eraTotalStake?.keys.enc(epochIndex)
|
||||
eraTotalStake?.keys.enc(eraIndex)
|
||||
).pipe(
|
||||
filter(Boolean),
|
||||
distinct(),
|
||||
|
||||
@ -1,18 +0,0 @@
|
||||
import { useState, useEffect } from "react";
|
||||
import useSWRSubscription from "swr/subscription"
|
||||
import { useUnstableProvider } from "./UnstableProvider"
|
||||
|
||||
export const useLatestBlockNumber = () => {
|
||||
const { chainHead$ } = useUnstableProvider();
|
||||
const [blockNumber, setBlockNumber] = useState(null);
|
||||
|
||||
useEffect(() => {
|
||||
if (!chainHead$) return;
|
||||
const subscription = chainHead$.best$.subscribe((block) => {
|
||||
setBlockNumber(block.number);
|
||||
});
|
||||
return () => subscription.unsubscribe();
|
||||
}, [chainHead$]);
|
||||
|
||||
return blockNumber;
|
||||
}
|
||||
@ -1,10 +1,8 @@
|
||||
import { useMemo } from "react";
|
||||
import { useReadContract, useReadContracts } from "wagmi";
|
||||
import { simulateContract, writeContract, waitForTransactionReceipt } from "@wagmi/core";
|
||||
import toast from "react-hot-toast";
|
||||
import { keccak256, stringToBytes } from 'viem'
|
||||
|
||||
import { isNetworkLegacyType } from "../../constants";
|
||||
import { config } from "../../config";
|
||||
|
||||
import {
|
||||
@ -18,23 +16,6 @@ import { abi as GovernorVotesQuorumFractionAbi } from "../../abi/GovernorVotesQu
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { getTokenDecimals, getTokenAbi, getTokenAddress } from "../helpers";
|
||||
|
||||
export const getVoteValue = (forVotes, totalVotes) => {
|
||||
if (totalVotes == 0n) return 0;
|
||||
const value = forVotes * 100n / totalVotes;
|
||||
return Math.floor(Number(value.toString()));
|
||||
}
|
||||
|
||||
export const getVoteTarget = (totalVotes, totalSupply) => {
|
||||
if (totalSupply == 0n) return 80;
|
||||
|
||||
const precision = 10n ** 3n;
|
||||
const valueRaw = (totalVotes * precision) / totalSupply;
|
||||
const value = Number(valueRaw) / Number(precision);
|
||||
|
||||
const result = (5 - Math.sqrt(1 + 80/9 * (value - 0.1) )) / 4;
|
||||
return Math.floor(result * 100);
|
||||
}
|
||||
|
||||
export const useProposalVoteOf = (chainId, proposalId, who) => {
|
||||
const { data, error } = useReadContract({
|
||||
abi: GovernorAbi,
|
||||
@ -115,7 +96,6 @@ export const useMinQuorum = (chainId) => {
|
||||
|
||||
export const useProposalThreshold = (chainId, name) => {
|
||||
const decimals = getTokenDecimals(name);
|
||||
const { proposalCount } = useProposalCount(chainId);
|
||||
|
||||
const { data } = useReadContract({
|
||||
abi: GovernorStorageAbi,
|
||||
@ -133,17 +113,13 @@ export const useProposalThreshold = (chainId, name) => {
|
||||
chainId: chainId,
|
||||
});
|
||||
|
||||
const lastIndex = proposalCount === 0n ? 0n : proposalCount - 1n;
|
||||
const { proposalId } = useProposalDetailsAt(chainId, lastIndex, {
|
||||
enabled: proposalCount !== 0n
|
||||
});
|
||||
const { state } = useProposalState(chainId, proposalId, {
|
||||
enabled: proposalCount !== 0n && !!proposalId
|
||||
});
|
||||
|
||||
let threshold = new DecimalBigNumber(data ?? 0n, decimals);
|
||||
|
||||
if (proposalCount !== 0n && state !== undefined && state < 2) {
|
||||
const { proposalCount } = useProposalCount(chainId);
|
||||
const { proposalId } = useProposalDetailsAt(chainId, proposalCount === 0n ? 0n : proposalCount - 1n);
|
||||
const { state } = useProposalState(chainId, proposalId);
|
||||
|
||||
if (state < 2) {
|
||||
threshold = new DecimalBigNumber(activeProposedLock ?? 0n, decimals);
|
||||
}
|
||||
|
||||
@ -344,22 +320,22 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalDetails } = useReadContracts({
|
||||
contracts: searchedIndexes?.map(proposalId => {
|
||||
contracts: searchedIndexes?.map(index => {
|
||||
return {
|
||||
abi: GovernorStorageAbi,
|
||||
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
||||
functionName: "proposalDetails",
|
||||
args: [proposalId],
|
||||
scopeKey: `proposalDetails-${chainId}-${proposalId}`,
|
||||
args: [index],
|
||||
scopeKey: `proposalDetails-${chainId}-${index}`,
|
||||
chainId: chainId,
|
||||
}
|
||||
})
|
||||
});
|
||||
|
||||
const { data: proposalDeadlines } = useReadContracts({
|
||||
contracts: indexes?.map((_, index) => {
|
||||
contracts: indexes?.map(index => {
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
return {
|
||||
@ -374,9 +350,9 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalVotes } = useReadContracts({
|
||||
contracts: indexes?.map((_, index) => {
|
||||
contracts: indexes?.map(index => {
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
return {
|
||||
@ -391,9 +367,9 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalStates } = useReadContracts({
|
||||
contracts: indexes?.map((_, index) => {
|
||||
contracts: indexes?.map(index => {
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
return {
|
||||
@ -408,9 +384,9 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalSnapshots } = useReadContracts({
|
||||
contracts: indexes?.map((_, index) => {
|
||||
contracts: indexes?.map(index => {
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
return {
|
||||
@ -425,8 +401,8 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalQuorums } = useReadContracts({
|
||||
contracts: proposalSnapshots?.map((proposal, index) => {
|
||||
const timepoint = proposal?.result;
|
||||
contracts: indexes?.map(index => {
|
||||
const timepoint = proposalSnapshots?.at(index)?.result;
|
||||
return {
|
||||
abi: GovernorAbi,
|
||||
address: GHOST_GOVERNANCE_ADDRESSES[chainId],
|
||||
@ -439,8 +415,8 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: pastTotalSupplies } = useReadContracts({
|
||||
contracts: proposalSnapshots?.map((proposal, index) => {
|
||||
const timepoint = proposal?.result;
|
||||
contracts: indexes?.map(index => {
|
||||
const timepoint = proposalSnapshots?.at(index)?.result;
|
||||
return {
|
||||
abi: ghstAbi,
|
||||
address: ghstAddress,
|
||||
@ -453,9 +429,9 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
});
|
||||
|
||||
const { data: proposalProposer } = useReadContracts({
|
||||
contracts: indexes?.map((_, index) => {
|
||||
contracts: indexes?.map(index => {
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
return {
|
||||
@ -469,69 +445,32 @@ export const useProposals = (chainId, depth, searchedIndexes) => {
|
||||
})
|
||||
});
|
||||
|
||||
const hashes = useMemo(() => {
|
||||
return indexes?.map((_, index) => {
|
||||
let result = { short: index + 1, full: undefined };
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(index)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
const hashes = indexes?.map(index => {
|
||||
let result = { short: index + 1, full: undefined };
|
||||
const proposalId = searchedIndexes
|
||||
? searchedIndexes?.at(0)
|
||||
: proposalsDetailsAt?.at(index)?.result?.at(0);
|
||||
|
||||
if (proposalId) {
|
||||
const hash = "0x" + proposalId.toString(16);
|
||||
result.short = hash.slice(-5);
|
||||
result.full = hash;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
}, [indexes, searchedIndexes, proposalsDetailsAt]);
|
||||
if (proposalId) {
|
||||
const hash = "0x" + proposalId.toString(16);
|
||||
result.short = hash.slice(-5);
|
||||
result.full = hash;
|
||||
}
|
||||
return result;
|
||||
});
|
||||
|
||||
const voteValues = useMemo(() => {
|
||||
return indexes?.map((_, idx) => {
|
||||
const index = indexes.length - idx - 1;
|
||||
const againstVotes = proposalVotes?.at(index)?.result?.at(0) ?? 0n
|
||||
const forVotes = proposalVotes?.at(index)?.result?.at(1) ?? 0n;
|
||||
|
||||
const totalVotes = againstVotes + forVotes;
|
||||
return getVoteValue(forVotes, totalVotes);
|
||||
}) ?? [];
|
||||
}, [indexes, proposalVotes]);
|
||||
|
||||
const voteTargets = useMemo(() => {
|
||||
return indexes?.map((_, idx) => {
|
||||
const index = indexes.length - idx - 1;
|
||||
const againstVotes = proposalVotes?.at(index)?.result?.at(0) ?? 0n
|
||||
const forVotes = proposalVotes?.at(index)?.result?.at(1) ?? 0n;
|
||||
const totalSupply = pastTotalSupplies?.at(index)?.result ?? 0n;
|
||||
|
||||
const totalVotes = againstVotes + forVotes;
|
||||
return getVoteTarget(totalVotes, totalSupply);
|
||||
}) ?? [];
|
||||
}, [indexes, proposalVotes, pastTotalSupplies]);
|
||||
|
||||
const proposalDetailsPrepared = useMemo(() => {
|
||||
if (!searchedIndexes) return proposalsDetailsAt;
|
||||
return proposalDetails?.map((obj, index) => {
|
||||
if (!obj?.result) return obj;
|
||||
const proposalId = searchedIndexes.at(index);
|
||||
return {
|
||||
...obj,
|
||||
result: [proposalId, ...obj.result],
|
||||
};
|
||||
}) ?? [];
|
||||
}, [searchedIndexes, proposalsDetailsAt, proposalDetails]);
|
||||
|
||||
const proposals = indexes?.map((_, index) => ({
|
||||
hashes: hashes?.at(index) ?? { short: "", full: "" },
|
||||
const proposals = indexes?.map(index => ({
|
||||
hashes: hashes?.at(index),
|
||||
proposer: proposalProposer?.at(index)?.result,
|
||||
details: proposalDetailsPrepared?.at(index)?.result,
|
||||
details: proposalsDetailsAt?.at(index)?.result,
|
||||
deadline: proposalDeadlines?.at(index)?.result ?? 0n,
|
||||
snapshot: proposalSnapshots?.at(index)?.result ?? 0n,
|
||||
state: proposalStates?.at(index)?.result ?? 0,
|
||||
pastTotalSupply: new DecimalBigNumber(pastTotalSupplies?.at(index)?.result ?? 0n, decimals),
|
||||
quorum: new DecimalBigNumber(proposalQuorums?.at(index)?.result ?? 0n, decimals),
|
||||
votes: proposalVotes?.at(index)?.result?.map(vote => new DecimalBigNumber(vote ?? 0n, decimals)),
|
||||
voteValue: voteValues?.at(index),
|
||||
voteTarget: voteTargets?.at(index),
|
||||
snapshot: new DecimalBigNumber(proposalSnapshots?.at(index)?.result ?? 0n, decimals),
|
||||
votes: proposalVotes?.at(index)?.result?.map(
|
||||
vote => new DecimalBigNumber(vote ?? 0n, decimals),
|
||||
),
|
||||
}));
|
||||
|
||||
return { proposals };
|
||||
@ -545,8 +484,7 @@ export const releaseLocked = async (chainId, account, proposalId) => {
|
||||
functionName: 'releaseLocked',
|
||||
args: [proposalId],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -573,8 +511,7 @@ export const executeProposal = async (chainId, account, proposalId) => {
|
||||
functionName: 'execute',
|
||||
args: [proposalId],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -601,8 +538,7 @@ export const castVote = async (chainId, account, proposalId, support) => {
|
||||
functionName: 'castVote',
|
||||
args: [proposalId, support],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -633,8 +569,7 @@ export const propose = async (chainId, account, functions, description) => {
|
||||
functionName: 'propose',
|
||||
args: [targets, values, calldatas, description],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
@ -116,7 +116,7 @@ export const useReservePrice = (chainId) => {
|
||||
.then(response => {
|
||||
if ('data' in response) {
|
||||
const coinPrice = Number(response?.data?.amount ?? 0.0);
|
||||
const priceInWei = Math.floor(coinPrice * 1e18);
|
||||
const priceInWei = Math.floor(coinPrice * 1e18 / 0.99);
|
||||
setReservePrice(new DecimalBigNumber(BigInt(priceInWei), 18));
|
||||
} else if ('price' in response) {
|
||||
const coinPrice = Number(response?.price ?? 0.0);
|
||||
|
||||
@ -4,7 +4,6 @@ import toast from "react-hot-toast";
|
||||
|
||||
import { config } from "../../config";
|
||||
|
||||
import { isNetworkLegacyType } from "../../constants";
|
||||
import { STAKING_ADDRESSES } from "../../constants/addresses";
|
||||
import { abi as StakingAbi } from "../../abi/GhostStaking.json";
|
||||
|
||||
@ -249,8 +248,7 @@ const executeOnChainTransaction = async (
|
||||
functionName,
|
||||
args,
|
||||
account,
|
||||
chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
@ -4,7 +4,6 @@ import toast from "react-hot-toast";
|
||||
|
||||
import { getTokenAbi, getTokenAddress, getTokenDecimals } from "../helpers";
|
||||
|
||||
import { isNetworkLegacyType } from "../../constants";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { shorten } from "../../helpers";
|
||||
import { tokenNameConverter } from "../../helpers/tokenConverter";
|
||||
@ -200,8 +199,7 @@ export const approveTokens = async (chainId, name, owner, spender, value) => {
|
||||
functionName: 'approve',
|
||||
args: [spender, value],
|
||||
account: owner,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -227,8 +225,7 @@ export const mintDai = async (chainId, account, value) => {
|
||||
args: [account],
|
||||
account: account,
|
||||
value: value,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -253,8 +250,7 @@ export const burnDai = async (chainId, account, value) => {
|
||||
functionName: 'burn',
|
||||
args: [value],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
@ -279,8 +275,7 @@ export const depositNative = async (chainId, account, value) => {
|
||||
functionName: 'deposit',
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
value: value,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
value: value
|
||||
});
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
@ -305,8 +300,7 @@ export const withdrawWeth = async (chainId, account, value) => {
|
||||
functionName: 'withdraw',
|
||||
args: [value],
|
||||
account: account,
|
||||
chainId: chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId: chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
@ -5,7 +5,6 @@ import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
|
||||
|
||||
import { useReservePrice } from "../prices/index";
|
||||
|
||||
import { bigIntSqrt } from "../../helpers";
|
||||
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
|
||||
import { getTokenAddress } from "../helpers";
|
||||
|
||||
@ -41,9 +40,6 @@ export const useTotalReserves = (chainId) => {
|
||||
|
||||
export const useLpValuation = (chainId, pairAddress, pairSupply) => {
|
||||
const convertedPairAddress = getTokenAddress(chainId, pairAddress);
|
||||
const originalCoefficient = useOrinalCoefficient(chainId);
|
||||
const reservePrice = useReservePrice(chainId);
|
||||
|
||||
const { data: valuationRaw } = useReadContract({
|
||||
abi: TreasuryAbi,
|
||||
address: DAO_TREASURY_ADDRESSES[chainId],
|
||||
@ -56,18 +52,7 @@ export const useLpValuation = (chainId, pairAddress, pairSupply) => {
|
||||
chainId,
|
||||
});
|
||||
|
||||
const sqrtReservePrice = reservePrice?._value
|
||||
? bigIntSqrt(reservePrice._value)
|
||||
: 0n;
|
||||
|
||||
const sqrtOriginalCoefficient = originalCoefficient?._value
|
||||
? bigIntSqrt(originalCoefficient._value)
|
||||
: 1n;
|
||||
|
||||
const valuationPrepared = valuationRaw
|
||||
? valuationRaw * sqrtReservePrice / sqrtOriginalCoefficient
|
||||
: 0n;
|
||||
|
||||
const valuationPrepared = valuationRaw ? valuationRaw : 0n;
|
||||
const valuation = new DecimalBigNumber(valuationPrepared, 9);
|
||||
|
||||
return valuation;
|
||||
|
||||
@ -1,7 +1,6 @@
|
||||
import { simulateContract, writeContract, waitForTransactionReceipt } from "@wagmi/core";
|
||||
import toast from "react-hot-toast";
|
||||
|
||||
import { isNetworkLegacyType } from "../../constants";
|
||||
import { UNISWAP_V2_ROUTER } from "../../constants/addresses";
|
||||
import { abi as RouterAbi } from "../../abi/UniswapV2Router.json";
|
||||
import { getTokenAddress } from "../helpers";
|
||||
@ -94,8 +93,7 @@ const executeOnChainTransaction = async (
|
||||
functionName,
|
||||
args,
|
||||
account,
|
||||
chainId,
|
||||
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
|
||||
chainId
|
||||
});
|
||||
|
||||
const txHash = await writeContract(config, request);
|
||||
|
||||
Loading…
Reference in New Issue
Block a user