From f1d533707fd1471b8b6ac1b20cb8c9e4381c7b0a Mon Sep 17 00:00:00 2001 From: "Carina.Akaia.io" Date: Wed, 19 Jun 2024 09:20:22 +0400 Subject: [PATCH] wip --- src/common/api/coingecko/hooks.ts | 15 ++++ src/common/api/coingecko/index.ts | 1 + src/common/constants.ts | 4 + src/common/lib/converters.ts | 19 +++++ src/common/lib/index.ts | 2 +- src/common/lib/yoctosToNear.ts | 10 --- src/common/ui/components/text-field.tsx | 8 +- src/modules/core/hooks/price.ts | 25 ++++++ src/modules/core/utils/index.ts | 13 ++++ .../components/DonationProjectAllocation.tsx | 78 +++++++++++++++---- 10 files changed, 147 insertions(+), 28 deletions(-) create mode 100644 src/common/api/coingecko/hooks.ts create mode 100644 src/common/api/coingecko/index.ts create mode 100644 src/common/lib/converters.ts delete mode 100644 src/common/lib/yoctosToNear.ts create mode 100644 src/modules/core/hooks/price.ts diff --git a/src/common/api/coingecko/hooks.ts b/src/common/api/coingecko/hooks.ts new file mode 100644 index 00000000..86202500 --- /dev/null +++ b/src/common/api/coingecko/hooks.ts @@ -0,0 +1,15 @@ +import useSWR from "swr"; + +import { COINGECKO_API_ENDPOINT } from "../../constants"; + +const fetcher = (url: string) => + fetch(COINGECKO_API_ENDPOINT + url).then((res) => res.json()); + +const fetcherWithTransform = (transform: Function) => (url: string) => + fetcher(url).then((data) => transform(data)); + +export const useOneNearUsdPrice = () => + useSWR( + "/simple/price?ids=near&vs_currencies=usd", + fetcherWithTransform((response: any) => response.near.usd), + ); diff --git a/src/common/api/coingecko/index.ts b/src/common/api/coingecko/index.ts new file mode 100644 index 00000000..6c33b983 --- /dev/null +++ b/src/common/api/coingecko/index.ts @@ -0,0 +1 @@ +export * as coingecko from "./hooks"; diff --git a/src/common/constants.ts b/src/common/constants.ts index 5fd6479b..b6f7dd1f 100644 --- a/src/common/constants.ts +++ b/src/common/constants.ts @@ -45,6 +45,8 @@ export const PAGODA_REQUEST_CONFIG = { }, }; +export const COINGECKO_API_ENDPOINT = "https://api.coingecko.com/api/v3"; + // SYBIL CONTRACT export const NADABOT_CONTRACT_ID = process.env .NEXT_PUBLIC_NADABOT_CONTRACT_ID as string; @@ -70,6 +72,8 @@ export const POTLOCK_REGISTRY_LIST_ID = 1; export const NEAR_TOKEN_DENOM = "near"; +export const NEAR_DEFAULT_TOKEN_DECIMALS = 24; + // 1 NEAR export const ONE_NEAR = utils.format.parseNearAmount("1")!; // 0.5 NEAR diff --git a/src/common/lib/converters.ts b/src/common/lib/converters.ts new file mode 100644 index 00000000..8cfb0941 --- /dev/null +++ b/src/common/lib/converters.ts @@ -0,0 +1,19 @@ +import Big from "big.js"; + +import formatWithCommas from "./formatWithCommas"; +import { NEAR_DEFAULT_TOKEN_DECIMALS } from "../constants"; + +export const yoctosToNear = (amountYoctos: string, abbreviate?: boolean) => { + return ( + formatWithCommas(Big(amountYoctos).div(1e24).toFixed(2)) + + (abbreviate ? "N" : " NEAR") + ); +}; + +export const bigNumToFloat = (amount: string, decimals: number) => { + const decimalMultiplier = Big(10).pow(decimals); + return parseFloat(Big(amount).div(decimalMultiplier).toFixed(2)); +}; + +export const yoctoNearToFloat = (amountYoctoNear: string) => + bigNumToFloat(amountYoctoNear, NEAR_DEFAULT_TOKEN_DECIMALS); diff --git a/src/common/lib/index.ts b/src/common/lib/index.ts index 1f066632..b4ef6d77 100644 --- a/src/common/lib/index.ts +++ b/src/common/lib/index.ts @@ -1,2 +1,2 @@ export * from "./_address"; -export * from "./yoctosToNear"; +export * from "./converters"; diff --git a/src/common/lib/yoctosToNear.ts b/src/common/lib/yoctosToNear.ts deleted file mode 100644 index be793326..00000000 --- a/src/common/lib/yoctosToNear.ts +++ /dev/null @@ -1,10 +0,0 @@ -import Big from "big.js"; - -import formatWithCommas from "./formatWithCommas"; - -export const yoctosToNear = (amountYoctos: string, abbreviate?: boolean) => { - return ( - formatWithCommas(Big(amountYoctos).div(1e24).toFixed(2)) + - (abbreviate ? "N" : " NEAR") - ); -}; diff --git a/src/common/ui/components/text-field.tsx b/src/common/ui/components/text-field.tsx index 06d3beb8..a7384156 100644 --- a/src/common/ui/components/text-field.tsx +++ b/src/common/ui/components/text-field.tsx @@ -8,7 +8,7 @@ export interface TextFieldProps label: string; labelExtension?: React.ReactNode; fieldExtension?: React.ReactNode; - appendix?: string; + appendix?: string | null; } export const TextField = forwardRef( @@ -71,13 +71,17 @@ export const TextField = forwardRef( {fieldExtensionElement} { + const { data: oneNearUsdPrice } = coingecko.useOneNearUsdPrice(); + + return useMemo( + () => + `~$ ${formatWithCommas( + (oneNearUsdPrice + ? bigNumToFloat(amountYoctoNear, NEAR_DEFAULT_TOKEN_DECIMALS) * + oneNearUsdPrice + : 0.0 + ).toString(), + )}`, + + [amountYoctoNear, oneNearUsdPrice], + ); +}; diff --git a/src/modules/core/utils/index.ts b/src/modules/core/utils/index.ts index 761b730c..f1417d95 100644 --- a/src/modules/core/utils/index.ts +++ b/src/modules/core/utils/index.ts @@ -1,5 +1,7 @@ import Big from "big.js"; +import { NearBalanceResponse } from "@/common/api/pagoda/generated"; +import { bigNumToFloat } from "@/common/lib"; import formatWithCommas from "@/common/lib/formatWithCommas"; import { fetchNearPrice } from "@/common/services"; @@ -17,3 +19,14 @@ export const yoctosToUsdWithFallback = async ( : formatWithCommas(Big(amountYoctos).div(1e24).toFixed(2)) + (abbreviate ? "N" : " NEAR"); }; + +export const balanceToFloat = ( + amount: NearBalanceResponse["balance"]["amount"], + decimals: NearBalanceResponse["balance"]["metadata"]["decimals"], +) => bigNumToFloat(amount, decimals); + +export const balanceToString = ({ + amount, + metadata, +}: NearBalanceResponse["balance"]) => + `${balanceToFloat(amount, metadata.decimals)} ${metadata.symbol}`; diff --git a/src/modules/donation/components/DonationProjectAllocation.tsx b/src/modules/donation/components/DonationProjectAllocation.tsx index a6b29a5d..40039540 100644 --- a/src/modules/donation/components/DonationProjectAllocation.tsx +++ b/src/modules/donation/components/DonationProjectAllocation.tsx @@ -6,6 +6,7 @@ import { pagoda } from "@/common/api/pagoda"; import { ByAccountId, potlock } from "@/common/api/potlock"; import { NEAR_TOKEN_DENOM } from "@/common/constants"; import { walletApi } from "@/common/contracts"; +import { bigNumToFloat } from "@/common/lib"; import { DialogDescription, DialogHeader, @@ -25,7 +26,12 @@ import { SelectValue, TextField, } from "@/common/ui/components"; -import { RuntimeErrorAlert } from "@/modules/core"; +import { + RuntimeErrorAlert, + balanceToFloat, + balanceToString, +} from "@/modules/core"; +import { useYoctoNearUsdDisplayValue } from "@/modules/core/hooks/price"; import { DONATION_MIN_NEAR_AMOUNT } from "../constants"; import { @@ -82,6 +88,22 @@ export const DonationProjectAllocation: React.FC< [availableFtBalances, availableNearBalance, isFtDonation, tokenId], ); + const availableBalanceFloat = useMemo( + () => + availableBalance === null + ? null + : balanceToFloat( + availableBalance?.amount, + availableBalance?.metadata.decimals, + ), + + [availableBalance], + ); + + const availableNearBalanceUsdDisplayValue = useYoctoNearUsdDisplayValue( + availableNearBalance?.amount ?? "0", + ); + return isAccountLoading || isNearBalanceLoading || isFtBalanceLoading ? ( - {`${availableBalance.amount} ${availableBalance.metadata.symbol}`} + {balanceToString(availableBalance)} @@ -171,26 +193,52 @@ export const DonationProjectAllocation: React.FC< ) } fieldExtension={ - + ( + + )} + /> } type="number" placeholder="0.00" min={ tokenId === NEAR_TOKEN_DENOM ? DONATION_MIN_NEAR_AMOUNT : 0.0 } + max={availableBalanceFloat ?? undefined} step={0.01} - appendix="$ 0.00" + appendix={ + isFtDonation ? null : availableNearBalanceUsdDisplayValue + } />