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", "name": "ghost-dao-interface",
"private": true, "private": true,
"version": "0.7.50", "version": "0.7.51",
"type": "module", "type": "module",
"scripts": { "scripts": {
"dev": "vite", "dev": "vite",

View File

@ -198,7 +198,7 @@ function App() {
<Route path="dashboard" element={<TreasuryDashboard chainId={addressChainId ? addressChainId : chainId} />} /> <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" 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="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="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="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} />} /> <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 // getModalStyle is not a pure function, we roll the style only on the first render
const [modalStyle] = useState(getModalStyle); const [modalStyle] = useState(getModalStyle);
const closeButton = ( const closeButton = (
<IconButton <GhostStyledIcon
aria-label="close" aria-label="close"
color="inherit" component={CloseIcon}
size="small" sx={{ cursor: "pointer" }}
onClick={e => { onClick={e => {
if (props.onClose) { if (props.onClose) {
props.onClose(e, "escapeKeyDown"); 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. //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; const topLeftPos = closePosition === "left" ? closeButton : topLeft;
return ( return (

View File

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

View File

@ -44,6 +44,7 @@ const SwapCard = ({
tokenName, tokenName,
inputWidth, inputWidth,
inputFontSize, inputFontSize,
chainTokenName,
maxWidth = "416px", maxWidth = "416px",
...props ...props
}) => { }) => {
@ -72,12 +73,26 @@ const SwapCard = ({
cursor: tokenOnClick ? "pointer" : "default", cursor: tokenOnClick ? "pointer" : "default",
}} }}
borderRadius="6px" borderRadius="6px"
paddingX="9px" paddingX="7px"
paddingY="6.5px" paddingY="2px"
alignItems="center" alignItems="center"
{...onClickProps} {...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) && ( {(typeof token === "string" || tokenName) && (
<Typography fontSize="15px" lineHeight="24px" marginLeft="9px"> <Typography fontSize="15px" lineHeight="24px" marginLeft="9px">

View File

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

View File

@ -1,11 +1,12 @@
import { Avatar, Box } from "@mui/material"; import { Avatar, Box } from "@mui/material";
import Token from "../Token/Token"; import Token from "../Token/Token";
const TokenStack = ({ tokens, style, images, network, ...props }) => { const TokenStack = ({ chainTokenNames, tokens, style, images, network, ...props }) => {
const imageStyles = { const imageStyles = {
height: "27px", height: "27px",
width: "27px", width: "27px",
...style, ...style,
}; };
return ( return (
<Box display="flex" flexDirection="row" marginTop={network ? "3px" : "0px"} marginLeft={network ? "3px" : "0px"}> <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 <Avatar
src={image} src={image}
style={{ 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) => ( {tokens?.map((token, i) => (
<Token <Token
{...props}
key={i} key={i}
name={token} name={token}
style={{ 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> </Box>

View File

@ -132,7 +132,6 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
useEffect(() => { useEffect(() => {
if (isWalletOpen) { if (isWalletOpen) {
console.log(`/${chainName.toLowerCase()}/sidebar`)
ReactGA.send({ hitType: "pageview", page: `/${chainName.toLowerCase()}/sidebar` }); ReactGA.send({ hitType: "pageview", page: `/${chainName.toLowerCase()}/sidebar` });
} }
}, [isWalletOpen, chainName]) }, [isWalletOpen, chainName])
@ -143,7 +142,6 @@ function InitialWalletView({ isWalletOpen, address, chainId, onClose }) {
} }
}, [isWalletOpen, tokens]) }, [isWalletOpen, tokens])
return ( return (
<Paper> <Paper>
<Box sx={{ padding: theme.spacing(0, 3), display: "flex", flexDirection: "column", minHeight: "100vh" }}> <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", height: "28px",
}} }}
tokens={props.icons} tokens={props.icons}
chainTokenNames={props.chainTokenNames}
/> />
<Typography>{symbol}</Typography> <Typography>{symbol}</Typography>
</Box> </Box>
@ -207,7 +208,7 @@ export const useWallet = (chainId, userAddress) => {
const nativeSymbol = useMemo(() => { const nativeSymbol = useMemo(() => {
return config?.getClient()?.chain?.nativeCurrency?.symbol; return config?.getClient()?.chain?.nativeCurrency?.symbol;
}, [config]); }, [chainId, config]);
const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE"); const { symbol: reserveSymbol } = useTokenSymbol(chainId, "RESERVE");
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
@ -223,7 +224,7 @@ export const useWallet = (chainId, userAddress) => {
if (token0?.at(0) === reserveSymbol) { if (token0?.at(0) === reserveSymbol) {
tokenAddresses = [lpReserveFtsoTokens?.token0, lpReserveFtsoTokens?.token1]; tokenAddresses = [lpReserveFtsoTokens?.token0, lpReserveFtsoTokens?.token1];
let tokenNames = [...token0, ...token1]; tokenNames = [...token0, ...token1];
} }
return { tokenAddresses, tokenNames } return { tokenAddresses, tokenNames }
@ -251,6 +252,7 @@ export const useWallet = (chainId, userAddress) => {
address: ftsoAddress, address: ftsoAddress,
balance: ftsoBalance, balance: ftsoBalance,
price: ftsoPrice, price: ftsoPrice,
chainTokenNames: [nativeSymbol],
icons: ["FTSO"], icons: ["FTSO"],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/eGHST.svg", externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/eGHST.svg",
refetch: ftsoRefetch, refetch: ftsoRefetch,
@ -261,6 +263,7 @@ export const useWallet = (chainId, userAddress) => {
balance: ghstBalance, balance: ghstBalance,
price: ghstPrice, price: ghstPrice,
icons: ["GHST"], icons: ["GHST"],
chainTokenNames: [nativeSymbol],
externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg", externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/GHST.svg",
refetch: ghstRefetch, refetch: ghstRefetch,
}, },
@ -271,6 +274,10 @@ export const useWallet = (chainId, userAddress) => {
balance: lpReserveFtsoBalance, balance: lpReserveFtsoBalance,
price: lpReserveFtsoPrice, price: lpReserveFtsoPrice,
icons: lpReserveFtsoTokenNames?.tokenNames, 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", externalUrl: "https://ghostchain.io/wp-content/uploads/2025/03/uni-v2.svg",
refetch: lpReserveFtsoRefetch, refetch: lpReserveFtsoRefetch,
} }

View File

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

View File

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

View File

@ -42,6 +42,7 @@ const BondConfirmModal = ({
isNative, isNative,
bondQuoteTokenName, bondQuoteTokenName,
bondQuoteTokenIcons, bondQuoteTokenIcons,
bondQuoteTokenChainNames,
handleConfirmClose handleConfirmClose
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
@ -127,16 +128,16 @@ const BondConfirmModal = ({
maxWidth="476px" maxWidth="476px"
minHeight="200px" minHeight="200px"
open={isOpen} open={isOpen}
headerContent={ headerContent={<Typography variant="h4">{bondQuoteTokenName} Bond</Typography>}
<Box display="flex" flexDirection="row">
<TokenStack tokens={bondQuoteTokenIcons} sx={{ fontSize: "27px" }} />
<Typography variant="h4" sx={{ marginLeft: "6px" }}>
{bondQuoteTokenName}
</Typography>
</Box>
}
onClose={!isPending && handleConfirmCloseMaster} 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"> <Box display="flex" flexDirection="row" justifyContent="space-between" alignItems="center">
@ -145,19 +146,58 @@ const BondConfirmModal = ({
label="Assets to Bond" label="Assets to Bond"
metric={spendAmount} 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> <Typography>{bondQuoteTokenName}</Typography>
</Box> </Box>
</Box> </Box>
<GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} /> <GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} />
<Box display="flex" flexDirection="column"> <Box display="flex" flexDirection="column">
<Metric label="Assets to Receive" metric={receiveAmount} /> <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> <Typography>{bond.baseToken.name}</Typography>
</Box> </Box>
</Box> </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="ROI" balance={<BondDiscount discount={bond.discount} textOnly />} />
<DataRow title="Bond Slippage" balance={<BondSlippage slippage={slippage} textOnly />} /> <DataRow title="Bond Slippage" balance={<BondSlippage slippage={slippage} textOnly />} />
<DataRow title="Vesting Term" balance={<BondVesting vesting={bond.vesting} />} /> <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 display="flex" flexDirection="column" width="100%" maxWidth="476px">
<Box mb="21px"> <Box mb="21px">
<SwapCollection <SwapCollection
iconNotNeeded
UpperSwapCard={ UpperSwapCard={
<SwapCard <SwapCard
maxWidth="476px" maxWidth="476px"
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"} inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
id="from" 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} tokenName={preparedQuoteToken.name}
info={formatCurrency(balance, formatDecimals, preparedQuoteToken.name)} info={formatCurrency(balance, formatDecimals, preparedQuoteToken.name)}
endString="Max" endString="Max"
@ -119,7 +126,13 @@ const BondInputArea = ({
maxWidth="476px" maxWidth="476px"
inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"} inputWidth={isVerySmallScreen ? "100px" : isSemiSmallScreen ? "180px" : "250px"}
id="to" 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} tokenName={bond.baseToken.name}
value={amountInBaseToken.toString({ decimals: 9 })} value={amountInBaseToken.toString({ decimals: 9 })}
inputProps={{ "data-testid": "toInput" }} inputProps={{ "data-testid": "toInput" }}
@ -229,6 +242,7 @@ const BondInputArea = ({
receiveAmount={formatNumber(amountInBaseToken, formatDecimals)} receiveAmount={formatNumber(amountInBaseToken, formatDecimals)}
bondQuoteTokenName={preparedQuoteToken.name} bondQuoteTokenName={preparedQuoteToken.name}
bondQuoteTokenIcons={preparedQuoteToken.icons} bondQuoteTokenIcons={preparedQuoteToken.icons}
bondQuoteTokenChainNames={preparedQuoteToken.chainNames}
isNative={preparedQuoteToken.address === undefined} isNative={preparedQuoteToken.address === undefined}
handleSettingsOpen={handleSettingsOpen} handleSettingsOpen={handleSettingsOpen}
isOpen={confirmOpen} isOpen={confirmOpen}

View File

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

View File

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

View File

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

View File

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

View File

@ -33,11 +33,23 @@ const RouteHop = ({ theme, token, chainTokenName, arrowNeeded }) => {
sx={{ backgroundColor: theme.colors.gray[600] }} sx={{ backgroundColor: theme.colors.gray[600] }}
borderRadius="6px" borderRadius="6px"
paddingX="9px" paddingX="9px"
paddingY="6.5px" paddingY="2px"
alignItems="center" alignItems="center"
> >
<Token chainTokenName={chainTokenName} name={token} sx={{ fontSize: "21px" }} /> <Token
<Typography fontSize="15px" lineHeight="24px" marginLeft="9px"> 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} {token}
</Typography> </Typography>
</Box> </Box>

View File

@ -32,6 +32,7 @@ import {
EMPTY_ADDRESS, EMPTY_ADDRESS,
WETH_ADDRESSES, WETH_ADDRESSES,
} from "../../constants/addresses"; } from "../../constants/addresses";
import { AVAILABLE_DEXES } from "../../constants/dexes";
import { useLocalStorage } from "../../hooks/localstorage"; import { useLocalStorage } from "../../hooks/localstorage";
import { useTokenSymbol } from "../../hooks/tokens"; import { useTokenSymbol } from "../../hooks/tokens";
import { getTokenAddress } from "../../hooks/helpers"; import { getTokenAddress } from "../../hooks/helpers";
@ -184,6 +185,13 @@ const Dex = ({ chainId, address, connect, config }) => {
setSearchParams(newQueryParameters); 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 setSlippageInner = useCallback((value) => {
const maybeValue = parseFloat(value); const maybeValue = parseFloat(value);
if (!maybeValue || parseFloat(value) <= 100) { if (!maybeValue || parseFloat(value) <= 100) {
@ -242,7 +250,7 @@ const Dex = ({ chainId, address, connect, config }) => {
<PageTitle <PageTitle
name="DEX Swap" 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 <Container
style={{ style={{
@ -397,6 +405,7 @@ const Dex = ({ chainId, address, connect, config }) => {
<Box height="350px"> <Box height="350px">
{isSwap ? {isSwap ?
<SwapContainer <SwapContainer
chainSymbol={chainSymbol}
tokenNameTop={tokenNameTop} tokenNameTop={tokenNameTop}
tokenNameBottom={tokenNameBottom} tokenNameBottom={tokenNameBottom}
onCardsSwap={onCardsSwap} onCardsSwap={onCardsSwap}
@ -416,6 +425,7 @@ const Dex = ({ chainId, address, connect, config }) => {
/> />
: :
<PoolContainer <PoolContainer
chainSymbol={chainSymbol}
tokenNameTop={tokenNameTop} tokenNameTop={tokenNameTop}
tokenNameBottom={tokenNameBottom} tokenNameBottom={tokenNameBottom}
onCardsSwap={onCardsSwap} onCardsSwap={onCardsSwap}

View File

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

View File

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

View File

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

View File

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

View File

@ -81,7 +81,7 @@ export const parseFunctionCalldata = ({
case allPossibleFunctions[1].value: case allPossibleFunctions[1].value:
return <SetAdjustmentParsed key={index} {...props} />; return <SetAdjustmentParsed key={index} {...props} />;
case allPossibleFunctions[2].value: case allPossibleFunctions[2].value:
return <CreateBondParsed {...props} />; return <CreateBondParsed key={index} {...props} />;
case allPossibleFunctions[3].value: case allPossibleFunctions[3].value:
return <SetProposalThresholdParsed key={index} {...props} />; return <SetProposalThresholdParsed key={index} {...props} />;
case allPossibleFunctions[4].value: case allPossibleFunctions[4].value:
@ -288,7 +288,7 @@ export const ParsedCell = (props) => {
</TableCell> </TableCell>
<TableCell align="center" style={{ padding: "8px 0" }}> <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>
<TableCell> <TableCell>
@ -330,7 +330,7 @@ export const ParsedCell = (props) => {
<Box display="flex" justifyContent="space-between"> <Box display="flex" justifyContent="space-between">
<Typography>Value</Typography> <Typography>Value</Typography>
<Link onClick={handleCalldataCopy} target="_blank" rel="noopener noreferrer"> <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> </Link>
</Box> </Box>

View File

@ -14,7 +14,7 @@ import Paper from "../../components/Paper/Paper";
import TokenStack from "../../components/TokenStack/TokenStack"; import TokenStack from "../../components/TokenStack/TokenStack";
import { useTokenSymbol } from "../../hooks/tokens"; 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 [action, setAction] = useState("STAKE");
const [settingsModalOpen, setSettingsModalOpen] = useState(false); const [settingsModalOpen, setSettingsModalOpen] = useState(false);
@ -31,13 +31,13 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
useEffect(() => { useEffect(() => {
switch (true) { switch (true) {
case (upperToken === ftsoSymbol && bottomToken === ghstSymbol): case (upperToken === ftsoSymbol && bottomToken === ghstSymbol):
setAction("STAKE") setAction("Stake")
break; break;
case (upperToken === ghstSymbol && bottomToken === ftsoSymbol): case (upperToken === ghstSymbol && bottomToken === ftsoSymbol):
setAction("UNSTAKE") setAction("Unstake")
break; break;
default: default:
setAction("STAKE") setAction("Stake")
} }
}, [ftsoSymbol, ghstSymbol, upperToken, bottomToken]) }, [ftsoSymbol, ghstSymbol, upperToken, bottomToken])
@ -46,10 +46,10 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
headerContent={ headerContent={
<Box display="flex" justifyContent="center" alignItems="center" gap="15px"> <Box display="flex" justifyContent="center" alignItems="center" gap="15px">
<Typography variant="h4">{action}</Typography> <Typography variant="h4">{action}</Typography>
<TokenStack tokens={[upperToken, bottomToken]} /> <TokenStack tokens={[upperToken, bottomToken, chainSymbol]} />
</Box> </Box>
} }
topLeft={ topRight={
<GhostStyledIcon <GhostStyledIcon
viewBox="0 0 23 23" viewBox="0 0 23 23"
component={SettingsIcon} component={SettingsIcon}
@ -64,6 +64,7 @@ const Stake = ({ chainId, address, isOpened, closeModal, connect }) => {
> >
<Box display="flex" alignItems="center" justifyContent="center" flexDirection="column"> <Box display="flex" alignItems="center" justifyContent="center" flexDirection="column">
<StakeArea <StakeArea
chainSymbol={chainSymbol}
settingsModalOpen={settingsModalOpen} settingsModalOpen={settingsModalOpen}
closeSettingModal={() => setSettingsModalOpenInner(false)} closeSettingModal={() => setSettingsModalOpenInner(false)}
upperToken={upperToken} 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 { Box, Container, Grid, Divider, Typography, useMediaQuery, useTheme } from "@mui/material";
import { useLocation } from "react-router-dom"; import { useLocation } from "react-router-dom";
import ReactGA from "react-ga4"; import ReactGA from "react-ga4";
@ -19,7 +19,7 @@ import { Apy, CurrentIndex, TotalDeposit } from "./components/Metric";
import { useEpoch } from "../../hooks/staking"; import { useEpoch } from "../../hooks/staking";
import { useTokenSymbol } from "../../hooks/tokens"; import { useTokenSymbol } from "../../hooks/tokens";
export const StakeContainer = ({ chainId, address, connect }) => { export const StakeContainer = ({ chainId, address, config, connect }) => {
const location = useLocation(); const location = useLocation();
const [isModalOpened, handleModal] = useState(false); const [isModalOpened, handleModal] = useState(false);
@ -32,6 +32,12 @@ export const StakeContainer = ({ chainId, address, connect }) => {
const { epoch, refetch: refetchEpoch } = useEpoch(chainId); 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(() => { useEffect(() => {
ReactGA.send({ hitType: "pageview", page: location.pathname }); ReactGA.send({ hitType: "pageview", page: location.pathname });
}, [location]) }, [location])
@ -39,6 +45,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
if (isModalOpened) { if (isModalOpened) {
return ( return (
<Stake <Stake
chainSymbol={chainSymbol}
chainId={chainId} chainId={chainId}
address={address} address={address}
isOpened={isModalOpened} isOpened={isModalOpened}
@ -88,7 +95,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
</Grid> </Grid>
<Divider sx={{ marginTop: "30px" }} /> <Divider sx={{ marginTop: "30px" }} />
<ClaimsArea chainId={chainId} address={address} epoch={epoch} /> <ClaimsArea chainId={chainId} address={address} chainSymbol={chainSymbol} epoch={epoch} />
<Divider /> <Divider />
<Box mt="15px" display="flex" flexDirection="column" alignItems="center" justifyContent="center"> <Box mt="15px" display="flex" flexDirection="column" alignItems="center" justifyContent="center">
@ -104,7 +111,7 @@ export const StakeContainer = ({ chainId, address, connect }) => {
</Box> </Box>
</Box> </Box>
</Paper> </Paper>
<FarmPools chainId={chainId} /> <FarmPools chainId={chainId} chainSymbol={chainSymbol} />
</Box> </Box>
</Container> </Container>
</Box> </Box>

View File

@ -42,7 +42,7 @@ const ClaimConfirmationModal = (props) => {
maxWidth="356px" maxWidth="356px"
topLeft={<></>} topLeft={<></>}
headerContent={ headerContent={
<Box display="flex" flexDirection="row"> <Box display="flex" flexDirection="column">
<Typography variant="h5">{`Confirm ${capitalize(props.action)}`}</Typography> <Typography variant="h5">{`Confirm ${capitalize(props.action)}`}</Typography>
</Box> </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 isSmallScreen = useMediaQuery("(max-width: 745px)");
const { breakoutFromStaking } = useBreakoutModal(); const { breakoutFromStaking } = useBreakoutModal();
@ -70,7 +70,17 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
const claimableBalance = useMemo(() => { const claimableBalance = useMemo(() => {
if (isNetworkLegacy(chainId)) { 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); const toClaim = new DecimalBigNumber(claim.shares, 18);
return isPayoutGhst ? toClaim : toClaim.mul(currentIndex); return isPayoutGhst ? toClaim : toClaim.mul(currentIndex);
@ -108,7 +118,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
if (claim.shares === 0n) return <></>; 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 ( return (
<> <>
@ -134,7 +144,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
justifyContent="space-between" justifyContent="space-between"
flexDirection={isSmallScreen ? "column" : "row"} 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> </Box>
} }
tooltip={warmupTooltip} tooltip={warmupTooltip}
@ -164,6 +174,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
</TableRow> </TableRow>
</StyledTableHeader> </StyledTableHeader>
<ClaimInfo <ClaimInfo
chainSymbol={chainSymbol}
setConfirmationModalOpen={setConfirmationModalOpen} setConfirmationModalOpen={setConfirmationModalOpen}
prepareBalance={claimableBalance} prepareBalance={claimableBalance}
isPayoutGhst={isPayoutGhst} isPayoutGhst={isPayoutGhst}
@ -184,6 +195,7 @@ export const ClaimsArea = ({ chainId, address, epoch }) => {
}; };
const ClaimInfo = ({ const ClaimInfo = ({
chainSymbol,
setConfirmationModalOpen, setConfirmationModalOpen,
prepareBalance, prepareBalance,
claim, claim,
@ -200,7 +212,10 @@ const ClaimInfo = ({
<TableRow> <TableRow>
<TableCell style={{ padding: "8px 8px 8px 0" }}> <TableCell style={{ padding: "8px 8px 8px 0" }}>
<Box display="flex" flexDirection="row" alignItems="center" style={{ whiteSpace: "nowrap" }}> <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"> <Box marginLeft="14px" marginRight="10px">
<Typography>{isPayoutGhst ? ghstSymbol : stnkSymbol}</Typography> <Typography>{isPayoutGhst ? ghstSymbol : stnkSymbol}</Typography>
</Box> </Box>

View File

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

View File

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

View File

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

View File

@ -9,7 +9,7 @@ const StakeInfoText = () => {
fontSize="0.875em" fontSize="0.875em"
lineHeight="15px" 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 &nbsp;<Link
color={theme.colors.primary[300]} color={theme.colors.primary[300]}
href="https://ghostchain.io/ghostdao_litepaper" href="https://ghostchain.io/ghostdao_litepaper"

View File

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

View File

@ -116,7 +116,7 @@ const ProtocolDetails = ({ chainId, isMobileScreen, theme, }) => {
theme={theme} theme={theme}
url={`/${networkName.toLowerCase()}/stake`} url={`/${networkName.toLowerCase()}/stake`}
name="(3, 3) 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.`} 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} theme={theme}
url={`/${networkName.toLowerCase()}/bridge`} url={`/${networkName.toLowerCase()}/bridge`}
name={`${bridgeNumbers} Stake\u00B2`} 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.`} 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> </Box>

View File

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

View File

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