add flexibility during bond purchase

Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
This commit is contained in:
Uncle Fatso 2026-04-07 21:54:48 +03:00
parent a4b801efee
commit 421f2cef27
Signed by: f4ts0
GPG Key ID: 565F4F2860226EBB
7 changed files with 186 additions and 155 deletions

View File

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

View File

@ -215,7 +215,7 @@ function App() {
<Route path="/:network" element={<AvailableNetworkGuard allowedNetworks={chains.map(chain => chain.name.toLowerCase())} /> }> <Route path="/:network" element={<AvailableNetworkGuard allowedNetworks={chains.map(chain => chain.name.toLowerCase())} /> }>
<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 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 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} />} />

View File

@ -18,24 +18,24 @@ import BondSettingsModal from "./components/BondSettingsModal";
import NotFound from "../NotFound/NotFound"; import NotFound from "../NotFound/NotFound";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber"; import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { NetworkId } from "../../constants"; import { isNetworkLegacy } from "../../constants";
import { formatCurrency } from "../../helpers"; import { formatCurrency } from "../../helpers";
import { useLocalStorage } from "../../hooks/localstorage"; import { useLocalStorage } from "../../hooks/localstorage";
import { useLiveBonds } from "../../hooks/bonds"; import { useLiveBonds } from "../../hooks/bonds";
import { useFtsoPrice } from "../../hooks/prices"; import { useFtsoPrice } from "../../hooks/prices";
const BondModalContainer = ({ chainId, address, connect }) => { const BondModalContainer = ({ chainId, address, config, connect }) => {
const { id, network } = useParams(); const { id, network } = useParams();
const { liveBonds } = useLiveBonds(chainId, network); const { liveBonds } = useLiveBonds(chainId, network);
const bond = liveBonds.find(bond => bond.id === Number(id)); const bond = liveBonds.find(bond => bond.id === Number(id));
if (!bond) return <NotFound />; if (!bond) return <NotFound />;
return <BondModal chainId={chainId} bond={bond} address={address} connect={connect} />; return <BondModal config={config} chainId={chainId} bond={bond} address={address} connect={connect} />;
}; };
export const BondModal = ({ bond, chainId, address, connect }) => { export const BondModal = ({ bond, chainId, address, config, connect }) => {
const navigate = useNavigate(); const navigate = useNavigate();
const { network } = useParams(); const { network } = useParams();
const { pathname } = useLocation(); const { pathname } = useLocation();
@ -81,6 +81,23 @@ export const BondModal = ({ bond, chainId, address, connect }) => {
return () => window.removeEventListener("keydown", handleKeyDown); return () => window.removeEventListener("keydown", handleKeyDown);
}, [network, navigate, isSettingsOpen]); }, [network, navigate, isSettingsOpen]);
const chainSymbol = useMemo(() => {
const chainSymbol = config?.getClient()?.chain?.nativeCurrency?.symbol;
if (chainSymbol) return chainSymbol;
return "WTF";
}, [config]);
const preparedQuoteToken = useMemo(() => {
if (isNetworkLegacy(chainId)) {
return {
address: bond.quoteToken.quoteTokenAddress,
icons: bond.quoteToken.icons,
name: bond.quoteToken.name,
}
}
return { address: undefined, icons: [chainSymbol], name: chainSymbol };
}, [bond, chainSymbol, chainId])
return ( return (
<Box> <Box>
<PageTitle <PageTitle
@ -95,10 +112,10 @@ export const BondModal = ({ bond, chainId, address, connect }) => {
</Box> </Box>
</Link> </Link>
<TokenStack tokens={bond.quoteToken.icons} sx={{ fontSize: "27px" }} /> <TokenStack 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}>
{bond.quoteToken.name} {preparedQuoteToken.name}
</Typography> </Typography>
</Box> </Box>
</Box> </Box>
@ -144,10 +161,12 @@ export const BondModal = ({ bond, chainId, address, connect }) => {
<BondInputArea <BondInputArea
chainId={chainId} chainId={chainId}
bond={bond} bond={bond}
config={config}
connect={connect} connect={connect}
address={address} address={address}
slippage={slippage} slippage={slippage}
recipientAddress={recipientAddress} recipientAddress={recipientAddress}
preparedQuoteToken={preparedQuoteToken}
handleSettingsOpen={() => setSettingsOpen(true)} handleSettingsOpen={() => setSettingsOpen(true)}
formatDecimals={formatDecimals} formatDecimals={formatDecimals}
/> />

View File

@ -37,6 +37,9 @@ const BondConfirmModal = ({
sender, sender,
handleSettingsOpen, handleSettingsOpen,
isOpen, isOpen,
isNative,
bondQuoteTokenName,
bondQuoteTokenIcons,
handleConfirmClose handleConfirmClose
}) => { }) => {
const theme = useTheme(); const theme = useTheme();
@ -53,15 +56,16 @@ const BondConfirmModal = ({
const maxPrice = bond.price.inBaseToken._value * bigIntSlippage / one; const maxPrice = bond.price.inBaseToken._value * bigIntSlippage / one;
const referral = import.meta.env.VITE_APP_REFERRAL_ADDRESS; const referral = import.meta.env.VITE_APP_REFERRAL_ADDRESS;
await purchaseBond( await purchaseBond({
chainId, chainId,
bond.id, bondId: bond.id,
spendAmountValue._value.toBigInt(), amount: spendAmountValue._value.toBigInt(),
maxPrice, maxPrice,
recipientAddress, user: recipientAddress,
sender, sender,
referral referral,
); isNative
});
setIsPending(false); setIsPending(false);
handleConfirmClose(); handleConfirmClose();
@ -74,9 +78,9 @@ const BondConfirmModal = ({
open={isOpen} open={isOpen}
headerContent={ headerContent={
<Box display="flex" flexDirection="row"> <Box display="flex" flexDirection="row">
<TokenStack tokens={bond.quoteToken.icons} sx={{ fontSize: "27px" }} /> <TokenStack tokens={bondQuoteTokenIcons} sx={{ fontSize: "27px" }} />
<Typography variant="h4" sx={{ marginLeft: "6px" }}> <Typography variant="h4" sx={{ marginLeft: "6px" }}>
{bond.quoteToken.name} {bondQuoteTokenName}
</Typography> </Typography>
</Box> </Box>
} }
@ -91,7 +95,7 @@ const BondConfirmModal = ({
metric={spendAmount} metric={spendAmount}
/> />
<Box display="flex" flexDirection="row" justifyContent="center"> <Box display="flex" flexDirection="row" justifyContent="center">
<Typography>{bond.quoteToken.name}</Typography> <Typography>{bondQuoteTokenName}</Typography>
</Box> </Box>
</Box> </Box>
<GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} /> <GhostStyledIcon sx={{ transform: "rotate(-90deg)" }} component={ArrowDropDownIcon} />

View File

@ -30,6 +30,7 @@ const BondInputArea = ({
formatDecimals, formatDecimals,
handleSettingsOpen, handleSettingsOpen,
address, address,
preparedQuoteToken,
connect connect
}) => { }) => {
const isSemiSmallScreen = useMediaQuery("(max-width: 480px)"); const isSemiSmallScreen = useMediaQuery("(max-width: 480px)");
@ -37,7 +38,7 @@ const BondInputArea = ({
const { pathname } = useLocation(); const { pathname } = useLocation();
const { currentIndex } = useCurrentIndex(chainId); const { currentIndex } = useCurrentIndex(chainId);
const { balance, refetch } = useBalance(chainId, bond.quoteToken.quoteTokenAddress, address); const { balance, refetch } = useBalance(chainId, preparedQuoteToken.address, address);
const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO"); const { symbol: ftsoSymbol } = useTokenSymbol(chainId, "FTSO");
const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST"); const { symbol: ghstSymbol } = useTokenSymbol(chainId, "GHST");
@ -100,15 +101,14 @@ const BondInputArea = ({
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 tokens={bond.quoteToken.icons} sx={{ fontSize: "21px" }} />}
tokenName={bond.quoteToken.name} tokenName={preparedQuoteToken.name}
info={formatCurrency(balance, formatDecimals, bond.quoteToken.name)} info={formatCurrency(balance, formatDecimals, preparedQuoteToken.name)}
endString="Max" endString={preparedQuoteToken.address && "Max"}
endStringOnClick={setMax} endStringOnClick={setMax}
value={amount} value={amount}
onChange={event => setAmount(event.currentTarget.value)} onChange={event => setAmount(event.currentTarget.value)}
inputProps={{ "data-testid": "fromInput" }} inputProps={{ "data-testid": "fromInput" }}
/> />}
}
LowerSwapCard={ LowerSwapCard={
<SwapCard <SwapCard
maxWidth="476px" maxWidth="476px"
@ -134,6 +134,7 @@ const BondInputArea = ({
connect={connect} connect={connect}
width="100%" width="100%"
height="60px" height="60px"
isNative={preparedQuoteToken.address === undefined}
isVertical isVertical
message={ message={
<> <>
@ -221,6 +222,9 @@ const BondInputArea = ({
spendAmountValue={parsedAmount} spendAmountValue={parsedAmount}
spendAmount={formatNumber(parsedAmount, formatDecimals)} spendAmount={formatNumber(parsedAmount, formatDecimals)}
receiveAmount={formatNumber(amountInBaseToken, formatDecimals)} receiveAmount={formatNumber(amountInBaseToken, formatDecimals)}
bondQuoteTokenName={preparedQuoteToken.name}
bondQuoteTokenIcons={preparedQuoteToken.icons}
isNative={preparedQuoteToken.address === undefined}
handleSettingsOpen={handleSettingsOpen} handleSettingsOpen={handleSettingsOpen}
isOpen={confirmOpen} isOpen={confirmOpen}
sender={address} sender={address}

View File

@ -56,12 +56,12 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
setIsWapmup(true); setIsWapmup(true);
} else { } else {
setIsPending(true); setIsPending(true);
await redeem( await redeem({
chainId, chainId,
address, user: address,
isPayoutGhst, isGhst: isPayoutGhst,
indexes indexes
); });
await notesRefetch(); await notesRefetch();
setIsPending(false); setIsPending(false);
} }
@ -99,8 +99,9 @@ export const ClaimBonds = ({ chainId, address, secondsTo }) => {
? formatCurrency(totalClaimableBalance, 5, ghstSymbol) ? formatCurrency(totalClaimableBalance, 5, ghstSymbol)
: formatCurrency(currentIndex.mul(totalClaimableBalance), 5, stnkSymbol) : formatCurrency(currentIndex.mul(totalClaimableBalance), 5, stnkSymbol)
} }
&nbsp; </Typography>
({formatCurrency(totalClaimableBalance * ghstPrice, 2)}) <Typography variant="subtitle1" align="center">
{formatCurrency(totalClaimableBalance * ghstPrice, 2)}
</Typography> </Typography>
</Box> </Box>

View File

@ -266,7 +266,7 @@ export const useNotes = (chainId, address) => {
return { notes, refetch }; return { notes, refetch };
} }
export const purchaseBond = async (chainId, bondId, amount, maxPrice, user, sender, referral) => { export const purchaseBond = async ({ chainId, bondId, amount, maxPrice, user, sender, referral, isNative }) => {
const args = [ const args = [
bondId, bondId,
amount, amount,
@ -284,11 +284,12 @@ export const purchaseBond = async (chainId, bondId, amount, maxPrice, user, send
"deposit", "deposit",
args, args,
sender, sender,
messages messages,
isNative ? amount : undefined
); );
} }
export const redeem = async (chainId, user, isGhst, indexes) => { export const redeem = async ({ chainId, user, isGhst, indexes }) => {
const args = [ const args = [
user, user,
isGhst, isGhst,
@ -313,7 +314,8 @@ const executeOnChainTransaction = async (
functionName, functionName,
args, args,
account, account,
messages messages,
value
) => { ) => {
try { try {
const { request } = await simulateContract(config, { const { request } = await simulateContract(config, {
@ -324,6 +326,7 @@ const executeOnChainTransaction = async (
account, account,
chainId, chainId,
type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559', type: isNetworkLegacyType(chainId) ? 'legacy' : 'eip1559',
value: value,
}); });
const txHash = await writeContract(config, request); const txHash = await writeContract(config, request);