ghost-dao-interface/src/hooks/bonds/index.jsx
Uncle Fatso 5dffd62c5a
replace hardcoded token symbols with on-chain data hook
Signed-off-by: Uncle Fatso <uncle.fatso@ghostchain.io>
2025-06-30 19:49:07 +03:00

336 lines
13 KiB
JavaScript

import { useReadContract, useReadContracts } from "wagmi";
import { simulateContract, writeContract, waitForTransactionReceipt } from "@wagmi/core";
import toast from "react-hot-toast";
import { config } from "../../config";
import {
BOND_DEPOSITORY_ADDRESSES,
DAO_TREASURY_ADDRESSES,
BONDING_CALCULATOR_ADDRESSES,
} from "../../constants/addresses";
import { abi as BondAbi } from "../../abi/GhostBondDepository.json";
import { abi as TreasuryAbi } from "../../abi/GhostTreasury.json";
import { abi as BondingCalculatorAbi } from "../../abi/GhostBondingCalculator.json";
import { useFtsoPrice } from "../prices";
import { useTokenSymbol, useTokenSymbols } from "../tokens";
import { getTokenAddress, getTokenIcons, getBondNameDisplayName, getTokenPurchaseLink } from "../helpers";
import { DecimalBigNumber } from "../../helpers/DecimalBigNumber";
import { shorten } from "../../helpers";
export const useLiveBonds = (chainId) => {
const baseTokenPerUsd = useFtsoPrice(chainId);
const { data: liveIndexesRaw, refetch } = useReadContract({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "liveMarkets",
scopeKey: `liveMarkets-${chainId}`,
chainId: chainId,
});
const { data: markets } = useReadContracts({
contracts: liveIndexesRaw?.map((id) => ({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "markets",
args: [id],
scopeKey: `markets-${id.toString()}-${chainId}`,
chainId: chainId,
}))
});
const { data: terms } = useReadContracts({
contracts: liveIndexesRaw?.map((id) => ({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "terms",
args: [id],
scopeKey: `terms-${id.toString()}-${chainId}`,
chainId: chainId,
}))
});
const { data: metas } = useReadContracts({
contracts: liveIndexesRaw?.map((id) => ({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "metadatas",
args: [id],
scopeKey: `metadatas-${id.toString()}-${chainId}`,
chainId: chainId,
}))
});
const { data: marketPrices } = useReadContracts({
contracts: liveIndexesRaw?.map((id) => ({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "marketPrice",
args: [id],
scopeKey: `marketPrice-${id.toString()}-${chainId}`,
chainId: chainId,
}))
});
const { data: quotePrices } = useReadContracts({
contracts: markets?.map((_, index) => {
const quoteTokenDecimals = metas?.at(index).result?.at(5) ? metas.at(index).result.at(5) : 18;
const quoteTokenAddress = markets?.at(index).result?.at(1) ? markets.at(index).result.at(1) : "";
const one = 10n ** BigInt(quoteTokenDecimals);
return {
abi: TreasuryAbi,
address: DAO_TREASURY_ADDRESSES[chainId],
functionName: "tokenValue",
args: [quoteTokenAddress, one],
scopeKey: `tokenValue-${quoteTokenAddress}-${one.toString()}-${chainId}`,
chainId: chainId,
}
})
});
const { data: markdowns } = useReadContracts({
contracts: markets?.map((_, index) => {
const quoteTokenAddress = markets?.at(index).result?.at(1) ? markets.at(index).result.at(1) : "";
return {
abi: BondingCalculatorAbi,
address: BONDING_CALCULATOR_ADDRESSES[chainId],
functionName: "markdown",
args: [quoteTokenAddress],
scopeKey: `markdown-${quoteTokenAddress}-${chainId}`,
chainId: chainId,
}
})
});
const { symbols: quoteTokenSymbols } = useTokenSymbols(chainId, markets?.map(m => m.result?.at(1)));
const { symbol: baseTokenSymbol } = useTokenSymbol(chainId, "FTSO");
const liveBonds = liveIndexesRaw ? liveIndexesRaw.map((bondIndex, index) => {
const id = Number(bondIndex);
const marketPrice = marketPrices?.at(index).result ? marketPrices.at(index).result : 0n;
const marketCapacity = markets?.at(index).result?.at(0) ? markets.at(index).result.at(0) : 0n;
const quoteTokenAddress = markets?.at(index).result?.at(1) ? markets.at(index).result.at(1) : "";
const capacityInQuote = markets?.at(index).result?.at(2) ? markets.at(index).result.at(2) : 0n;
const marketMaxPayout = markets?.at(index).result?.at(4) ? markets.at(index).result.at(4) : 0n;
const quoteTokenDecimals = metas?.at(index).result?.at(5) ? metas.at(index).result.at(5) : 18;
const quoteTokenPerUsd = quotePrices?.at(index).result
? new DecimalBigNumber(quotePrices.at(index).result * (10n ** 9n), quoteTokenDecimals)
: new DecimalBigNumber(0n, quoteTokenDecimals);
const markdown = markdowns?.at(index).result
? new DecimalBigNumber(markdowns.at(index).result, quoteTokenDecimals)
: new DecimalBigNumber(1n, 0);
const quoteTokenSymbol = quoteTokenSymbols?.at(index).result ? quoteTokenSymbols.at(index).result : "";
const quoteTokenPerBaseToken = new DecimalBigNumber(marketPrice, 9);
const priceInUsd = quoteTokenPerUsd.mul(quoteTokenPerBaseToken).mul(markdown);
const discount = baseTokenPerUsd._value > 0n
? baseTokenPerUsd.sub(priceInUsd).div(baseTokenPerUsd)
: new DecimalBigNumber("0", baseTokenPerUsd._decimals);
const capacityInBaseToken = capacityInQuote
? new DecimalBigNumber(marketPrice > 0n ? marketCapacity / marketPrice : 0n, 9)
: new DecimalBigNumber(marketCapacity, 9);
const capacityInQuoteToken = capacityInQuote
? new DecimalBigNumber(marketCapacity, quoteTokenDecimals)
: new DecimalBigNumber(marketCapacity * marketPrice, quoteTokenDecimals);
const maxPayoutInBaseToken = new DecimalBigNumber(marketMaxPayout, 9);
const maxPayoutInQuoteToken = new DecimalBigNumber(
marketMaxPayout * marketPrice,
quoteTokenDecimals
);
const zero = new DecimalBigNumber(0n, 0);
const bondName = `${baseTokenSymbol}/${quoteTokenSymbol}`;
return {
id,
discount,
displayName: getBondNameDisplayName(chainId, bondName, quoteTokenAddress),
baseToken: {
name: quoteTokenSymbol,
purchaseUrl: getTokenPurchaseLink(chainId, ""),
icons: ["FTSO"],
tokenAddress: getTokenAddress(chainId, "FTSO")
},
quoteToken: {
name: quoteTokenName,
purchaseUrl: getTokenPurchaseLink(chainId, quoteTokenAddress),
icons: getTokenIcons(chainId, quoteTokenAddress),
decimals: quoteTokenDecimals,
quoteTokenAddress,
},
duration: terms?.at(index).result?.at(3) ? terms.at(index).result.at(3) : 0,
vesting: terms?.at(index).result?.at(2) ? terms.at(index).result.at(2) : 0,
isFixedTerm: terms?.at(index).result?.at(0) ? terms.at(index).result.at(0) : true,
isSoldOut: capacityInBaseToken.eq(zero) || capacityInBaseToken.eq(zero),
price: {
inUsd: priceInUsd,
inBaseToken: quoteTokenPerBaseToken,
marketPriceInUsd: baseTokenPerUsd
},
capacity: {
inBaseToken: capacityInBaseToken,
inQuoteToken: capacityInQuoteToken,
},
maxPayout: {
inBaseToken: maxPayoutInBaseToken,
inQuoteToken: maxPayoutInQuoteToken,
},
};
}).sort((a, b) => (a.id > b.id ? -1 : 1)) : [];
return { liveBonds, refetch };
}
export const useNotes = (chainId, address) => {
const { data: indexesFor, refetch } = useReadContract({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "indexesFor",
args: [address],
scopeKey: `indexesFor-${address}-${chainId}`,
chainId: chainId,
});
const { data: notesRaw } = useReadContracts({
contracts: indexesFor?.map((id) => ({
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "notes",
args: [address, id],
scopeKey: `notes-${id.toString()}-${address}-${chainId}`,
chainId: chainId,
}))
});
const { data: markets } = useReadContracts({
contracts: notesRaw?.map((note) => {
const marketId = note.result?.at(4) ? note.result.at(4) : "";
return {
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "markets",
args: [marketId],
scopeKey: `markets-${marketId.toString()}-${chainId}`,
chainId: chainId,
}
}),
});
const { data: terms } = useReadContracts({
contracts: notesRaw?.map((note) => {
const marketId = note.result?.at(4) ? note.result.at(4) : "";
return {
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName: "terms",
args: [marketId],
scopeKey: `terms-${marketId.toString()}-${chainId}`,
chainId: chainId,
}
}),
});
const { symbols: quoteTokenSymbols } = useTokenSymbols(chainId, markets?.map(m => m.result?.at(1)));
const notes = indexesFor ? indexesFor.map((noteIndex, index) => {
const id = Number(noteIndex);
const quoteTokenAddress = markets?.at(index).result?.at(1) ? markets.at(index).result.at(1) : "";
const quoteTokenSymbol = quoteTokenSymbols?.at(index).result ? quoteTokenSymbols.at(index).result : "";
return {
id,
quoteToken: {
name: quoteTokenSymbol,
icons: getTokenIcons(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,
matured: notesRaw?.at(index).result?.at(2) ? notesRaw.at(index).result.at(2) : 0,
payout: new DecimalBigNumber(
notesRaw?.at(index).result?.at(0) ? notesRaw.at(index).result.at(0) : 0n,
18
),
}
}) : [];
return { notes };
}
export const purchaseBond = async (chainId, bondId, amount, maxPrice, user, referral) => {
const args = [
bondId,
amount,
maxPrice,
user,
referral
];
const messages = {
replacedMsg: "Bond transaction was replaced. Wait for inclusion please.",
successMsg: `Bond successfully purchased for ${shorten(user)}! Wait until it'll mature before claim.`,
errorMsg: "Bond transaction failed. Check logs for error detalization.",
};
await executeOnChainTransaction(
chainId,
"deposit",
args,
user,
messages
);
}
export const redeem = async (chainId, user, isGhst, indexes) => {
const args = [
user,
isGhst,
indexes
];
const messages = {
replacedMsg: "Redeem transaction was replaced. Wait for inclusion please.",
successMsg: `Address ${shorten(user)} redeemed ${indexes.length} bonds in ${isGhst ? "GHST" : "STNK"}!`,
errorMsg: `Redeem of ${indexes.length} bonds failed. Check logs for error detalization.`,
};
await executeOnChainTransaction(
chainId,
"redeem",
args,
user,
messages
);
}
const executeOnChainTransaction = async (
chainId,
functionName,
args,
account,
messages
) => {
try {
const { request } = await simulateContract(config, {
abi: BondAbi,
address: BOND_DEPOSITORY_ADDRESSES[chainId],
functionName,
args,
account,
chainId
});
const txHash = await writeContract(config, request);
await waitForTransactionReceipt(config, {
hash: txHash,
onReplaced: () => toast(messages.replacedMsg),
chainId
});
toast.success(messages.successMsg);
} catch (err) {
console.error(err);
toast.error(messages.errorMsg)
}
}