From cf72706a4217993f37454104e0056a7e366b26af Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 20 Dec 2023 12:25:02 +0700 Subject: [PATCH 1/5] fix: token amount --- CHANGELOG.md | 1 + src/lib/components/token/TokenComposition.tsx | 10 ++- src/lib/services/move/poolService.ts | 66 ++++++++++--------- src/lib/types/move/pool.ts | 14 ++-- src/lib/types/pool.ts | 8 ++- src/lib/utils/assetValue.ts | 36 +++++----- src/lib/utils/formatter/token.ts | 10 ++- 7 files changed, 82 insertions(+), 63 deletions(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 55f6a44f6..dfed6cd74 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -52,6 +52,7 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ### Improvements +- [#682](https://github.com/alleslabs/celatone-frontend/pull/682) Render token amount < 0.000001 properly - [#669](https://github.com/alleslabs/celatone-frontend/pull/669) api v1 - contract transaction - [#672](https://github.com/alleslabs/celatone-frontend/pull/672) refactor balances - [#662](https://github.com/alleslabs/celatone-frontend/pull/662) Add republish button in module detail diff --git a/src/lib/components/token/TokenComposition.tsx b/src/lib/components/token/TokenComposition.tsx index 4e0566ae7..e59b91934 100644 --- a/src/lib/components/token/TokenComposition.tsx +++ b/src/lib/components/token/TokenComposition.tsx @@ -20,7 +20,10 @@ export const TokenComposition = ({ - {token.poolInfo.coinA.amount} + {formatUTokenWithPrecision( + token.poolInfo.coinA.amount, + token.poolInfo.coinA.precision ?? 0 + )} {getTokenLabel( token.poolInfo.coinA.denom, @@ -32,7 +35,10 @@ export const TokenComposition = ({ - {token.poolInfo.coinB.amount} + {formatUTokenWithPrecision( + token.poolInfo.coinB.amount, + token.poolInfo.coinB.precision ?? 0 + )} {getTokenLabel( token.poolInfo.coinB.denom, diff --git a/src/lib/services/move/poolService.ts b/src/lib/services/move/poolService.ts index b8c9834fd..f03eebeee 100644 --- a/src/lib/services/move/poolService.ts +++ b/src/lib/services/move/poolService.ts @@ -8,30 +8,35 @@ import { useBaseApiRoute, useMoveConfig, } from "lib/app-provider"; -import type { MovePoolInfos, Option, USD } from "lib/types"; +import type { MovePoolInfos, Option, Token, U, USD } from "lib/types"; +import { calculateAssetValue, toToken } from "lib/utils"; import { getMovePoolInfos } from "./pool"; -const computePricePerShare = ( - amountAPerShare: Big, +const computePricePerPShare = ( + amountAPerShare: Token, weightA: string, priceA: Option, - amountBPerShare: Big, + amountBPerShare: Token, weightB: string, - priceB: Option + priceB: Option, + poolPrecision: number ): Option> => { + const multiplier = big(10).pow(poolPrecision); if (priceA && priceB) - return big(priceA) - .times(amountAPerShare) - .plus(big(priceB).times(amountBPerShare)) as USD; + return calculateAssetValue(amountAPerShare, priceA as USD) + .plus(calculateAssetValue(amountBPerShare, priceB as USD)) + .times(multiplier) as USD; + + const totalWeight = big(weightA).plus(weightB); if (priceA) - return big(priceA) - .times(amountAPerShare) - .times(big(weightA).plus(weightB).div(weightA)) as USD; + return calculateAssetValue(amountAPerShare, priceA as USD) + .times(totalWeight.div(weightA)) + .times(multiplier) as USD; if (priceB) - return big(priceB) - .times(amountBPerShare) - .times(big(weightA).plus(weightB).div(weightB)) as USD; + return calculateAssetValue(amountBPerShare, priceB as USD) + .times(totalWeight.div(weightB)) + .times(multiplier) as USD; return undefined; }; @@ -61,29 +66,28 @@ export const useMovePoolInfos = () => { const data = pools?.reduce((acc, curr) => { const coinAInfo = assetInfos?.[curr.coin_a.denom]; - const coinAprecision = coinAInfo?.precision ?? 0; const coinBInfo = assetInfos?.[curr.coin_b.denom]; - const coinBprecision = coinBInfo?.precision ?? 0; - const totalShares = big(curr.total_share).div(big(10).pow(curr.precision)); - const [amountAPerShare, amountBPerShare] = totalShares.eq(0) + const totalShares = big(curr.total_share); + const [tempA, tempB] = totalShares.eq(0) ? [big(0), big(0)] : [ - big(curr.coin_a.amount) - .div(big(10).pow(coinAprecision)) - .div(totalShares), - big(curr.coin_b.amount) - .div(big(10).pow(coinBprecision)) - .div(totalShares), + big(curr.coin_a.amount).div(totalShares), + big(curr.coin_b.amount).div(totalShares), ]; + const [amountAPerShare, amountBPerShare] = [tempA, tempB] as [ + U>, + U>, + ]; - const lpPricePerShare = computePricePerShare( - amountAPerShare, + const lpPricePerPShare = computePricePerPShare( + toToken(amountAPerShare, coinAInfo?.precision ?? 0), curr.coin_a.weight, coinAInfo?.price, - amountBPerShare, + toToken(amountBPerShare, coinBInfo?.precision ?? 0), curr.coin_b.weight, - coinBInfo?.price + coinBInfo?.price, + curr.precision ); return { @@ -91,18 +95,18 @@ export const useMovePoolInfos = () => { [curr.lp_denom]: { coinA: { ...curr.coin_a, - precision: coinAprecision, amountAPerShare, + precision: coinAInfo?.precision, symbol: coinAInfo?.symbol, }, coinB: { ...curr.coin_b, - precision: coinBprecision, amountBPerShare, + precision: coinBInfo?.precision, symbol: coinBInfo?.symbol, }, precision: curr.precision, - lpPricePerShare, + lpPricePerPShare, logo: [coinAInfo?.logo, coinBInfo?.logo], }, }; diff --git a/src/lib/types/move/pool.ts b/src/lib/types/move/pool.ts index 935a24973..42bde75f1 100644 --- a/src/lib/types/move/pool.ts +++ b/src/lib/types/move/pool.ts @@ -1,6 +1,8 @@ +import type Big from "big.js"; + import type { HexAddr } from "../addrs"; import type { Option } from "../common"; -import type { USD } from "../currency"; +import type { Token, U, USD } from "../currency"; export type MovePoolInfos = Record< string, @@ -8,18 +10,18 @@ export type MovePoolInfos = Record< coinA: { metadata: HexAddr; denom: string; - precision: number; - amountAPerShare: Big; + amountAPerShare: U>; + precision: Option; symbol: Option; }; coinB: { metadata: HexAddr; denom: string; - precision: number; - amountBPerShare: Big; + amountBPerShare: U>; + precision: Option; symbol: Option; }; - lpPricePerShare: Option>; + lpPricePerPShare: Option>; precision: number; logo: Option[]; } diff --git a/src/lib/types/pool.ts b/src/lib/types/pool.ts index c311daac9..69697b0ed 100644 --- a/src/lib/types/pool.ts +++ b/src/lib/types/pool.ts @@ -6,7 +6,9 @@ import type { ContractAddr, Nullable, Option, + Token, TokenWithValue, + U, } from "lib/types"; export enum PoolType { @@ -57,12 +59,14 @@ export interface PoolDetail< export interface PoolInfo { coinA: { - amount: string; + amount: U>; + precision: Option; denom: string; symbol: Option; }; coinB: { - amount: string; + amount: U>; + precision: Option; denom: string; symbol: Option; }; diff --git a/src/lib/utils/assetValue.ts b/src/lib/utils/assetValue.ts index 34118ea4e..985dc92d5 100644 --- a/src/lib/utils/assetValue.ts +++ b/src/lib/utils/assetValue.ts @@ -12,11 +12,11 @@ import type { USD, } from "lib/types"; -import { formatUTokenWithPrecision, getTokenLabel, toToken } from "./formatter"; +import { getTokenLabel, toToken } from "./formatter"; export const calculateAssetValue = ( amount: Token, - price: USD + price: USD ): USD => big(amount).mul(price) as USD; export const filterSupportedTokens = (tokens: Option) => @@ -45,6 +45,7 @@ export const coinToTokenWithValue = ( const tokenAmount = big(amount) as U>; const assetInfo = assetInfos?.[denom]; const movePoolInfo = poolInfos?.[denom]; + return movePoolInfo ? { isLPToken: true, @@ -59,30 +60,27 @@ export const coinToTokenWithValue = ( )}`, logo: movePoolInfo.logo, precision: movePoolInfo.precision, - price: movePoolInfo.lpPricePerShare, - value: movePoolInfo.lpPricePerShare - ? (tokenAmount - .times(movePoolInfo.lpPricePerShare) - .div(big(10).pow(movePoolInfo.precision)) as USD) + price: movePoolInfo.lpPricePerPShare, + value: movePoolInfo.lpPricePerPShare + ? calculateAssetValue( + toToken(tokenAmount, movePoolInfo.precision), + movePoolInfo.lpPricePerPShare + ) : undefined, poolInfo: { coinA: { - amount: formatUTokenWithPrecision( - tokenAmount.times(movePoolInfo.coinA.amountAPerShare) as U< - Token - >, - movePoolInfo.precision - ), + amount: tokenAmount.times(movePoolInfo.coinA.amountAPerShare) as U< + Token + >, + precision: movePoolInfo.coinA.precision, denom: movePoolInfo.coinA.denom, symbol: movePoolInfo.coinA.symbol, }, coinB: { - amount: formatUTokenWithPrecision( - tokenAmount.times(movePoolInfo.coinB.amountBPerShare) as U< - Token - >, - movePoolInfo.precision - ), + amount: tokenAmount.times(movePoolInfo.coinB.amountBPerShare) as U< + Token + >, + precision: movePoolInfo.coinB.precision, denom: movePoolInfo.coinB.denom, symbol: movePoolInfo.coinB.symbol, }, diff --git a/src/lib/utils/formatter/token.ts b/src/lib/utils/formatter/token.ts index 406457416..a86d3ef71 100644 --- a/src/lib/utils/formatter/token.ts +++ b/src/lib/utils/formatter/token.ts @@ -58,6 +58,7 @@ export const toToken = ( } }; +const LOWEST_THRESHOLD = 0.000001; /** * @remarks * If token is more than or equal to 1 billion, should add suffix B and format to 2 decimal point @@ -75,6 +76,10 @@ export const formatUTokenWithPrecision = ( if (token.gte(M)) return `${d2Formatter(token.div(M), "0.00")}M`; if (token.gte(K)) return `${d2Formatter(token, "0.00")}`; } + if (token.lt(LOWEST_THRESHOLD)) { + return `<${LOWEST_THRESHOLD}`; + } + return formatDecimal({ decimalPoints: decimalPoints ?? precision, delimiter: true, @@ -91,7 +96,6 @@ export const formatUTokenWithPrecision = ( export const formatPrice = (value: USD): string => { try { const price = big(value); - const lowestThreshold = 0.000001; const d2 = d2Formatter(price, "0.00"); const d6 = d6Formatter(price, "0.00"); @@ -102,8 +106,8 @@ export const formatPrice = (value: USD): string => { return `$${d2}`; } - if (price.lt(lowestThreshold)) { - return `<$${lowestThreshold}`; + if (price.lt(LOWEST_THRESHOLD)) { + return `<$${LOWEST_THRESHOLD}`; } return `$${d6}`; } catch { From 9eb3355783944af4d1e48e94f8879dbda9bf5b2d Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 20 Dec 2023 12:29:44 +0700 Subject: [PATCH 2/5] fix: test case --- src/lib/utils/assetValue.test.ts | 37 +++++++++++++++----------------- 1 file changed, 17 insertions(+), 20 deletions(-) diff --git a/src/lib/utils/assetValue.test.ts b/src/lib/utils/assetValue.test.ts index bb1597501..f3a9e5f01 100644 --- a/src/lib/utils/assetValue.test.ts +++ b/src/lib/utils/assetValue.test.ts @@ -16,7 +16,6 @@ import { coinToTokenWithValue, filterSupportedTokens, } from "./assetValue"; -import { formatUTokenWithPrecision } from "./formatter"; describe("filterSupportedTokens", () => { const token1: TokenWithValue = { @@ -53,12 +52,14 @@ describe("filterSupportedTokens", () => { poolInfo: { coinA: { denom: "", - amount: "", + amount: big(0) as U>, + precision: undefined, symbol: undefined, }, coinB: { denom: "", - amount: "", + amount: big(0) as U>, + precision: undefined, symbol: undefined, }, }, @@ -106,17 +107,17 @@ describe("coinToTokenWithValue", () => { metadata: zHexAddr.parse("0x1"), denom: "denom1", precision: 6, - amountAPerShare: big(1), + amountAPerShare: big(1) as U>, symbol: undefined, }, coinB: { metadata: zHexAddr.parse("0x2"), denom: "denom2", precision: 6, - amountBPerShare: big(1), + amountBPerShare: big(1) as U>, symbol: "DENOM_2", }, - lpPricePerShare: big(1) as USD, + lpPricePerPShare: big(0.000001) as USD, precision: 6, logo: ["denom1_logo", "denom2_logo"], }, @@ -144,28 +145,24 @@ describe("coinToTokenWithValue", () => { amount: big(coin.amount) as U>, symbol: `${movePoolInfo.coinA.denom}-${movePoolInfo.coinB.symbol}`, precision: movePoolInfo.precision, - price: movePoolInfo.lpPricePerShare, + price: movePoolInfo.lpPricePerPShare, value: big(coin.amount) - .mul(movePoolInfo.lpPricePerShare ?? big(0)) + .mul(movePoolInfo.lpPricePerPShare ?? big(0)) .div(10 ** movePoolInfo.precision) as USD, poolInfo: { coinA: { - amount: formatUTokenWithPrecision( - big(coin.amount).times(movePoolInfo.coinA.amountAPerShare) as U< - Token - >, - movePoolInfo.precision - ), + amount: movePoolInfo.coinA.amountAPerShare.times(coin.amount) as U< + Token + >, + precision: 6, denom: movePoolInfo.coinA.denom, symbol: movePoolInfo.coinA.symbol, }, coinB: { - amount: formatUTokenWithPrecision( - big(coin.amount).times(movePoolInfo.coinB.amountBPerShare) as U< - Token - >, - movePoolInfo.precision - ), + amount: movePoolInfo.coinB.amountBPerShare.times(coin.amount) as U< + Token + >, + precision: 6, denom: movePoolInfo.coinB.denom, symbol: movePoolInfo.coinB.symbol, }, From a736957a51c9acbbee147e1b8f6556ab7235d611 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:16:53 +0700 Subject: [PATCH 3/5] fix: stone 12 gql --- src/config/chain/initia.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/config/chain/initia.ts b/src/config/chain/initia.ts index 19ee55913..9b48398e1 100644 --- a/src/config/chain/initia.ts +++ b/src/config/chain/initia.ts @@ -155,7 +155,7 @@ export const INITIA_CHAIN_CONFIGS: ChainConfigs = { prettyName: "Initia Testnet 12-1", lcd: "https://next-stone-rest.initia.tech", rpc: "https://next-stone-rpc.initia.tech:443", - indexer: "https://stone-12-1-graphql.alleslabs.dev/v1/graphql", + indexer: "https://stone-12-1-nft-graphql.alleslabs.dev/v1/graphql", wallets: [...keplrWallets], features: { faucet: { From 99e51174a5b6597241eedf7971ba01d4ae7e371f Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 20 Dec 2023 16:41:53 +0700 Subject: [PATCH 4/5] fix: add test --- src/lib/utils/formatter/token.test.ts | 11 +++++++++++ src/lib/utils/formatter/token.ts | 12 +++++++----- 2 files changed, 18 insertions(+), 5 deletions(-) diff --git a/src/lib/utils/formatter/token.test.ts b/src/lib/utils/formatter/token.test.ts index 826334c12..e95317a10 100644 --- a/src/lib/utils/formatter/token.test.ts +++ b/src/lib/utils/formatter/token.test.ts @@ -183,6 +183,17 @@ describe("formatUTokenWithPrecision", () => { ).toEqual("0.000"); }); }); + test("too small", () => { + expect(formatUTokenWithPrecision("0.1" as U, 6, false)).toEqual( + "<0.000001" + ); + expect(formatUTokenWithPrecision("0.1" as U, 7, false)).toEqual( + "<0.0000001" + ); + expect(formatUTokenWithPrecision("0.1" as U, 6, false, 2)).toEqual( + "<0.01" + ); + }); test("no suffix", () => { expect( formatUTokenWithPrecision("12345678901234567890" as U, 6, false) diff --git a/src/lib/utils/formatter/token.ts b/src/lib/utils/formatter/token.ts index a86d3ef71..e5a29c488 100644 --- a/src/lib/utils/formatter/token.ts +++ b/src/lib/utils/formatter/token.ts @@ -58,7 +58,6 @@ export const toToken = ( } }; -const LOWEST_THRESHOLD = 0.000001; /** * @remarks * If token is more than or equal to 1 billion, should add suffix B and format to 2 decimal point @@ -76,8 +75,10 @@ export const formatUTokenWithPrecision = ( if (token.gte(M)) return `${d2Formatter(token.div(M), "0.00")}M`; if (token.gte(K)) return `${d2Formatter(token, "0.00")}`; } - if (token.lt(LOWEST_THRESHOLD)) { - return `<${LOWEST_THRESHOLD}`; + + const lowestThreshold = big(10).pow(-(decimalPoints ?? precision)); + if (!token.eq(0) && token.lt(lowestThreshold)) { + return `<${lowestThreshold.toFixed()}`; } return formatDecimal({ @@ -96,6 +97,7 @@ export const formatUTokenWithPrecision = ( export const formatPrice = (value: USD): string => { try { const price = big(value); + const lowestThreshold = 0.000001; const d2 = d2Formatter(price, "0.00"); const d6 = d6Formatter(price, "0.00"); @@ -106,8 +108,8 @@ export const formatPrice = (value: USD): string => { return `$${d2}`; } - if (price.lt(LOWEST_THRESHOLD)) { - return `<$${LOWEST_THRESHOLD}`; + if (price.lt(lowestThreshold)) { + return `<$${lowestThreshold}`; } return `$${d6}`; } catch { From 5fea93fcf0924cf9f7e3faeab185530a1689d070 Mon Sep 17 00:00:00 2001 From: songwongtp <16089160+songwongtp@users.noreply.github.com> Date: Wed, 20 Dec 2023 17:22:36 +0700 Subject: [PATCH 5/5] fix: handle 0 weight --- src/lib/services/move/poolService.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/lib/services/move/poolService.ts b/src/lib/services/move/poolService.ts index f03eebeee..02e885ca8 100644 --- a/src/lib/services/move/poolService.ts +++ b/src/lib/services/move/poolService.ts @@ -29,11 +29,11 @@ const computePricePerPShare = ( .times(multiplier) as USD; const totalWeight = big(weightA).plus(weightB); - if (priceA) + if (priceA && big(weightA).gt(0)) return calculateAssetValue(amountAPerShare, priceA as USD) .times(totalWeight.div(weightA)) .times(multiplier) as USD; - if (priceB) + if (priceB && big(weightB).gt(0)) return calculateAssetValue(amountBPerShare, priceB as USD) .times(totalWeight.div(weightB)) .times(multiplier) as USD;