apply small changes for the app

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-07-12 13:57:02 +03:00
parent 2fca4651cc
commit e244299465
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
37 changed files with 342 additions and 144 deletions

View File

@ -1,7 +1,7 @@
{
"name": "ghost-dao-interface",
"private": true,
"version": "0.7.50",
"version": "0.7.51",
"type": "module",
"scripts": {
"dev": "vite",

View File

@ -198,7 +198,7 @@ function App() {
<Route path="dashboard" element={<TreasuryDashboard chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="bonds" element={<Bonds connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="bonds/:id" element={<BondModalContainer config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="stake" element={<StakeContainer connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="stake" element={<StakeContainer config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="bridge" element={<Bridge config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="dex/:name" element={<Dex config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />
<Route path="governance" element={<Governance config={config} connect={tryConnectInjected} address={address} chainId={addressChainId ? addressChainId : chainId} />} />

View File

@ -84,22 +84,20 @@ const Modal = ({
// getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = useState(getModalStyle);
const closeButton = (
<IconButton
<GhostStyledIcon
aria-label="close"
color="inherit"
size="small"
component={CloseIcon}
sx={{ cursor: "pointer" }}
onClick={e => {
if (props.onClose) {
props.onClose(e, "escapeKeyDown");
}
}}
>
<GhostStyledIcon component={CloseIcon} />
</IconButton>
/>
);
//modal must have a close position. Close position and topLeft or topRight cant be used for the same position.
const topRightPos = closePosition === "right" ? closeButton : topRight;
const topRightPos = topRight ? topRight : closeButton;
const topLeftPos = closePosition === "left" ? closeButton : topLeft;
return (

View File

@ -64,7 +64,7 @@ const Paper = ({
{(topLeft || headerText || topRight || headerContent) && (
<Grid item className="card-header">
<Box display="flex" justifyContent="space-between">
{topLeft && <div className="top-left">{topLeft}</div>}
{topLeft && <Box className="top-left" display="flex" justifyContent="center" alignItems="center">{topLeft}</Box>}
{headerText && !headerContent && (
<Box display="flex" flexDirection="row" alignItems="center">
<Typography fontSize="24px" className="header-text" fontWeight={700} lineHeight="33px">
@ -83,7 +83,7 @@ const Paper = ({
</Box>
)}
{headerContent}
<div className="top-right">{topRight}</div>
<Box className="top-right" display="flex" justifyContent="center" alignItems="center">{topRight}</Box>
</Box>
{subHeader && <Box display="flex">{subHeader}</Box>}
</Grid>

View File

@ -44,6 +44,7 @@ const SwapCard = ({
tokenName,
inputWidth,
inputFontSize,
chainTokenName,
maxWidth = "416px",
...props
}) => {
@ -72,12 +73,26 @@ const SwapCard = ({
cursor: tokenOnClick ? "pointer" : "default",
}}
borderRadius="6px"
paddingX="9px"
paddingY="6.5px"
paddingX="7px"
paddingY="2px"
alignItems="center"
{...onClickProps}
>
{typeof token === "string" ? <Token name={token} sx={{ fontSize: "21px" }} /> : token}
{typeof token === "string"
? <Token
chainTokenName={chainTokenName}
name={token}
parentSx={{
"& svg:nth-of-type(2)": {
transform: "scale(0.52)",
}
}}
sx={{
transform: "scale(0.65)",
transformOrigin: "center"
}}
/>
: token}
{(typeof token === "string" || tokenName) && (
<Typography fontSize="15px" lineHeight="24px" marginLeft="9px">

View File

@ -69,27 +69,28 @@ export const parseKnownToken = (name) => {
return icon;
}
const Token = ({ chainTokenName, name, viewBox = "0 0 260 260", fontSize = "large", ...props }) => {
const Token = ({ chainTokenName, name, isNative, parentSx, viewBox = "0 0 260 260", fontSize = "large", ...props }) => {
return (
<Box display="flex" justifyContent="center" alignItems="center" position="relative">
<Box display="flex" justifyContent="center" alignItems="center" position="relative" sx={{ ...parentSx }} >
<StyledSvgIcon
inheritViewBox
fontSize={fontSize}
component={parseKnownToken(name)}
style={{ animation: `spinTokenIcon ${isNative ? 10 : 0}s linear infinite` }}
{...props}
></StyledSvgIcon>
{chainTokenName && (
<StyledSvgIcon
inheritViewBox
component={parseKnownToken(chainTokenName)}
style={{
sx={{
position: "absolute",
marginLeft: "70%",
marginTop: "45%",
width: "45%",
height: "45%",
bottom: 0,
right: 0,
transform: "translate(15%, 15%) scale(0.8)",
borderRadius: "100%",
zIndex: 10,
border: "1px solid #fff",
borderRadius: "100%"
}}
></StyledSvgIcon>
)}

View File

@ -1,11 +1,12 @@
import { Avatar, Box } from "@mui/material";
import Token from "../Token/Token";
const TokenStack = ({ tokens, style, images, network, ...props }) => {
const TokenStack = ({ chainTokenNames, tokens, style, images, network, ...props }) => {
const imageStyles = {
height: "27px",
width: "27px",
...style,
};
return (
<Box display="flex" flexDirection="row" marginTop={network ? "3px" : "0px"} marginLeft={network ? "3px" : "0px"}>
@ -20,18 +21,25 @@ const TokenStack = ({ tokens, style, images, network, ...props }) => {
<Avatar
src={image}
style={{
...(i !== 0 ? { marginLeft: -7, zIndex: 1, ...imageStyles } : { zIndex: 1, ...imageStyles }),
...(i !== 0 ? { marginLeft: -7, zIndex: 100 - i, ...imageStyles } : { zIndex: 100 - i, ...imageStyles }),
}}
/>
))}
{tokens?.map((token, i) => (
<Token
{...props}
key={i}
name={token}
style={{
...(i !== 0 ? { marginLeft: -12, zIndex: 1, ...style } : { zIndex: 2, ...style }),
...(i !== 0 ? { marginLeft: -12, zIndex: 100 - i, ...style } : { zIndex: 100 - i, ...style }),
}}
parentSx={{
"& svg:nth-of-type(2)": {
transform: "translate(22%, 22%) scale(0.6)",
zIndex: 100,
}
}}
chainTokenName={chainTokenNames?.at(i)}
{...props}
/>
))}
</Box>

View File

@ -132,7 +132,6 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
useEffect(() => {
if (isWalletOpen) {
console.log(`/${chainName.toLowerCase()}/sidebar`)
ReactGA.send({ hitType: "pageview", page: `/${chainName.toLowerCase()}/sidebar` });
}
}, [isWalletOpen, chainName])
@ -143,7 +142,6 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
}
}, [isWalletOpen, tokens])
return (
<Paper>
<Box sx={{ padding: theme.spacing(0, 3), display: "flex", flexDirection: "column", minHeight: "100vh" }}>

View File

@ -130,6 +130,7 @@ export const Token = (props) => {
height: "28px",
}}
tokens={props.icons}
chainTokenNames={props.chainTokenNames}
/>
<Typography>{symbol}</Typography>
</Box>
@ -207,7 +208,7 @@ export const useWallet = (chainId, userAddress) => {
const nativeSymbol = useMemo(() => {
return config?.getClient()?.chain?.nativeCurrency?.symbol;
}, [config]);
}, [chainId, config]);
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
@ -223,7 +224,7 @@ export const useWallet = (chainId, userAddress) => {
if (token0?.at(0) === reserveSymbol) {
tokenAddresses = [lpReserveFtsoTokens?.token0, lpReserveFtsoTokens?.token1];
let tokenNames = [...token0, ...token1];
tokenNames = [...token0, ...token1];
}
return { tokenAddresses, tokenNames }
@ -251,6 +252,7 @@ export const useWallet = (chainId, userAddress) => {
address: ftsoAddress,
balance: ftsoBalance,
price: ftsoPrice,
chainTokenNames: [nativeSymbol],
icons: ["FTSO"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/eGHST.svg",
refetch: ftsoRefetch,
@ -261,6 +263,7 @@ export const useWallet = (chainId, userAddress) => {
balance: ghstBalance,
price: ghstPrice,
icons: ["GHST"],
chainTokenNames: [nativeSymbol],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg",
refetch: ghstRefetch,
},
@ -271,6 +274,10 @@ export const useWallet = (chainId, userAddress) => {
balance: lpReserveFtsoBalance,
price: lpReserveFtsoPrice,
icons: lpReserveFtsoTokenNames?.tokenNames,
chainTokenNames: [
lpReserveFtsoTokenNames?.tokenNames?.at(0) !== reserveSymbol && nativeSymbol,
lpReserveFtsoTokenNames?.tokenNames?.at(1) !== reserveSymbol && nativeSymbol,
],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg",
refetch: lpReserveFtsoRefetch,
}

View File

@ -9,6 +9,7 @@ export const AVAILABLE_DEXES = {
name: "Uniswap",
icon: UniswapIcon,
viewBox: "0 0 195 230",
isFork: true,
},
],
[NetworkId.TESTNET_HOODI]: [
@ -16,6 +17,7 @@ export const AVAILABLE_DEXES = {
name: "Uniswap",
icon: UniswapIcon,
viewBox: "0 0 195 230",
isFork: true,
},
],
[NetworkId.TESTNET_MORDOR]: [
@ -23,6 +25,7 @@ export const AVAILABLE_DEXES = {
name: "Uniswap",
icon: UniswapIcon,
viewBox: "0 0 195 230",
isFork: true,
},
],
};

View File

@ -91,6 +91,7 @@ export const BondModal = ({ bond, chainId, address, config, connect }) => {
if (isNetworkLegacy(chainId)) {
return {
address: bond.quoteToken.quoteTokenAddress,
chainNames: bond.quoteToken.chainNames,
icons: bond.quoteToken.icons,
name: bond.quoteToken.name,
}
@ -112,7 +113,11 @@ export const BondModal = ({ bond, chainId, address, config, connect }) => {
</Box>
</Link>
<TokenStack tokens={preparedQuoteToken.icons} sx={{ fontSize: "27px" }} />
<TokenStack
chainTokenNames={preparedQuoteToken.chainNames}
tokens={preparedQuoteToken.icons}
sx={{ fontSize: "27px" }}
/>
<Box display="flex" flexDirection="column" ml={1} justifyContent="center" alignItems="center">
<Typography variant="h4" fontWeight={500}>
{preparedQuoteToken.name}

View File

@ -42,6 +42,7 @@ const BondConfirmModal = ({
isNative,
bondQuoteTokenName,
bondQuoteTokenIcons,
bondQuoteTokenChainNames,
handleConfirmClose
}) => {
const theme = useTheme();
@ -127,16 +128,16 @@ const BondConfirmModal = ({
maxWidth="476px"
minHeight="200px"
open={isOpen}
headerContent={
<Box display="flex" flexDirection="row">
<TokenStack tokens={bondQuoteTokenIcons} sx={{ fontSize: "27px" }} />
<Typography variant="h4" sx={{ marginLeft: "6px" }}>
{bondQuoteTokenName}
</Typography>
</Box>
}
headerContent={<Typography variant="h4">{bondQuoteTokenName} Bond</Typography>}
onClose={!isPending && handleConfirmCloseMaster}
topLeft={<GhostStyledIcon viewBox="0 0 23 23" component={SettingsIcon} style={{ cursor: "pointer" }} onClick={handleSettingsOpen} />}
topRight={
<GhostStyledIcon
viewBox="0 0 23 23"
component={SettingsIcon}
style={{ cursor: "pointer" }}
onClick={handleSettingsOpen}
/>
}
>
<>
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
@ -145,19 +146,58 @@ const BondConfirmModal = ({
label="Assets to Bond"
metric={spendAmount}
/>
<Box display="flex" flexDirection="row" justifyContent="center">
<Box
sx={{ background: "" }}
height="120px"
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="center"
>
<TokenStack
parentSx={{
height: "80px",
"& svg:nth-of-type(2)": {
transform: "translate(50%, -30%)",
zIndex: 100,
}
}}
sx={{ transform: "scale(1.65)", }}
tokens={bond.quoteToken.icons}
chainTokenNames={bond.quoteToken.chainNames}
/>
<Typography>{bondQuoteTokenName}</Typography>
</Box>
</Box>
<GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} />
<Box display="flex" flexDirection="column">
<Metric label="Assets to Receive" metric={receiveAmount} />
<Box display="flex" flexDirection="row" justifyContent="center">
<Box
sx={{ background: "" }}
height="120px"
display="flex"
flexDirection="column"
justifyContent="center"
alignItems="center"
gap="0"
>
<TokenStack
parentSx={{
height: "80px",
"& svg:nth-of-type(2)": {
transform: "translate(50%, -30%)",
zIndex: 100,
}
}}
sx={{ transform: "scale(1.65)", }}
tokens={bond.baseToken.icons}
chainTokenNames={bond.baseToken.chainNames}
/>
<Typography>{bond.baseToken.name}</Typography>
</Box>
</Box>
</Box>
<Box mt="21px" mb="21px" borderTop={`1px solid ${theme.colors.gray[500]}`}></Box>
<Box mt="10px" mb="18px" borderTop={`1px solid ${theme.colors.gray[500]}`}></Box>
<DataRow title="ROI" balance={<BondDiscount discount={bond.discount} textOnly />} />
<DataRow title="Bond Slippage" balance={<BondSlippage slippage={slippage} textOnly />} />
<DataRow title="Vesting Term" balance={<BondVesting vesting={bond.vesting} />} />

View File

@ -100,12 +100,19 @@ const BondInputArea = ({
<Box display="flex" flexDirection="column" width="100%" maxWidth="476px">
<Box mb="21px">
<SwapCollection
iconNotNeeded
UpperSwapCard={
<SwapCard
maxWidth="476px"
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
id="from"
token={<TokenStack tokens={bond.quoteToken.icons} sx={{ fontSize: "21px" }} />}
token={
<TokenStack
chainTokenNames={bond.quoteToken.chainNames}
tokens={bond.quoteToken.icons}
sx={{ fontSize: "25px", height: "30px" }}
/>
}
tokenName={preparedQuoteToken.name}
info={formatCurrency(balance, formatDecimals, preparedQuoteToken.name)}
endString="Max"
@ -119,7 +126,13 @@ const BondInputArea = ({
maxWidth="476px"
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
id="to"
token={<TokenStack tokens={bond.baseToken.icons} sx={{ fontSize: "21px" }} />}
token={
<TokenStack
chainTokenNames={bond.baseToken.chainNames}
tokens={bond.baseToken.icons}
sx={{ fontSize: "25px", height: "30px" }}
/>
}
tokenName={bond.baseToken.name}
value={amountInBaseToken.toString({ decimals: 9 })}
inputProps={{ "data-testid": "toInput" }}
@ -229,6 +242,7 @@ const BondInputArea = ({
receiveAmount={formatNumber(amountInBaseToken, formatDecimals)}
bondQuoteTokenName={preparedQuoteToken.name}
bondQuoteTokenIcons={preparedQuoteToken.icons}
bondQuoteTokenChainNames={preparedQuoteToken.chainNames}
isNative={preparedQuoteToken.address === undefined}
handleSettingsOpen={handleSettingsOpen}
isOpen={confirmOpen}

View File

@ -72,7 +72,7 @@ const BondCard = ({ bond, secondsTo, chainName }) => {
return (
<Box key={bond.id} id={bond.id + `--bond`} mt="32px">
<Box display="flex" alignItems="center">
<TokenStack tokens={bond.quoteToken.icons} />
<TokenStack chainTokenNames={bond.quoteToken.chainNames} tokens={bond.quoteToken.icons} />
<Box display="flex" flexDirection="column" ml="8px">
<Typography>{bond.quoteToken.name}</Typography>
@ -237,7 +237,7 @@ const BondRow = ({ bond, secondsTo, chainName }) => {
const TokenIcons = ({ token, chainId, explorer }) => (
<Box display="flex" alignItems="center">
<TokenStack tokens={token.icons} />
<TokenStack chainTokenNames={token.chainNames} tokens={token.icons} />
<Box display="flex" flexDirection="column" ml="16px">
<Typography style={{ fontSize: "12px", fontWeight: 600, lineHeight: "18px" }}>{token.name}</Typography>

View File

@ -103,7 +103,7 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
headerContent={
<Box display="flex" alignItems="center" flexDirection="row" gap="5px">
<Typography variant="h6">
Your Bonds
My Bonds
</Typography>
</Box>
}
@ -143,7 +143,7 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
{notes.map((note, index) => (
<Box key={index} mt="32px">
<Box display="flex" alignItems="center">
<TokenStack tokens={note.quoteToken.icons} />
<TokenStack chainTokenNames={note.quoteToken.chainNames} tokens={note.quoteToken.icons} />
<Box ml="8px">
<Typography>{note.quoteToken.name}</Typography>
</Box>
@ -205,7 +205,7 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
<TableRow key={index}>
<TableCell style={{ padding: "8px 0" }}>
<Box display="flex" alignItems="center">
<TokenStack tokens={note.quoteToken.icons} />
<TokenStack chainTokenNames={note.quoteToken.chainNames} tokens={note.quoteToken.icons} />
<Box display="flex" flexDirection="column" ml="16px">
<Typography>{note.quoteToken.name}</Typography>
</Box>

View File

@ -5,7 +5,8 @@ import { getBlockNumber } from "@wagmi/core";
import { useConfig } from "wagmi";
import { ss58Decode } from "@polkadot-labs/hdkd-helpers";
import { toHex } from "@polkadot-api/utils";
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
import CheckIcon from '@mui/icons-material/CheckCircle';
import CancelIcon from '@mui/icons-material/Cancel';
import ArrowDropDownIcon from '@mui/icons-material/ArrowDropDown';
import { CheckBoxOutlineBlank, CheckBoxOutlined } from "@mui/icons-material";
@ -165,6 +166,7 @@ const BridgeView = ({
incomingFee
}) => {
const theme = useTheme();
const [inputFocused, setInputFocused] = useState(false);
const config = useConfig();
const { gatekeeperAddress } = useGatekeeperAddress(chainId);
@ -189,12 +191,16 @@ const BridgeView = ({
<SwapCard
id={`bridge-token-receiver`}
inputWidth={"100%"}
value={convertedReceiver ? shorten(receiver, 15, -10) : receiver}
onChange={event => setReceiver(convertedReceiver ? "" : event.currentTarget.value)}
onBlur={() => setInputFocused(true)}
onFocus={() => setInputFocused(false)}
onChange={event => setReceiver(event.currentTarget.value)}
placeholder={convertedReceiver ? shorten(receiver, 15, -10) : "GHOST address (sf prefixed)"}
value={convertedReceiver ? "" : receiver}
inputProps={{ "data-testid": "fromInput" }}
placeholder="GHOST address (sf prefixed)"
endString={convertedReceiver
? <GhostStyledIcon color="success" viewBox="0 0 25 25" component={CheckCircleIcon} />
? <GhostStyledIcon color="success" viewBox="0 0 25 25" component={CheckIcon} />
: inputFocused && receiver !== ""
? <GhostStyledIcon color="error" viewBox="0 0 25 25" component={CancelIcon} />
: undefined
}
type="text"
@ -274,7 +280,7 @@ const WelcomeView = ({
<>
<Typography>{warmupPeriod <= 0
? `You have succesfully warmed-up your ${isStakingOpened ? " " : "bonded "}${ftsoSymbol} ${isStakingOpened ? "(3, 3)" : "(1, 1)"} Staked at:`
: `${isStakingOpened ? "Stake" : "Bond"} is in warm-up${isStakingOpened ? "" : ", which extends with each purchase"}. Your ${ftsoSymbol} ${isStakingOpened ? "(3, 3)" : "(1, 1)"} is Staked at:`
: `${isStakingOpened ? "Stake" : "Bond"} is in warm-up${isStakingOpened ? "" : ", which extends with each purchase"}. Your ${ftsoSymbol} (3, 3) Staked at:`
}</Typography>
<Box display="flex" justifyContent="center">
@ -421,7 +427,7 @@ const ConfirmStep = ({
<Box display="flex" flexDirection="column" justifyContent="space-between" alignItems="center" gap="10px">
<Metric label="To Receive" metric={formatNumber(receivedEstimation, 5)} />
<Box width="100%" display="flex" flexDirection="column" justifyContent="center" alignItems="center">
<Token name={"GHST"} sx={{ fontSize: "55px" }} />
<Token isNative name={"GHST"} sx={{ fontSize: "55px" }} />
<Typography>{ghstSymbol}</Typography>
</Box>
</Box>
@ -493,7 +499,7 @@ const ConfirmStep = ({
disabled={isPending || !acknowledgeWalletCustody || !acknowledgeBridgingRisk}
fullWidth
>
{isPending ? "Confirming..." : "I Confirm"}
{isPending ? "Confirming..." : "Confirm"}
</PrimaryButton>
</>
)

View File

@ -28,7 +28,8 @@ import SwapCollection from "../../components/Swap/SwapCollection";
import { ghost } from "../../hooks/staking";
import { useBalance } from "../../hooks/tokens";
import CheckIcon from '@mui/icons-material/Check';
import CheckIcon from '@mui/icons-material/CheckCircle';
import CancelIcon from '@mui/icons-material/Cancel';
import HourglassBottomIcon from '@mui/icons-material/HourglassBottom';
import CheckCircleIcon from '@mui/icons-material/CheckCircle';
@ -58,6 +59,7 @@ export const BridgeCardAction = ({
storeTransactionHash,
}) => {
const [isPending, setIsPending] = useState(false);
const [inputFocused, setInputFocused] = useState(false);
const [receiver, setReceiver] = useState("");
const [convertedReceiver, setConvertedReceiver] = useState(undefined);
const [amount, setAmount] = useState("");
@ -166,12 +168,16 @@ export const BridgeCardAction = ({
UpperSwapCard={<SwapCard
id={`bridge-token-receiver`}
inputWidth={"100%"}
value={convertedReceiver ? sliceString(receiver, 15, -10) : receiver}
onChange={event => setReceiver(convertedReceiver ? "" : event.currentTarget.value)}
value={convertedReceiver ? "" : receiver}
onBlur={() => setInputFocused(true)}
onFocus={() => setInputFocused(false)}
onChange={event => setReceiver(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }}
placeholder="GHOST address (sf prefixed)"
placeholder={convertedReceiver ? sliceString(receiver, 15, -10) : "GHOST address (sf prefixed)"}
endString={convertedReceiver
? <GhostStyledIcon color="success" viewBox="0 0 25 25" component={CheckCircleIcon} />
: inputFocused && receiver !== ""
? <GhostStyledIcon color="error" viewBox="0 0 25 25" component={CancelIcon} />
: undefined
}
type="text"

View File

@ -33,11 +33,23 @@ const RouteHop = ({ theme, token, chainTokenName, arrowNeeded }) => {
sx={{ backgroundColor: theme.colors.gray[600] }}
borderRadius="6px"
paddingX="9px"
paddingY="6.5px"
paddingY="2px"
alignItems="center"
>
<Token chainTokenName={chainTokenName} name={token} sx={{ fontSize: "21px" }} />
<Typography fontSize="15px" lineHeight="24px" marginLeft="9px">
<Token
chainTokenName={chainTokenName}
name={token}
parentSx={{
"& svg:nth-of-type(2)": {
transform: "scale(0.52)",
}
}}
sx={{
transform: "scale(0.65)",
transformOrigin: "center"
}}
/>
<Typography fontSize="15px" lineHeight="24px" marginLeft="5px">
{token}
</Typography>
</Box>

View File

@ -32,6 +32,7 @@ import {
EMPTY_ADDRESS,
WETH_ADDRESSES,
} from "../../constants/addresses";
import { AVAILABLE_DEXES } from "../../constants/dexes";
import { useLocalStorage } from "../../hooks/localstorage";
import { useTokenSymbol } from "../../hooks/tokens";
import { getTokenAddress } from "../../hooks/helpers";
@ -184,6 +185,13 @@ const Dex = ({ chainId, address, connect, config }) => {
setSearchParams(newQueryParameters);
}
const isDexFork = useMemo(() => {
const dexInfo = AVAILABLE_DEXES[chainId]?.find(elem =>
elem.name.toLowerCase() == pathname.name.toLowerCase()
)
return dexInfo ? dexInfo.isFork : false;
}, [chainId, pathname])
const setSlippageInner = useCallback((value) => {
const maybeValue = parseFloat(value);
if (!maybeValue || parseFloat(value) <= 100) {
@ -242,7 +250,7 @@ const Dex = ({ chainId, address, connect, config }) => {
<PageTitle
name="DEX Swap"
subtitle={`(3, 3) Swap ${chainSymbol} for ${ftsoSymbol} and ${ftsoSymbol}-${chainSymbol} LP powered by ${pathname.name.charAt(0).toUpperCase() + pathname.name.slice(1).toLowerCase()} V2`}
subtitle={`(3, 3) Swap ${chainSymbol} for ${ftsoSymbol} and ${ftsoSymbol}-${chainSymbol} LP powered by ${pathname.name.charAt(0).toUpperCase() + pathname.name.slice(1).toLowerCase()} V2${isDexFork ? " Fork" : ""}`}
/>
<Container
style={{
@ -397,6 +405,7 @@ const Dex = ({ chainId, address, connect, config }) => {
<Box height="350px">
{isSwap ?
<SwapContainer
chainSymbol={chainSymbol}
tokenNameTop={tokenNameTop}
tokenNameBottom={tokenNameBottom}
onCardsSwap={onCardsSwap}
@ -416,6 +425,7 @@ const Dex = ({ chainId, address, connect, config }) => {
/>
:
<PoolContainer
chainSymbol={chainSymbol}
tokenNameTop={tokenNameTop}
tokenNameBottom={tokenNameBottom}
onCardsSwap={onCardsSwap}

View File

@ -20,6 +20,7 @@ import {
} from "../../hooks/uniswapv2";
const PoolContainer = ({
chainSymbol,
tokenNameTop,
tokenNameBottom,
onCardsSwap,
@ -219,7 +220,8 @@ const PoolContainer = ({
UpperSwapCard={
<SwapCard
id="from"
token={<TokenStack tokens={[tokenNameTop]} sx={{ fontSize: "21px" }} />}
token={tokenNameTop}
chainTokenName={tokenNameTop !== chainSymbol && chainSymbol}
tokenName={tokenNameTop}
info={(!isSmallScreen ? "Balance: " : "") + formatCurrency(balanceTop, formatDecimals, tokenNameTop)}
endString="Max"
@ -233,7 +235,8 @@ const PoolContainer = ({
LowerSwapCard={
<SwapCard
id="to"
token={<TokenStack tokens={[tokenNameBottom]} sx={{ fontSize: "21px" }} />}
token={tokenNameBottom}
chainTokenName={tokenNameBottom !== chainSymbol && chainSymbol}
tokenName={tokenNameBottom}
value={amountBottom}
onChange={event => emptyPool ? setAmountBottom(event.currentTarget.value) : {}}

View File

@ -25,6 +25,7 @@ import {
import { EMPTY_ADDRESS } from "../../constants/addresses";
const SwapContainer = ({
chainSymbol,
tokenNameTop,
tokenNameBottom,
address,
@ -212,7 +213,8 @@ const SwapContainer = ({
<SwapCard
maxWidth="356px"
id="from"
token={<TokenStack tokens={[tokenNameTop]} sx={{ fontSize: "21px" }} />}
token={tokenNameTop}
chainTokenName={tokenNameTop !== chainSymbol && chainSymbol}
tokenName={tokenNameTop}
info={
(!isSmallScreen ? "Balance: " : "") +
@ -230,8 +232,8 @@ const SwapContainer = ({
<SwapCard
maxWidth="356px"
id="to"
token={<TokenStack tokens={[tokenNameBottom]} sx={{ fontSize: "21px" }} />}
tokenName={tokenNameBottom}
token={tokenNameBottom}
chainTokenName={tokenNameBottom !== chainSymbol && chainSymbol}
value={amountBottom}
inputProps={{ "data-testid": "toInput" }}
tokenOnClick={() => setBottomTokenListOpen(true)}

View File

@ -15,7 +15,7 @@ import { isAddress, getAddress } from "@ethersproject/address";
import Modal from "../../components/Modal/Modal";
import SwapCard from "../../components/Swap/SwapCard";
import TokenStack from "../../components/TokenStack/TokenStack";
import Token from "../../components/Token/Token";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { formatNumber } from "../../helpers/";
@ -65,26 +65,24 @@ const TokenModal = ({ chainId, account, chainSymbol, listOpen, setListOpen, setT
return [
{
name: chainSymbol,
icons: [chainSymbol],
balance: nativeBalance,
address: EMPTY_ADDRESS,
},
{
name: reserveSymbol,
icons: [chainSymbol],
balance: reserveBalance,
address: RESERVE_ADDRESSES[chainId]
},
{
name: ftsoSymbol,
icons: ["FTSO"],
balance: ftsoBalance,
chainTokenName: chainSymbol,
address: FTSO_ADDRESSES[chainId]
},
{
name: ghstSymbol,
icons: ["GHST"],
balance: ghstBalance,
chainTokenName: chainSymbol,
address: GHST_ADDRESSES[chainId]
}
]
@ -105,7 +103,7 @@ const TokenModal = ({ chainId, account, chainSymbol, listOpen, setListOpen, setT
maxWidth="476px"
minHeight="200px"
open={listOpen}
headerText={"Select a token"}
headerText={"Select a Token"}
onClose={() => setListOpen(false)}
>
<SwapCard
@ -132,10 +130,18 @@ const TokenModal = ({ chainId, account, chainSymbol, listOpen, setListOpen, setT
return (
<TableRow onClick={() => setUsedAddress(token)} hover key={index} id={index + `--token`} data-testid={index + `--token`}>
<TableCell style={{ padding: "8px 0" }}>
<TokenStack style={{ width: "32px", height: "32px" }} tokens={token.icons} />
<Token
chainTokenName={token.chainTokenName}
name={token.name}
parentSx={{
"& svg:nth-of-type(2)": {
transform: "translate(-5%, 10%) scale(0.8)",
}
}}
/>
</TableCell>
<TableCell style={{ padding: "8px 0", height: "32px" }}>
<TableCell style={{ padding: "8px", height: "32px" }}>
<Typography>{token.name}</Typography>
</TableCell>

View File

@ -110,8 +110,6 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
const { pastTotalSupply: totalSupply } = usePastTotalSupply(chainId, "GHST", proposalSnapshot);
const { pastVotes } = usePastVotes(chainId, "GHST", proposalSnapshot, address);
console.log(location.pathname)
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: `${chainId}/governance/${id}` });
}, []);
@ -239,10 +237,10 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
<Box display="flex" flexDirection="column">
<Box display="flex" justifyContent="space-between">
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.success}>
For: {formatNumber(forVotes.toString(), 2)} ({formatNumber(forPercentage?.toString(), 1)}%)
For: {formatNumber(forVotes.toString(), 2)} ({formatNumber(forPercentage?.toString(), 2)}%)
</Typography>
<Typography sx={{ textShadow: "0 0 black", fontWeight: 600 }} variant="body1" color={theme.colors.feedback.error}>
Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstPercentage?.toString(), 1)}%)
Against: {formatNumber(againstVotes.toString(), 2)} ({formatNumber(againstPercentage?.toString(), 2)}%)
</Typography>
</Box>
<LinearProgressBar
@ -263,7 +261,7 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
</Box>
<Box display="flex" justifyContent="space-between">
<Typography>Locked</Typography>
<Typography>{formatCurrency(proposalLocked, 4, ghstSymbol)}</Typography>
<Typography>{formatCurrency(proposalLocked, 5, ghstSymbol)}</Typography>
</Box>
<hr width="100%" />
@ -273,7 +271,7 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
<Typography>Quorum</Typography>
<InfoTooltip message={`Minimum $${ghstSymbol} turnout required for the proposal to become valid, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`} />
</Box>
<Typography>{formatNumber(proposalQuorum.toString(), 4)} ({formatNumber(quorumPercentage, 1)}%)</Typography>
<Typography>{formatNumber(proposalQuorum.toString(), 5)} ({formatNumber(quorumPercentage, 2)}%)</Typography>
</Box>
<Box display="flex" justifyContent="space-between">
@ -281,7 +279,7 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
<Typography>Total</Typography>
<InfoTooltip message={`Total votes for the proposal, as percentage of the total $${ghstSymbol} supply at the time when proposal was created`}/>
</Box>
<Typography>{formatNumber(totalSupply.toString(), 4)} ({formatNumber(votePercentage, 1)}%)</Typography>
<Typography>{formatNumber(totalSupply.toString(), 5)} ({formatNumber(votePercentage, 2)}%)</Typography>
</Box>
<Box display="flex" justifyContent="space-between">
@ -289,7 +287,7 @@ const ProposalDetails = ({ chainId, address, connect, config }) => {
<Typography>Votes</Typography>
<InfoTooltip message={`Your voting power, as percentage of total $${ghstSymbol} at the time when proposal was created`} />
</Box>
<Typography>{formatNumber(pastVotes.toString(), 4)} ({formatNumber(voteWeightPercentage, 1)}%)</Typography>
<Typography>{formatNumber(pastVotes.toString(), 5)} ({formatNumber(voteWeightPercentage, 2)}%)</Typography>
</Box>
</Box>

View File

@ -81,7 +81,7 @@ export const parseFunctionCalldata = ({
case allPossibleFunctions[1].value:
return <SetAdjustmentParsed key={index} {...props} />;
case allPossibleFunctions[2].value:
return <CreateBondParsed {...props} />;
return <CreateBondParsed key={index} {...props} />;
case allPossibleFunctions[3].value:
return <SetProposalThresholdParsed key={index} {...props} />;
case allPossibleFunctions[4].value:
@ -288,7 +288,7 @@ export const ParsedCell = (props) => {
</TableCell>
<TableCell align="center" style={{ padding: "8px 0" }}>
<Typography>{formatCurrency(props.value, 4, props.nativeCoin)}</Typography>
<Typography>{formatCurrency(props.value, 5, props.nativeCoin)}</Typography>
</TableCell>
<TableCell>
@ -330,7 +330,7 @@ export const ParsedCell = (props) => {
<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>
<Typography>{formatCurrency(props.value, 5, props.nativeCoin)}</Typography>
</Link>
</Box>

View File

@ -14,7 +14,7 @@ import Paper from "../../components/Paper/Paper";
import TokenStack from "../../components/TokenStack/TokenStack";
import { useTokenSymbol } from "../../hooks/tokens";
const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
const Stake = ({ chainId, chainSymbol, address, isOpened, closeModal, connect }) => {
const [action, setAction] = useState("STAKE");
const [settingsModalOpen, setSettingsModalOpen] = useState(false);
@ -31,13 +31,13 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
useEffect(() => {
switch (true) {
case (upperToken === ftsoSymbol && bottomToken === ghstSymbol):
setAction("STAKE")
setAction("Stake")
break;
case (upperToken === ghstSymbol && bottomToken === ftsoSymbol):
setAction("UNSTAKE")
setAction("Unstake")
break;
default:
setAction("STAKE")
setAction("Stake")
}
}, [ftsoSymbol, ghstSymbol, upperToken, bottomToken])
@ -46,10 +46,10 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
headerContent={
<Box display="flex" justifyContent="center" alignItems="center" gap="15px">
<Typography variant="h4">{action}</Typography>
<TokenStack tokens={[upperToken, bottomToken]} />
<TokenStack tokens={[upperToken, bottomToken, chainSymbol]} />
</Box>
}
topLeft={
topRight={
<GhostStyledIcon
viewBox="0 0 23 23"
component={SettingsIcon}
@ -64,6 +64,7 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
>
<Box display="flex" alignItems="center" justifyContent="center" flexDirection="column">
<StakeArea
chainSymbol={chainSymbol}
settingsModalOpen={settingsModalOpen}
closeSettingModal={() => setSettingsModalOpenInner(false)}
upperToken={upperToken}

View File

@ -1,4 +1,4 @@
import { Dispatch, SetStateAction, useState, useEffect } from "react";
import { Dispatch, SetStateAction, useState, useEffect, useMemo } from "react";
import { Box, Container, Grid, Divider, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useLocation } from "react-router-dom";
import ReactGA from "react-ga4";
@ -19,7 +19,7 @@ import { Apy, CurrentIndex, TotalDeposit } from "./components/Metric";
import { useEpoch } from "../../hooks/staking";
import { useTokenSymbol } from "../../hooks/tokens";
export const StakeContainer = ({ chainId, address, connect }) => {
export const StakeContainer = ({ chainId, address, config, connect }) => {
const location = useLocation();
const [isModalOpened, handleModal] = useState(false);
@ -32,6 +32,12 @@ export const StakeContainer = ({ chainId, address, connect }) => {
const { epoch, refetch: refetchEpoch } = useEpoch(chainId);
const chainSymbol = useMemo(() => {
const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
if (chainSymbol) return chainSymbol;
return "WTF";
}, [config, chainId])
useEffect(() => {
ReactGA.send({ hitType: "pageview", page: location.pathname });
}, [location])
@ -39,6 +45,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
if (isModalOpened) {
return (
<Stake
chainSymbol={chainSymbol}
chainId={chainId}
address={address}
isOpened={isModalOpened}
@ -88,7 +95,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
</Grid>
<Divider sx={{ marginTop: "30px" }} />
<ClaimsArea chainId={chainId} address={address} epoch={epoch} />
<ClaimsArea chainId={chainId} address={address} chainSymbol={chainSymbol} epoch={epoch} />
<Divider />
<Box mt="15px" display="flex" flexDirection="column" alignItems="center" justifyContent="center">
@ -104,7 +111,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
</Box>
</Box>
</Paper>
<FarmPools chainId={chainId} />
<FarmPools chainId={chainId} chainSymbol={chainSymbol} />
</Box>
</Container>
</Box>

View File

@ -42,7 +42,7 @@ const ClaimConfirmationModal = (props) => {
maxWidth="356px"
topLeft={<></>}
headerContent={
<Box display="flex" flexDirection="row">
<Box display="flex" flexDirection="column">
<Typography variant="h5">{`Confirm ${capitalize(props.action)}`}</Typography>
</Box>
}

View File

@ -50,7 +50,7 @@ const StyledTableHeader = styled(TableHead)(({ theme }) => ({
},
}));
export const ClaimsArea = ({ chainId, address, epoch }) => {
export const ClaimsArea = ({ chainId, address, chainSymbol, epoch }) => {
const isSmallScreen = useMediaQuery("(max-width: 745px)");
const { breakoutFromStaking } = useBreakoutModal();
@ -70,7 +70,17 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
const claimableBalance = useMemo(() => {
if (isNetworkLegacy(chainId)) {
return isPayoutGhst ? balanceForShares.div(currentIndex) : balanceForShares;
return isPayoutGhst
? new DecimalBigNumber(
balanceForShares && currentIndex && currentIndex._value !== 0n
? (balanceForShares._value * (10n ** BigInt(9 + currentIndex._decimals - balanceForShares._decimals))) / currentIndex._value
: 0n,
9
)
: new DecimalBigNumber(
balanceForShares ? balanceForShares._value : 0n,
balanceForShares ? balanceForShares._decimals : 9
);
}
const toClaim = new DecimalBigNumber(claim.shares, 18);
return isPayoutGhst ? toClaim : toClaim.mul(currentIndex);
@ -108,7 +118,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
if (claim.shares === 0n) return <></>;
const warmupTooltip = `Your claim earns rebases during warm-up. You can emergency withdraw, but this forfeits the rebases`;
const warmupTooltip = `Claim earns rebases during warm-up. You can emergency withdraw, but this forfeits the rebases`;
return (
<>
@ -134,7 +144,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
justifyContent="space-between"
flexDirection={isSmallScreen ? "column" : "row"}
>
<Typography variant="h6">Your active {isPayoutGhst ? ghstSymbol : stnkSymbol} claim</Typography>
<Typography variant="h6">My active {isPayoutGhst ? ghstSymbol : stnkSymbol} claim</Typography>
</Box>
}
tooltip={warmupTooltip}
@ -164,6 +174,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
</TableRow>
</StyledTableHeader>
<ClaimInfo
chainSymbol={chainSymbol}
setConfirmationModalOpen={setConfirmationModalOpen}
prepareBalance={claimableBalance}
isPayoutGhst={isPayoutGhst}
@ -184,6 +195,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
};
const ClaimInfo = ({
chainSymbol,
setConfirmationModalOpen,
prepareBalance,
claim,
@ -200,7 +212,10 @@ const ClaimInfo = ({
<TableRow>
<TableCell style={{ padding: "8px 8px 8px 0" }}>
<Box display="flex" flexDirection="row" alignItems="center" style={{ whiteSpace: "nowrap" }}>
<Token key={isPayoutGhst ? ghstSymbol : stnkSymbol} name={isPayoutGhst ? ghstSymbol : stnkSymbol} />
<Token
chainTokenName={chainSymbol}
name={isPayoutGhst ? ghstSymbol : stnkSymbol}
/>
<Box marginLeft="14px" marginRight="10px">
<Typography>{isPayoutGhst ? ghstSymbol : stnkSymbol}</Typography>
</Box>

View File

@ -23,7 +23,7 @@ import { useTotalSupply, useTokenSymbol } from "../../../hooks/tokens";
import { EMPTY_ADDRESS, FTSO_ADDRESSES } from "../../../constants/addresses";
const FarmPools = ({ chainId }) => {
const FarmPools = ({ chainId, chainSymbol }) => {
const isSmallScreen = useMediaQuery("(max-width: 775px)");
const { network } = useParams();
@ -35,8 +35,9 @@ const FarmPools = ({ chainId }) => {
const pools = [
{
icons: ["FTSO", tokenNameConverter(chainId, reserveSymbol)],
name: `${ftsoSymbol}-${reserveSymbol}`,
icons: [tokenNameConverter(chainId, reserveSymbol), "FTSO"],
chainTokenNames: [undefined, chainSymbol],
name: `${reserveSymbol}-${ftsoSymbol}`,
dex: "Uniswap V2",
url: `/${network}/dex/uniswap`,
tvl: reserveFtsoUniValuation,
@ -101,7 +102,7 @@ const FarmPoolCard = ({ id, pool }) => {
<Box id={id + `--bond`}>
<Box display="flex" alignItems="center" flexDirection="column" gap="20px">
<Box display="flex" alignItems="center" gap="20px">
<TokenStack tokens={pool.icons} />
<TokenStack chainTokenNames={pool.chainTokenNames} tokens={pool.icons} />
<Typography>{pool.name}</Typography>
</Box>
@ -127,7 +128,7 @@ const FarmPoolRow = ({ id, pool }) => {
<TableRow key={id} id={id + `--pool`} data-testid={id + `--pool`}>
<TableCell style={{ padding: "8px 0" }}>
<Box display="flex" alignItems="center" gap="20px">
<TokenStack tokens={pool.icons} />
<TokenStack chainTokenNames={pool.chainTokenNames} tokens={pool.icons} />
<Typography>{pool.name}</Typography>
</Box>
</TableCell>

View File

@ -5,6 +5,7 @@ import { useAccount } from "wagmi";
import { StakeInputArea } from "./StakeInputArea";
export const StakeArea = ({
chainSymbol,
upperToken,
setUpperToken,
bottomToken,
@ -19,6 +20,7 @@ export const StakeArea = ({
return (
<Box width="100%">
<StakeInputArea
chainSymbol={chainSymbol}
upperToken={upperToken}
setUpperToken={setUpperToken}
bottomToken={bottomToken}

View File

@ -7,8 +7,9 @@ import { DecimalBigNumber } from "../../../helpers/DecimalBigNumber";
import GhostStyledIcon from "../../../components/Icon/GhostIcon";
import Modal from "../../../components/Modal/Modal";
import { PrimaryButton, SecondaryButton } from "../../../components/Button";
import Metric from "../../../components/Metric/Metric";
import Token from "../../../components/Token/Token";
import { PrimaryButton, SecondaryButton } from "../../../components/Button";
import { TokenAllowanceGuard } from "../../../components/TokenAllowanceGuard/TokenAllowanceGuard";
import { useWarmupLength, stake, unstake, wrap, unwrap } from "../../../hooks/staking";
@ -73,7 +74,7 @@ const StakeConfirmationModal = (props) => {
const actionAmount = new DecimalBigNumber(props.amount, props.spendDecimals);
let isRebase = false;
switch (props.action) {
switch (props.action.toUpperCase()) {
case "STAKE":
isRebase = props.bottomToken === props.stnkSymbol;
await stake(
@ -128,11 +129,8 @@ const StakeConfirmationModal = (props) => {
<Modal
data-testid="stake-confirmation-modal"
maxWidth="476px"
topLeft={<></>}
headerContent={
<Box display="flex" flexDirection="row">
<Typography variant="h5">{`Confirm ${props.action}`}</Typography>
</Box>
<Typography variant="h4">{`Confirm ${props.action}`}</Typography>
}
open={props.open}
onClose={() => isPending ? {} : props.onClose()}
@ -140,15 +138,25 @@ const StakeConfirmationModal = (props) => {
>
<Box display="flex" flexDirection="column">
<Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
<Box display="flex" flexDirection="column">
<Box height="170px" display="flex" flexDirection="column">
<Metric
label={`Assets to Spend`}
metric={formatNumber(new DecimalBigNumber(props.amount, props.spendDecimals), 5)}
/>
<Box display="flex" flexDirection="row" justifyContent="center">
<Typography>
{props.upperToken}
</Typography>
<Box display="flex" flexDirection="column" justifyContent="center" alignItems="center">
<Token
parentSx={{
height: "80px",
"& svg:nth-of-type(2)": {
transform: "translate(50%, -30%)",
zIndex: 100,
}
}}
sx={{ transform: "scale(1.65)", }}
chainTokenName={props.chainSymbol}
name={props.upperToken}
/>
<Typography>{props.upperToken}</Typography>
</Box>
</Box>
<GhostStyledIcon component={ArrowRightIcon} />
@ -157,15 +165,25 @@ const StakeConfirmationModal = (props) => {
label="Assets to Receive"
metric={formatNumber(new DecimalBigNumber(props.receiveAmount, props.receiveDecimals), 5)}
/>
<Box display="flex" flexDirection="row" justifyContent="center">
<Typography>
{props.bottomToken}
</Typography>
<Box display="flex" flexDirection="column" justifyContent="center" alignItems="center">
<Token
parentSx={{
height: "80px",
"& svg:nth-of-type(2)": {
transform: "translate(50%, -30%)",
zIndex: 100,
}
}}
sx={{ transform: "scale(1.65)", }}
chainTokenName={props.chainSymbol}
name={props.upperToken === props.ftsoSymbol ? props.ghstSymbol : props.ftsoSymbol}
/>
<Typography>{props.bottomToken}</Typography>
</Box>
</Box>
</Box>
<Box sx={{ marginTop: "2rem" }}>
<Box sx={{ marginTop: "1.5rem" }}>
{acknowledgedWarmup || props.action !== "STAKE" ?
<TokenAllowanceGuard
spendAmount={new DecimalBigNumber(props.amount, props.spendDecimals)}

View File

@ -9,7 +9,7 @@ const StakeInfoText = () => {
fontSize="0.875em"
lineHeight="15px"
>
Staking drives value accrual, rewarding stakers with rebase yields that fluctuate based on ghostDAO monetary policy and are backed by bond proceeds.
Staking generates rebase yields governed by ghostDAO monetary policy and backed by bonds, Protocol Owned Liquidity (POL), and allocators.
&nbsp;<Link
color={theme.colors.primary[300]}
href="https://ghostchain.io/ghostdao_litepaper"

View File

@ -68,6 +68,7 @@ const StyledBox = styled(Box)(({ theme }) => ({
}));
export const StakeInputArea = ({
chainSymbol,
upperToken,
setUpperToken,
bottomToken,
@ -198,7 +199,7 @@ export const StakeInputArea = ({
id={`${tokenName.toLowerCase()}-input`}
token={tokenName}
tokenName={realTokenName}
tokenOnClick={() => handleModal(true)}
chainTokenName={chainSymbol}
inputProps={{ "data-testid": `${tokenName.toLowerCase()}-input`, min: "0" }}
value={tokenAmount}
onChange={event => setAmount(event.target.value)}
@ -240,11 +241,7 @@ export const StakeInputArea = ({
loading={false}
disabled={exceedsAmount}
>
{exceedsAmount ?
"Exceeds amount"
:
action[0] + String(action.toLowerCase()).slice(1)
}
{exceedsAmount ? "Exceeds amount" : action }
</PrimaryButton>
</Box>
</Box>
@ -261,6 +258,7 @@ export const StakeInputArea = ({
onTriggerRebaseChange={setIsTriggerInner}
/>
<StakeConfirmationModal
chainSymbol={chainSymbol}
open={confirmationModalOpen}
onClose={() => closeAndUpdate()}
chainId={chainId}

View File

@ -116,7 +116,7 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
theme={theme}
url={`/${networkName.toLowerCase()}/stake`}
name="(3, 3) Stake"
sideName={`${formatNumber(apyInner, 0)}% APY`}
sideName={`${formatNumber(apyInner, 2)}% APY`}
description={`Staking enables (3, 3) coordination by aligning long-term incentives, sustainably backed by (1, 1) Bonds, LP fees, and Farming. Stake ${ftsoSymbol} to earn rewards and unlock ${bridgeNumbers} Stake\u00B2.`}
/>
@ -125,7 +125,7 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
theme={theme}
url={`/${networkName.toLowerCase()}/bridge`}
name={`${bridgeNumbers} Stake\u00B2`}
sideName={`${formatNumber(gatekeepedApy, 0)}% APY`}
sideName={`${formatNumber(gatekeepedApy, 2)}% APY`}
description={`Staking\u00B2 strategy further deepens long-term incentives powered by sustainable crosschain bridging revenue. ${bridgeNumbers} Stake\u00B2 your ${csprSymbol} to receive organic APY with no warm-up and no dilution.`}
/>
</Box>

View File

@ -16,6 +16,7 @@ import { useTokenSymbol, useTokenSymbols } from "../tokens";
import {
getTokenAddress,
getTokenIcons,
getChainTokenNames,
getBondNameDisplayName,
getTokenPurchaseLink,
executeOnChainTransaction
@ -162,13 +163,15 @@ export const useLiveBonds = (chainId, chainName) => {
baseToken: {
name: baseTokenSymbol,
purchaseUrl: getTokenPurchaseLink(chainId, "", chainName),
tokenAddress: getTokenAddress(chainId, "FTSO"),
chainNames: getChainTokenNames(chainId, "FTSO"),
icons: ["FTSO"],
tokenAddress: getTokenAddress(chainId, "FTSO")
},
quoteToken: {
name: tokenNameConverter(chainId, quoteTokenSymbol),
purchaseUrl: getTokenPurchaseLink(chainId, quoteTokenAddress, chainName),
icons: getTokenIcons(chainId, quoteTokenAddress),
chainNames: getChainTokenNames(chainId, quoteTokenAddress),
decimals: quoteTokenDecimals,
quoteTokenAddress,
},
@ -190,7 +193,7 @@ export const useLiveBonds = (chainId, chainName) => {
inQuoteToken: maxPayoutInQuoteToken,
},
};
}).sort((a, b) => (a.id > b.id ? -1 : 1)) : [];
}).sort((a, b) => (a.id > b.id ? -1 : 1)) || [];
return { liveBonds, refetch };
}
@ -255,6 +258,7 @@ export const useNotes = (chainId, address) => {
quoteToken: {
name: tokenNameConverter(chainId, quoteTokenSymbol),
icons: getTokenIcons(chainId, quoteTokenAddress),
chainNames: getChainTokenNames(chainId, quoteTokenAddress),
},
vesting: terms?.at(index).result?.at(2) ? terms.at(index).result.at(2) : 0,
created: notesRaw?.at(index).result?.at(1) ? notesRaw.at(index).result.at(1) : 0,

View File

@ -174,7 +174,7 @@ export const getTokenIcons = (chainId, address) => {
icons = ["GHST"];
break;
case FTSO_DAI_LP_ADDRESSES[chainId]:
icons = ["FTSO", "WETH"];
icons = ["WETH", "FTSO"];
break;
default:
icons = [""]
@ -182,6 +182,21 @@ export const getTokenIcons = (chainId, address) => {
return icons;
}
export const getChainTokenNames = (chainId, address) => {
let icons = [undefined];
switch (address) {
case FTSO_DAI_LP_ADDRESSES[chainId]:
icons = [undefined, "WETH"];
break;
case RESERVE_ADDRESSES[chainId]:
icons = [undefined]
break;
default:
icons = ["WETH"]
}
return icons;
}
export const getBondNameDisplayName = (chainId, tokenAddress, baseTokenSymbol) => {
let stringValue = tokenNameConverter(chainId, "WETH")
if (tokenAddress.toUpperCase() === FTSO_DAI_LP_ADDRESSES[chainId].toUpperCase()) {

View File

@ -173,3 +173,8 @@ input:-webkit-autofill {
color: #ffffff !important;
-webkit-text-fill-color: #ffffff !important;
}
@keyframes spinTokenIcon {
from { transform: rotate(360deg); }
to { transform: rotate(0deg); }
}