From 0b4fe20dae1a270d30b6412736f378b5de3999fb Mon Sep 17 00:00:00 2001 From: Max Date: Thu, 1 Jun 2023 13:36:55 +0800 Subject: [PATCH 001/204] add recent transactions template --- src/pages/pools/DetailsPage.tsx | 145 ++++++++++++++++++++-- src/pages/poolsV3/PoolDetailV3.tsx | 190 ++++++++++++++++++++++++++++- 2 files changed, 326 insertions(+), 9 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 75cfeb4ef..ad6e04856 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -186,13 +186,8 @@ interface LocationTypes { export type ChartType = 'volume' | 'tvl' | 'liquidity'; const ONLY_ZEROS = /^0*\.?0*$/; -const getMax = function (id: string, max: string) { - return id !== WRAP_NEAR_CONTRACT_ID - ? max - : Number(max) <= 0.5 - ? '0' - : String(Number(max) - 0.5); -}; +const REF_FI_RECENT_TRANSACTION_TAB_KEY = 'REF_FI_RECENT_TRANSACTION_TAB_KEY'; + const formatDate = (rawDate: string) => { const date = rawDate .split('-') @@ -1393,6 +1388,138 @@ function MyShares({ ); } +type RencentTabKey = 'swap' | 'liquidity'; + +export function RecentTransactions({ id }: { id: string | number }) { + const storedTab = sessionStorage.getItem( + REF_FI_RECENT_TRANSACTION_TAB_KEY + ) as RencentTabKey; + + const [tab, setTab] = useState(storedTab || 'swap'); + + const onChangeTab = (tab: RencentTabKey) => { + sessionStorage.setItem(REF_FI_RECENT_TRANSACTION_TAB_KEY, tab); + setTab(tab); + }; + + return ( + <> +
+
+ +
+ +
+
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('swap'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('liquidity'); + }} + > + +
+
+
+ + + + + + + + + + + + + + + + + + + +
+ {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + +
ttt
+ + ); +} + export const ChartChangeButton = ({ chartDisplay, setChartDisplay, @@ -2419,7 +2546,7 @@ export function PoolDetailsPage() { /> -
+
+ +
(storedTab || 'swap'); + + const onChangeTab = (tab: RencentTabKey) => { + sessionStorage.setItem(REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL, tab); + setTab(tab); + }; + + return ( + <> +
+
+ +
+ +
+
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('swap'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('liquidity'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('limit_order'); + }} + > + +
+
+
+ + + + + + + + + {tab === 'limit_order' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + + + + + + + + + +
+ {tab === 'liquidity' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + + + +
ttt
+ + ); +} + function TablePool(props: any) { const { poolDetail, tokenPriceList } = props; const [tokens, setTokens] = useState([]); @@ -1861,7 +2047,7 @@ function TablePool(props: any) { } return (
-
+
))}
+ +
); } From 9a0a2f452448f3a0aa2193de197a189381aedecf Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 2 Jun 2023 23:05:41 +0900 Subject: [PATCH 002/204] add classic pool recent transactions --- src/components/pool/YourLiquidityV2.tsx | 23 ++-- src/pages/pools/DetailsPage.tsx | 170 ++++++++++++++++++++++-- src/services/indexer.ts | 161 +++++++++++++++++++++- src/state/pool.ts | 36 +++++ tailwind.config.js | 17 +-- 5 files changed, 375 insertions(+), 32 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 8b8a95fdb..65809e262 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -648,9 +648,9 @@ function UserLiquidityLine({ }} > {styleType == '1' ? ( - + ) : ( - + )} @@ -690,6 +690,14 @@ function UserLiquidityLineStyle1() { liquidityDetail, showAddBox, } = useContext(LiquidityContext); + + const history = useHistory(); + + function goDetailV2() { + const url_pool_id = get_pool_name(poolDetail.pool_id); + history.push(`/poolV2/${url_pool_id}`); + } + const tokens = sort_tokens_by_base(tokenMetadata_x_y); return (
{/* for PC */}
-
- - - NFT ID #{getLpt_id()} - -
@@ -942,7 +944,7 @@ function UserLiquidityLineStyle1() {
@@ -1239,7 +1241,6 @@ function UserLiquidityLineStyle1() { function UserLiquidityLineStyle2() { const { getLpt_id, - goYourLiquidityDetailPage, goPoolDetailPage, tokenMetadata_x_y, fee, diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index ad6e04856..3d1fa694f 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -13,6 +13,7 @@ import { TVLDataType, TVLType, useDayVolume, + useClassicPoolTransaction, } from '~state/pool'; import { addLiquidityToPool, @@ -1390,7 +1391,17 @@ function MyShares({ type RencentTabKey = 'swap' | 'liquidity'; -export function RecentTransactions({ id }: { id: string | number }) { +export function RecentTransactions({ + tokens, + pool_id, +}: { + tokens: TokenMetadata[]; + pool_id: string | number; +}) { + const { swapTransaction, liquidityTransactions } = useClassicPoolTransaction({ + pool_id, + }); + const storedTab = sessionStorage.getItem( REF_FI_RECENT_TRANSACTION_TAB_KEY ) as RencentTabKey; @@ -1402,6 +1413,137 @@ export function RecentTransactions({ id }: { id: string | number }) { setTab(tab); }; + const renderSwapTransactions = swapTransaction.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.token_in); + + const swapOut = tokens.find((t) => t.id === tx.token_out); + + if (!swapIn || !swapOut) return null; + + const swapInAmount = toReadableNumber(swapIn.decimals, tx.swap_in); + const displayInAmount = + Number(swapInAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapInAmount, 6)); + + const swapOutAmount = toReadableNumber(swapOut.decimals, tx.swap_out); + + const displayOutAmount = + Number(swapOutAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapOutAmount, 6)); + + const txLink = ( + + Tx + + ); + + return ( + + + + {displayInAmount} + + + {toRealSymbol(swapIn.symbol)} + + + + + {displayOutAmount} + + + {toRealSymbol(swapOut.symbol)} + + + + {tx.timestamp} + + {txLink} + + + ); + }); + + const renderLiquidityTransactions = liquidityTransactions.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.token_in); + + const swapOut = tokens.find((t) => t.id === tx.token_out); + + if (!swapIn || !swapOut) return null; + + const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_in); + const displayInAmount = + Number(AmountIn) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountIn, 6)); + + const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_out); + + const displayOutAmount = + Number(AmountOut) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountOut, 6)); + + const txLink = ( + + Tx + + ); + + return ( + + + + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Add'} + + {tx.method_name.toLowerCase().indexOf('remove') > -1 && 'Remove'} + + + + + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + + + + + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + + + + + {tx.timestamp} + + {txLink} + + + ); + }); + + const renderTx = + tab === 'swap' ? renderSwapTransactions : renderLiquidityTransactions; + return ( <>
@@ -1478,12 +1620,12 @@ export function RecentTransactions({ id }: { id: string | number }) { style={{ width: '45%', }} - className="p-4 pb-3" + className="py-4 pb-3" > {tab === 'liquidity' && ( )} {tab === 'swap' && ( @@ -1498,7 +1640,7 @@ export function RecentTransactions({ id }: { id: string | number }) { style={{ width: '30%', }} - className="p-4 pb-3" + className="pr-4 py-4 pb-3" > - - - t - t - t - + + {renderTx} @@ -2682,7 +2825,10 @@ export function PoolDetailsPage() { })}
- +
) => { + return Object.keys(props) + .map((key) => key + '=' + props[key]) + .join('&'); +}; + export const getPoolsByTokensIndexer = async ({ token0, token1, @@ -310,8 +316,161 @@ export const getPool = async (pool_id: string): Promise => { return parsePoolView(pool); }); }; +const parsePoolTxTimeStamp = (ts: string) => { + return moment(Math.floor(Number(ts) / 1000000)).format('YYYY-MM-DD HH:mm:ss'); +}; + +export interface ClassicPoolSwapTransaction { + token_in: string; + token_out: string; + swap_in: string; + swap_out: string; + timestamp: string; + tx_id: string; +} + +export const getClassicPoolSwapRecentTransaction = async (props: { + pool_id: string | number; +}) => { + const paramString = genUrlParams(props); + + return await fetch( + config.indexerUrl + `/get-recent-transaction-swap?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ) + .then((res) => res.json()) + .then((res: ClassicPoolSwapTransaction[]) => { + return res.map((tx) => { + return { + ...tx, + timestamp: parsePoolTxTimeStamp(tx.timestamp), + }; + }); + }); +}; + +export interface DCLPoolSwapTransaction { + token_in: string; + token_out: string; + amount_in: string; + amount_out: string; + timestamp: string; + tx_id: string; +} + +export const getDCLPoolSwapRecentTransaction = async (props: { + pool_id: string; +}) => { + const paramString = genUrlParams(props); + + return await fetch( + config.indexerUrl + `/get-recent-transaction-dcl-swap?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ) + .then((res) => res.json()) + .then((res: DCLPoolSwapTransaction[]) => { + return res.map((t) => ({ + ...t, + timestamp: parsePoolTxTimeStamp(t.timestamp), + })); + }); +}; + +export interface ClassicPoolLiquidtyRecentTransaction { + method_name: string; + timestamp: string; + token_in: string; + token_out: string; + amount_in: string; + amount_out: string; + tx_id: string; +} + +export const getClassicPoolLiquidtyRecentTransaction = async (props: { + pool_id: string | number; +}) => { + const paramString = genUrlParams(props); + + return await fetch( + config.indexerUrl + `/get-recent-transaction-liquidity?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ) + .then((res) => res.json()) + .then((res: ClassicPoolLiquidtyRecentTransaction[]) => { + return res.map((t) => ({ + ...t, + timestamp: parsePoolTxTimeStamp(t.timestamp), + })); + }); +}; + +export interface DCLPoolLiquidtyRecentTransaction { + method_name: string; + amount_x: string; + amount_y: string; + timestamp: string; + tx_id: string; +} + +export const getDCLPoolLiquidtyRecentTransaction = async (props: { + pool_id: string; +}) => { + const paramString = genUrlParams(props); + + return await fetch( + config.indexerUrl + `/get-recent-transaction-dcl-liquidity?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ) + .then((res) => res.json()) + .then((res: DCLPoolLiquidtyRecentTransaction[]) => { + return res.map((t) => ({ + ...t, + timestamp: parsePoolTxTimeStamp(t.timestamp), + })); + }); +}; + +export interface LimitOrderRecentTransaction { + method_name: string; + timestamp: string; + amount: string; + tx_id: string; + point: string; + sell_token: string; +} -// https://testnet-indexer.ref-finance.com/get-proposal-hash-by-id?proposal_id=11|12 +export const getLimitOrderRecentTransaction = async (props: { + pool_id: string; +}) => { + const paramString = genUrlParams(props); + + return await fetch( + config.indexerUrl + `/get-recent-transaction-limit-order?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ) + .then((res) => res.json()) + .then((res: LimitOrderRecentTransaction[]) => { + return res.map((t) => ({ + ...t, + timestamp: parsePoolTxTimeStamp(t.timestamp), + })); + }); +}; export interface ProposalHash { proposal_id: string; diff --git a/src/state/pool.ts b/src/state/pool.ts index ca4d520c7..5540c87a5 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -46,6 +46,10 @@ import { getV3PoolVolumeById, getAllV3Pool24Volume, getV3poolTvlById, + getClassicPoolSwapRecentTransaction, + ClassicPoolSwapTransaction, + ClassicPoolLiquidtyRecentTransaction, + getClassicPoolLiquidtyRecentTransaction, } from '../services/indexer'; import { parsePoolView, PoolRPCView } from '../services/api'; import { @@ -86,6 +90,7 @@ import { PoolInfo, get_pool } from '../services/swapV3'; import { useTokenPriceList } from './token'; import { isStablePool } from '../services/near'; import { getStablePoolDecimal } from '../pages/stable/StableSwapEntry'; +import { useWalletSelector } from '~context/WalletSelectorContext'; const REF_FI_STABLE_POOL_INFO_KEY = `REF_FI_STABLE_Pool_INFO_VALUE_${ getConfig().STABLE_POOL_ID }`; @@ -1344,3 +1349,34 @@ export const useV3VolumesPools = () => { return volumes; }; + +export const useClassicPoolTransaction = ({ + pool_id, +}: { + pool_id: string | number; +}) => { + const [swapRecent, setSwapRecent] = useState( + [] + ); + + const [lqRecent, setLqRecent] = useState< + ClassicPoolLiquidtyRecentTransaction[] + >([]); + + useEffect(() => { + getClassicPoolSwapRecentTransaction({ + pool_id, + }).then(setSwapRecent); + + getClassicPoolLiquidtyRecentTransaction({ + pool_id, + }).then(setLqRecent); + + // getClassicPoolSwapRecentTransaction({ + // account_id: accountId, + // token_one: + // }) + }, []); + + return { swapTransaction: swapRecent, liquidityTransactions: lqRecent }; +}; diff --git a/tailwind.config.js b/tailwind.config.js index 5099e6fbf..f1e3a67ab 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -127,7 +127,7 @@ module.exports = { 'linear-gradient(180deg, #00C6A2 0%, #5B40FF 100%)', darkBlueColor: 'linear-gradient(360deg, #1A2B35 0%, rgba(19, 33, 42, 0) 100%)', - purpleBgColor:"linear-gradient(180deg, #C3BDAD 0%, #AF9773 100%)", + purpleBgColor: 'linear-gradient(180deg, #C3BDAD 0%, #AF9773 100%)', }), gridTemplateColumns: { farmSearch: '2fr 1fr', @@ -329,6 +329,7 @@ module.exports = { textRed: '#FF7575', baseGreen: '#00C6A2', orderLineHover: '#14222D', + poolRecentHover: 'rgba(37,51,61,0.5)', dark2: '#071C2B', dark3: '#39454E', assetsBorder: '#415767', @@ -383,11 +384,11 @@ module.exports = { light_red_color: 'rgba(255, 106, 142, 0.15)', menuBorderColor: '#27343E', guideBgColor: 'rgba(0, 19, 32, 0.8)', - burrowTabColor:'#22333E', - burrowTableBorderColor:"rgba(48, 67, 82, 0.5)", - burrowTitleGreenColor:'#78FF9E', - burrowPurpleColor:'#BCAB8F', - burrowDarkColor:'#04121F', + burrowTabColor: '#22333E', + burrowTableBorderColor: 'rgba(48, 67, 82, 0.5)', + burrowTitleGreenColor: '#78FF9E', + burrowPurpleColor: '#BCAB8F', + burrowDarkColor: '#04121F', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], @@ -431,7 +432,7 @@ module.exports = { p150: '150px', p212: '212px', '1000px': '1000px', - '1280px': '1280px' + '1280px': '1280px', }, height: { vh90: '90vh', @@ -455,7 +456,7 @@ module.exports = { '420px': '420px', p240: '240px', p90: '90px', - p300: '300px' + p300: '300px', }, fontSize: { From e2e5463659e7d5a904b7c1ab047d6e599ac7fb29 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 3 Jun 2023 23:18:49 +0900 Subject: [PATCH 003/204] update recent transactions table --- src/pages/pools/DetailsPage.tsx | 134 +++++---- src/pages/poolsV3/PoolDetailV3.tsx | 423 +++++++++++++++++++++++------ src/services/indexer.ts | 6 +- src/state/pool.ts | 47 +++- 4 files changed, 440 insertions(+), 170 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 3d1fa694f..14b89d1d8 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -175,6 +175,7 @@ import Big from 'big.js'; import { getEffectiveFarmList, sort_tokens_by_base } from '~services/commonV3'; import { openUrl } from '../../services/commonV3'; import { numberWithCommas } from '../Orderly/utiles'; +import { HiOutlineExternalLink, HiOutlineLink } from 'react-icons/hi'; interface ParamTypes { id: string; @@ -1437,33 +1438,37 @@ export function RecentTransactions({ - Tx + ); return ( - - + + {displayInAmount} - {toRealSymbol(swapIn.symbol)} + + {toRealSymbol(swapIn.symbol)} + - + {displayOutAmount} - {toRealSymbol(swapOut.symbol)} + + {toRealSymbol(swapOut.symbol)} + - - {tx.timestamp} + + {tx.timestamp} {txLink} @@ -1495,15 +1500,15 @@ export function RecentTransactions({ - Tx + ); return ( - + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Add'} @@ -1512,7 +1517,7 @@ export function RecentTransactions({ - + {displayInAmount} @@ -1532,8 +1537,8 @@ export function RecentTransactions({ - - {tx.timestamp} + + {tx.timestamp} {txLink} @@ -1593,72 +1598,57 @@ export function RecentTransactions({
- - - - - - +
+
+
+ {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} +
-
- - + )} + {tab === 'swap' && ( + + )} + - + + + + +
{renderTx} -
-
- {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} - - {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} - +
+ {tab === 'liquidity' && ( -
+
+
); } diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 5efd46f64..8287f5a60 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -30,6 +30,7 @@ import { get_pool_marketdepth, claim_all_liquidity_fee, get_metadata, + pointToPrice, } from '~services/swapV3'; import { UserLiquidityInfo, @@ -96,7 +97,11 @@ import { RemovePoolV3 } from '~components/pool/RemovePoolV3'; import { AddPoolV3 } from '~components/pool/AddPoolV3'; import Modal from 'react-modal'; import { ModalClose } from '~components/icon'; -import { useV3VolumeChart, useV3TvlChart } from '~state/pool'; +import { + useV3VolumeChart, + useV3TvlChart, + useDCLPoolTransaction, +} from '~state/pool'; import { getV3Pool24VolumeById } from '~services/indexer'; import { list_farmer_seeds, @@ -114,6 +119,9 @@ import { import _ from 'lodash'; import { PoolRPCView } from '../../services/api'; import { FarmStampNew } from '../../components/icon/FarmStamp'; +import { numberWithCommas } from '~pages/Orderly/utiles'; +import { HiOutlineExternalLink } from 'react-icons/hi'; +import Big from 'big.js'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); export default function PoolDetailV3() { @@ -1801,11 +1809,21 @@ type RencentTabKey = 'swap' | 'liquidity' | 'limit_order'; const REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL = 'REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL'; -export function RecentTransactions() { +export function RecentTransactions({ + pool_id, + tokens, +}: { + pool_id: string; + tokens: TokenMetadata[]; +}) { const storedTab = sessionStorage.getItem( REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL ) as RencentTabKey; + const { swapTransactions, liquidityTransactions, limitOrderTransactions } = + useDCLPoolTransaction({ pool_id }); + console.log('pool_id: ', pool_id); + const [tab, setTab] = useState(storedTab || 'swap'); const onChangeTab = (tab: RencentTabKey) => { @@ -1813,6 +1831,236 @@ export function RecentTransactions() { setTab(tab); }; + const renderSwapTransactions = swapTransactions.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.token_in); + + const swapOut = tokens.find((t) => t.id === tx.token_out); + + if (!swapIn || !swapOut) return null; + + const swapInAmount = toReadableNumber(swapIn.decimals, tx.amount_in); + const displayInAmount = + Number(swapInAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapInAmount, 6)); + + const swapOutAmount = toReadableNumber(swapOut.decimals, tx.amount_out); + + const displayOutAmount = + Number(swapOutAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapOutAmount, 6)); + + const txLink = ( + + + + ); + + return ( + + + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + + + + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + + + + + {tx.timestamp} + + {txLink} + + + ); + }); + + const renderLiquidityTransactions = liquidityTransactions.map((tx) => { + const swapIn = tokens[0]; + + const swapOut = tokens[1]; + + if (!swapIn || !swapOut) return null; + + const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_x); + const displayInAmount = + Number(AmountIn) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountIn, 6)); + + const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_y); + + const displayOutAmount = + Number(AmountOut) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountOut, 6)); + + const txLink = ( + + + + ); + + return ( + + + + {(tx.method_name.toLowerCase().indexOf('add') > -1 || + tx.method_name.toLowerCase().indexOf('append') > -1) && + 'Add'} + + {tx.method_name.toLowerCase().indexOf('remove') > -1 && 'Remove'} + + + + + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + + + + + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + + + + + {tx.timestamp} + + {txLink} + + + ); + }); + + const renderLimitOrderTransactions = limitOrderTransactions.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.sell_token); + + const swapOut = tokens.find((t) => t.id !== tx.sell_token); + + if (!swapIn || !swapOut) return null; + + const AmountIn = toReadableNumber(swapIn.decimals, tx.amount); + const displayInAmount = + Number(AmountIn) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountIn, 6)); + + const price = pointToPrice({ + tokenA: swapIn, + tokenB: swapOut, + point: + swapIn.id === pool_id.split('|')[0] + ? Number(tx.point) + : -Number(tx.point), + }); + + const AmountOut = new Big(AmountIn).mul(price).toFixed(0, 0); + + const displayOutAmount = + Number(AmountOut) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountOut, 6)); + + const txLink = ( + + + + ); + + return ( + + + {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && + 'Cancelled'} + + {tx.method_name.toLowerCase().indexOf('place') > -1 && 'Place'} + + + + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + + + + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + + + + + + {numberWithCommas(toPrecision(price, 4))} + + + + {toRealSymbol(swapOut.symbol)}/{toRealSymbol(swapIn.symbol)} + + + + + + {tx.timestamp} + + + {txLink} + + + ); + }); + + const renderTransactions = + tab === 'swap' + ? renderSwapTransactions + : tab === 'liquidity' + ? renderLiquidityTransactions + : renderLimitOrderTransactions; return ( <>
@@ -1880,104 +2128,96 @@ export function RecentTransactions() {
- - - - +
+
+ + {tab === 'swap' && ( + + )} + - {tab === 'limit_order' && ( - + + )} + - - - - - - - - - - - -
- {tab === 'liquidity' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - {tab === 'swap' && ( - - )} -
+ {tab === 'liquidity' && ( + + )} - - {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} + {tab === 'limit_order' && ( + + )} - {tab === 'limit_order' && ( - - )} - - - + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + )} {tab === 'limit_order' && ( - - - + {tab === 'limit_order' && ( +
ttt
+ )} + + {tab === 'limit_order' && ( + + + + )} + + + + + + +
+ {renderTransactions} +
+
); } @@ -2136,7 +2376,10 @@ function TablePool(props: any) { ))}
- + t.meta)} + pool_id={poolDetail.pool_id} + >
); } diff --git a/src/services/indexer.ts b/src/services/indexer.ts index eb5a80ff2..affddad0b 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -362,7 +362,7 @@ export interface DCLPoolSwapTransaction { } export const getDCLPoolSwapRecentTransaction = async (props: { - pool_id: string; + pool_id: string | number; }) => { const paramString = genUrlParams(props); @@ -422,7 +422,7 @@ export interface DCLPoolLiquidtyRecentTransaction { } export const getDCLPoolLiquidtyRecentTransaction = async (props: { - pool_id: string; + pool_id: string | number; }) => { const paramString = genUrlParams(props); @@ -452,7 +452,7 @@ export interface LimitOrderRecentTransaction { } export const getLimitOrderRecentTransaction = async (props: { - pool_id: string; + pool_id: string | number; }) => { const paramString = genUrlParams(props); diff --git a/src/state/pool.ts b/src/state/pool.ts index 5540c87a5..6f5fe2ec4 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -50,6 +50,12 @@ import { ClassicPoolSwapTransaction, ClassicPoolLiquidtyRecentTransaction, getClassicPoolLiquidtyRecentTransaction, + DCLPoolSwapTransaction, + DCLPoolLiquidtyRecentTransaction, + getDCLPoolSwapRecentTransaction, + getDCLPoolLiquidtyRecentTransaction, + getLimitOrderRecentTransaction, + LimitOrderRecentTransaction, } from '../services/indexer'; import { parsePoolView, PoolRPCView } from '../services/api'; import { @@ -1371,12 +1377,43 @@ export const useClassicPoolTransaction = ({ getClassicPoolLiquidtyRecentTransaction({ pool_id, }).then(setLqRecent); - - // getClassicPoolSwapRecentTransaction({ - // account_id: accountId, - // token_one: - // }) }, []); return { swapTransaction: swapRecent, liquidityTransactions: lqRecent }; }; + +export const useDCLPoolTransaction = ({ + pool_id, +}: { + pool_id: string | number; +}) => { + const [swapRecent, setSwapRecent] = useState([]); + + const [lqRecent, setLqRecent] = useState( + [] + ); + + const [limitOrderRecent, setLimitOrderRecent] = useState< + LimitOrderRecentTransaction[] + >([]); + + useEffect(() => { + getDCLPoolSwapRecentTransaction({ + pool_id, + }).then(setSwapRecent); + + getDCLPoolLiquidtyRecentTransaction({ + pool_id, + }).then(setLqRecent); + + getLimitOrderRecentTransaction({ + pool_id, + }).then(setLimitOrderRecent); + }, []); + + return { + swapTransactions: swapRecent, + liquidityTransactions: lqRecent, + limitOrderTransactions: limitOrderRecent, + }; +}; From c0d318109a6b7a95a4ce316d3b6c6ec8af2919c5 Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 5 Jun 2023 22:10:20 +0800 Subject: [PATCH 004/204] fix pool transactions ui --- src/pages/pools/DetailsPage.tsx | 4 ++-- src/pages/poolsV3/PoolDetailV3.tsx | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 14b89d1d8..154d826a4 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1508,7 +1508,7 @@ export function RecentTransactions({ ); return ( - + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Add'} @@ -1598,7 +1598,7 @@ export function RecentTransactions({
-
+
diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 8287f5a60..9c7ecd1ff 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -2006,7 +2006,7 @@ export function RecentTransactions({ ); return ( - + {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && 'Cancelled'} @@ -2128,7 +2128,7 @@ export function RecentTransactions({
-
+
{tab === 'liquidity' && ( From d78b8af482800cbce06d78922ae551edf632d76c Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 6 Jun 2023 00:32:57 +0800 Subject: [PATCH 005/204] add pool top bin apr --- src/components/icon/FarmStamp.tsx | 16 ++++++ src/pages/poolsV3/PoolDetailV3.tsx | 78 +++++++++++++++++++++++------- src/services/indexer.ts | 29 +++++++++++ src/state/pool.ts | 60 +++++++++++++++++++++++ 4 files changed, 166 insertions(+), 17 deletions(-) diff --git a/src/components/icon/FarmStamp.tsx b/src/components/icon/FarmStamp.tsx index cef3f4682..b2459a8c4 100644 --- a/src/components/icon/FarmStamp.tsx +++ b/src/components/icon/FarmStamp.tsx @@ -26,6 +26,22 @@ export const FarmStampNew = ({ multi }: { multi: boolean }) => { ); }; +export const FarmStampNewDCL = ({ multi }: { multi: boolean }) => { + return ( +
+ + + + + {multi && ( + + + + )} +
+ ); +}; + export function FarmDot({ inFarm, className, diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 9c7ecd1ff..c816e6944 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -101,6 +101,7 @@ import { useV3VolumeChart, useV3TvlChart, useDCLPoolTransaction, + useDCLTopBinFee, } from '~state/pool'; import { getV3Pool24VolumeById } from '~services/indexer'; import { @@ -118,7 +119,7 @@ import { } from '../../components/icon/V3'; import _ from 'lodash'; import { PoolRPCView } from '../../services/api'; -import { FarmStampNew } from '../../components/icon/FarmStamp'; +import { FarmStampNew, FarmStampNewDCL } from '../../components/icon/FarmStamp'; import { numberWithCommas } from '~pages/Orderly/utiles'; import { HiOutlineExternalLink } from 'react-icons/hi'; import Big from 'big.js'; @@ -316,6 +317,12 @@ export default function PoolDetailV3() { let result: string = `
${tip}
`; return result; } + + const topBinApr = useDCLTopBinFee({ + pool_id: poolDetail?.pool_id, + number: 100, + }); + if (!poolDetail) return ; const isMobile = isClientMobie(); const tokens = sort_tokens_by_base([ @@ -336,7 +343,7 @@ export default function PoolDetailV3() { ]} />
-
+
-
-
- - {tokens[0]?.symbol}-{tokens[1]?.symbol} - + +
+
+
+ + {tokens[0]?.symbol}-{tokens[1]?.symbol} + +
+
+ + :{' '} + + {poolDetail.fee / 10000}% + + + +
+ + + + :{' '} + + {topBinApr} + + + + {isMobile && sole_seed && ( + 1} /> + )} +
+
+ +
{ @@ -366,7 +410,7 @@ export default function PoolDetailV3() { style={{ background: '#172534', width: '30px', - height: '24px', + height: '30px', }} > {showFullStart ? ( @@ -414,21 +458,21 @@ export default function PoolDetailV3() {
{sole_seed && ( - 1} /> + 1} /> )}
-
- - : {poolDetail.fee / 10000}% - - {sole_seed && ( - 1} /> - )} -
+ +
+
=> { + const paramString = genUrlParams(props); + + return await fetch(config.indexerUrl + `/get-top-bin-fee?${paramString}`, { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + }).then((res) => res.json()); +}; + +export const getDCLAccountFee = async (props: { + pool_id: string | number; + account_id: string | number; +}): Promise => { + const paramString = genUrlParams(props); + + return await fetch(config.indexerUrl + `/get-fee-by-account?${paramString}`, { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + }).then((res) => res.json()); +}; + export interface ProposalHash { proposal_id: string; receipt_id: string; diff --git a/src/state/pool.ts b/src/state/pool.ts index 6f5fe2ec4..449378287 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -56,6 +56,8 @@ import { getDCLPoolLiquidtyRecentTransaction, getLimitOrderRecentTransaction, LimitOrderRecentTransaction, + getDCLAccountFee, + getDCLTopBinFee, } from '../services/indexer'; import { parsePoolView, PoolRPCView } from '../services/api'; import { @@ -1417,3 +1419,61 @@ export const useDCLPoolTransaction = ({ limitOrderTransactions: limitOrderRecent, }; }; + +export const useDCLAccountAPR = ({ + pool_id, + account_id, +}: { + pool_id: string | number; + account_id: string | number; +}) => { + const [accountAPR, setAccountAPR] = useState('-'); + + useEffect(() => { + getDCLAccountFee({ + pool_id, + account_id, + }).then((res) => { + if (!res || ONLY_ZEROS.test(res.total_liquidity)) { + setAccountAPR('-'); + } else { + const apr = new Big(res.total_fee) + .div(res.total_liquidity) + .mul(365) + .toFixed(2); + + setAccountAPR(apr + '%'); + } + }); + }, [account_id, pool_id]); + + return accountAPR; +}; + +export const useDCLTopBinFee = ({ + pool_id, + number, +}: { + pool_id: string | number; + number: string | number; +}) => { + const [topBinApr, setTopBinApr] = useState('-'); + + useEffect(() => { + if (!pool_id) return; + + getDCLTopBinFee({ + pool_id, + number, + }).then((res) => { + if (!res || ONLY_ZEROS.test(res.total_liquidity)) return; + const apr = + new Big(res.total_fee).div(res.total_liquidity).mul(365).toFixed(2) + + '%'; + + setTopBinApr(apr); + }); + }, [pool_id, number]); + + return topBinApr; +}; From 59e8caad5b7bba656195100db0043311ba6ff9de Mon Sep 17 00:00:00 2001 From: Max Date: Tue, 6 Jun 2023 01:21:19 +0800 Subject: [PATCH 006/204] add top bin apr in pool list --- src/pages/pools/LiquidityPage.tsx | 94 ++++++++++++++++++++++++------- src/services/swapV3.ts | 2 + src/state/pool.ts | 8 ++- src/state/swapV3.ts | 21 ++++++- 4 files changed, 103 insertions(+), 22 deletions(-) diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 143428650..1eaebd255 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -30,6 +30,7 @@ import { useAllWatchList, useWatchPools, useV3VolumesPools, + useDCLTopBinFee, } from '../../state/pool'; import Loading from '../../components/layout/Loading'; @@ -1739,6 +1740,12 @@ function PoolRowV2({ const curRowTokens = useTokens([pool.token_x, pool.token_y], tokens); const history = useHistory(); + const displayOfTopBinApr = useDCLTopBinFee({ + pool_id: pool.pool_id, + number: 100, + ownValue: pool.top_bin_apr_display, + }); + if (!curRowTokens) return <>; tokens = sort_tokens_by_base(tokens); function goDetailV2() { @@ -1762,7 +1769,7 @@ function PoolRowV2({ >
{calculateFeePercent(pool.fee / 100)}%
- - {mark && ( -
- / -
- )} -
+ {displayOfTopBinApr} +
+ +
{geth24volume()}
-
+
-
+
-
+
+ { + setV2SortBy('top_bin_apr'); + v2SortBy !== 'top_bin_apr' && setV2Order('desc'); + v2SortBy === 'top_bin_apr' && + setV2Order(v2Order === 'desc' ? 'asc' : 'desc'); + }} + > + + + + { + setV2SortBy('top_bin_apr'); + v2SortBy !== 'top_bin_apr' && setV2Order('desc'); + v2SortBy === 'top_bin_apr' && + setV2Order(v2Order === 'desc' ? 'asc' : 'desc'); + }} + > + {v2SortBy === 'top_bin_apr' ? ( + v2Order === 'desc' ? ( + + ) : ( + + ) + ) : ( + + )} + +
+ +
-
+
{ const [topBinApr, setTopBinApr] = useState('-'); useEffect(() => { if (!pool_id) return; + if (ownValue) { + setTopBinApr(ownValue); + return; + } getDCLTopBinFee({ pool_id, @@ -1473,7 +1479,7 @@ export const useDCLTopBinFee = ({ setTopBinApr(apr); }); - }, [pool_id, number]); + }, [pool_id, number, ownValue]); return topBinApr; }; diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index 4495456ee..94a6db918 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -9,8 +9,10 @@ import { import { WalletContext } from '../utils/wallets-integration'; import { useTokenPriceList } from './token'; import { ftGetTokenMetadata } from '../services/ft-contract'; -import { toReadableNumber } from '../utils/numbers'; +import { ONLY_ZEROS, toReadableNumber } from '../utils/numbers'; import BigNumber from 'bignumber.js'; +import { getDCLTopBinFee } from '../services/indexer'; +import Big from 'big.js'; export const useMyOrders = () => { const [activeOrder, setActiveOrder] = useState(); @@ -77,6 +79,23 @@ export const useAllPoolsV2 = () => { p.tvl = tvlx + tvly; + const topBinFee = await getDCLTopBinFee({ + pool_id: p.pool_id, + number: 100, + }); + + if (!topBinFee || ONLY_ZEROS.test(topBinFee.total_liquidity)) { + p.top_bin_apr = '0'; + p.top_bin_apr_display = '-'; + } else { + const apr = new Big(topBinFee.total_fee) + .div(topBinFee.total_liquidity) + .mul(365) + .toFixed(2); + p.top_bin_apr = apr; + p.top_bin_apr_display = apr + '%'; + } + return p; }) ); From 7ea828c04a8afeac0b6bf95f2351674da5af3df7 Mon Sep 17 00:00:00 2001 From: Max Date: Wed, 7 Jun 2023 22:48:25 +0800 Subject: [PATCH 007/204] add grouped your liquidity --- src/components/icon/Common.tsx | 4 +- src/components/icon/FarmBoost.tsx | 10 +- src/components/icon/Pool.tsx | 2 +- src/components/icon/V3.tsx | 48 + src/components/layout/Guider.tsx | 10 +- src/components/pool/PoolTabV3.tsx | 13 +- src/components/pool/YourLiquidityV2.tsx | 1745 ++++++++++++++--- src/locales/en_US.ts | 2 +- src/locales/es.ts | 2 +- src/locales/ja.ts | 2 +- src/locales/ko.ts | 2 +- src/locales/ru.ts | 2 +- src/locales/uk_UA.ts | 2 +- src/locales/vi.ts | 2 +- src/pages/Orderly/components/Common/Icons.tsx | 6 +- src/pages/poolsV3/PoolDetailV3.tsx | 393 +++- src/pages/poolsV3/YourLiquidityPageV3.tsx | 53 + src/services/config.ts | 4 +- src/state/pool.ts | 2 + tailwind.config.js | 27 +- 20 files changed, 1966 insertions(+), 365 deletions(-) diff --git a/src/components/icon/Common.tsx b/src/components/icon/Common.tsx index c563f3a07..2e4897c40 100644 --- a/src/components/icon/Common.tsx +++ b/src/components/icon/Common.tsx @@ -1972,7 +1972,7 @@ function SnowBar(props: any) { width="501" height="46" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > { stroke="#00C6A2" stroke-linecap="round" stroke-linejoin="round" - stroke-dasharray="1 2" + strokeDasharray="1 2" /> ); @@ -4004,7 +4004,7 @@ export const CrossIconLittle = (props: any) => { stroke="#00C6A2" stroke-linecap="round" stroke-linejoin="round" - stroke-dasharray="1 2" + strokeDasharray="1 2" /> { stroke="#00C6A2" stroke-linecap="round" stroke-linejoin="round" - stroke-dasharray="1 2" + strokeDasharray="1 2" /> { stroke="#00C6A2" stroke-linecap="round" stroke-linejoin="round" - stroke-dasharray="1 2" + strokeDasharray="1 2" /> { stroke="#00C6A2" stroke-linecap="round" stroke-linejoin="round" - stroke-dasharray="1 2" + strokeDasharray="1 2" /> ); diff --git a/src/components/icon/Pool.tsx b/src/components/icon/Pool.tsx index 11ec3c264..9b4c13150 100644 --- a/src/components/icon/Pool.tsx +++ b/src/components/icon/Pool.tsx @@ -113,7 +113,7 @@ export const PoolTabBannerMask = () => { width="1024" height="456" filterUnits="userSpaceOnUse" - color-interpolation-filters="sRGB" + colorInterpolationFilters="sRGB" > { ); }; +export const FarmBoardInDetailDCLPool = (props: any) => { + return ( + + + + + + + + + + + + + + ); +}; + export const HintIcon = (props: any) => { return ( { + if (!prev[cur.pool_id]) { + prev[cur.pool_id] = []; + } + prev[cur.pool_id].push(cur); + return prev; + }, {}); + + const countV2 = listLiquiditiesLoading + ? 0 + : Object.keys(groupedListByPoolId).length; const { finalStakeList, stakeList, v2StakeList, stakeListDone } = useStakeListByAccountId(); diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 65809e262..e8e538145 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -13,6 +13,7 @@ import { get_liquidity, PoolInfo, remove_liquidity, + claim_all_liquidity_fee, } from '../../services/swapV3'; import { ColorsBox, TipIon } from '~components/icon/V3'; import { @@ -25,6 +26,7 @@ import { toPrecision, toReadableNumber, formatWithCommas, + scientificNotationToString, } from '~utils/numbers'; import { useTokens } from '../../state/token'; import { @@ -61,6 +63,9 @@ import { UpDownButton } from '../portfolio/Tool'; import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; import { PortfolioData } from '../../pages/Portfolio'; import { isMobile } from '~utils/device'; +import { useDCLAccountAPR } from '~state/pool'; +import Big from 'big.js'; +import { useWalletSelector } from '~context/WalletSelectorContext'; const is_mobile = isMobile(); const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const LiquidityContext = createContext(null); @@ -77,6 +82,7 @@ export function YourLiquidityV2(props: any) { setLiquidityLoadingDone, setLiquidityQuantity, styleType, + liquidityLoadingDone, } = props; const [all_seeds, set_all_seeds] = useState([]); const [tokenPriceList, setTokenPriceList] = useState>({}); @@ -85,6 +91,9 @@ export function YourLiquidityV2(props: any) { const [liquidities_list, set_liquidities_list] = useState< UserLiquidityInfo[] >([]); + + const [groupYourLiquidity, setGroupYourLiquidity] = useState(); + const [liquidities_details_list, set_iquidities_details_list] = useState< UserLiquidityInfo[] >([]); @@ -168,7 +177,17 @@ export function YourLiquidityV2(props: any) { set_dcl_liquidities_details_list_done(true); } setLiquidityLoadingDone && setLiquidityLoadingDone(true); - setLiquidityQuantity && setLiquidityQuantity(list.length); + + const groupedList = list.reduce((pre, cur) => { + const { pool_id } = cur; + const pool = pre[pool_id] || []; + pool.push(cur); + pre[pool_id] = pool; + return pre; + }, {}); + + setLiquidityQuantity && + setLiquidityQuantity(Object.keys(groupedList).length); } async function get_all_pools_detail() { const pool_ids = new Set(); @@ -256,23 +275,115 @@ export function YourLiquidityV2(props: any) { setYourLpValueV2 && setYourLpValueV2(total_value.toFixed()); } } + + useEffect(() => { + if ( + !liquidities_list || + !all_pools_map || + !liquidities_tokens_metas || + !tokenPriceList || + !dcl_liquidities_details_map || + Object.keys(dcl_liquidities_details_map).length === 0 + ) { + return; + } + + Promise.all( + liquidities_list.map((liquidity) => + getYourLiquidityData({ + liquidity, + all_seeds, + styleType, + tokenPriceList, + poolDetail: all_pools_map?.[liquidity.pool_id], + liquidityDetail: dcl_liquidities_details_map?.[liquidity.lpt_id], + liquidities_tokens_metas, + }) + ) + ).then((yourLiquidityList) => { + const groupedLiquidity = yourLiquidityList.reduce((acc, cur) => { + const { pool_id } = cur; + const [token_x, token_y] = pool_id.split('|'); + const poolDetail = all_pools_map[pool_id]; + const tokensMeta = [ + liquidities_tokens_metas[token_x], + liquidities_tokens_metas[token_y], + ]; + + if (acc[pool_id]) { + acc[pool_id].push(cur); + + return acc; + } else { + return { + ...acc, + [pool_id]: [ + { + ...cur, + poolDetail, + tokensMeta, + }, + ], + }; + } + }, {} as Record); + + setGroupYourLiquidity(groupedLiquidity); + }); + }, [ + liquidities_list, + all_seeds, + all_pools_map, + liquidities_tokens_metas, + tokenPriceList, + dcl_liquidities_details_map, + Object.keys(dcl_liquidities_details_map).length === 0, + ]); + return (
- {liquidities_list.map((liquidity: UserLiquidityInfo, index: number) => { - return ( -
- + {liquidityLoadingDone && ( +
+
+
- ); - })} + +
+ +
+ +
+ +
+
+ +
+
+ )} + + {groupYourLiquidity && + Object.entries(groupYourLiquidity).map( + ([id, liquidity]: any, index: number) => { + return ( + + ); + } + )}
); } @@ -344,15 +455,18 @@ function UserLiquidityLine({ }); set_related_seed_info(info); }, [liquidity, all_seeds, is_in_farming, tokenPriceList, related_farms]); + async function get_pool_related_farms() { const is_in_farming = liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; + if (is_in_farming) { const id = liquidity.mft_id.slice(1); const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + id; const farmList = await list_seed_farms(seed_id); set_related_farms(farmList); } + set_is_in_farming(is_in_farming); } async function judge_is_in_range() { @@ -385,6 +499,7 @@ function UserLiquidityLine({ function getLpt_id() { return lpt_id.split('#')[1]; } + function get_your_liquidity(current_point: number) { const [tokenX, tokenY] = tokenMetadata_x_y; const priceX = tokenPriceList[tokenX.id]?.price || 0; @@ -657,210 +772,1371 @@ function UserLiquidityLine({ ); } -function UserLiquidityLineStyle1() { - const { - hover, - setHover, - getLpt_id, - goYourLiquidityDetailPage, - tokenMetadata_x_y, - fee, - Liquidity_icon, - liquidity_link, +// a function just to return your liquidity data +async function getYourLiquidityData({ + liquidity, + all_seeds, + styleType, + tokenPriceList, + poolDetail, + liquidities_tokens_metas, + liquidityDetail, +}: { + liquidity: UserLiquidityInfo; + all_seeds: Seed[]; + styleType: string; + tokenPriceList: Record; + poolDetail: PoolInfo; + liquidities_tokens_metas: Record; + liquidityDetail: UserLiquidityInfo; +}) { + const { lpt_id, pool_id, left_point, right_point, amount: L } = liquidity; + const [token_x, token_y, fee] = pool_id.split('|'); + + const tokenMetadata_x_y = liquidities_tokens_metas + ? [liquidities_tokens_metas[token_x], liquidities_tokens_metas[token_y]] + : null; + + let rate_need_to_reverse_display: boolean; + + if (tokenMetadata_x_y) { + const [tokenX] = tokenMetadata_x_y; + if (TOKEN_LIST_FOR_RATE.indexOf(tokenX.symbol) > -1) + rate_need_to_reverse_display = true; + else rate_need_to_reverse_display = false; + } + + let your_liquidity: any; + + if (tokenMetadata_x_y && poolDetail && tokenPriceList) { + const { current_point } = poolDetail; + your_liquidity = get_your_liquidity(current_point, left_point, right_point); + } + + const is_in_farming = + liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; + console.log('liquidity: ', liquidity); + + let related_farms: any; + + if (is_in_farming) { + const id = liquidity.mft_id.slice(1); + const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + id; + related_farms = await list_seed_farms(seed_id); + } + + const related_seed_info = get_detail_the_liquidity_refer_to_seed({ + liquidity, + all_seeds, is_in_farming, - liquidity_inRange, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, - liquidity_your_apr, - liquidity_staked_farm_status, - your_liquidity, - setShowAddBox, - setShowRemoveBox, - getTokenFeeAmount, - canClaim, - go_farm, - mobile_ReferenceToken, - claimRewards, - claimLoading, - showRemoveBox, - poolDetail, + related_farms, tokenPriceList, - liquidityDetail, - showAddBox, - } = useContext(LiquidityContext); + }); - const history = useHistory(); + function get_your_liquidity_in_farm_range() { + if (!related_seed_info.targetSeed || !related_seed_info.targetSeed.seed_id) + return '0'; - function goDetailV2() { - const url_pool_id = get_pool_name(poolDetail.pool_id); - history.push(`/poolV2/${url_pool_id}`); + const seed_info = related_seed_info.targetSeed.seed_id.split('&'); + const farmRangeLeft = Number(seed_info[2]); + const farmRangeRight = Number(seed_info[3]); + const { current_point } = poolDetail; + + if (left_point > farmRangeRight || right_point < farmRangeLeft) return '0'; + + return get_your_liquidity( + current_point, + left_point >= farmRangeLeft ? left_point : farmRangeLeft, + right_point <= farmRangeRight ? right_point : farmRangeRight + ); } - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - return ( -
setHover(true)} - onMouseLeave={() => setHover(false)} - > - {/* for PC */} -
-
-
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - -
- - - - {+fee / 10000}% -
- {Liquidity_icon ? ( -
{ - e.stopPropagation(); - if (liquidity_link) { - openUrl(liquidity_link); - } - }} - className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ - liquidity_link ? 'cursor-pointer' : '' - } ${ - is_in_farming || liquidity_inRange ? '' : 'opacity-40' - }`} - > - - - {' '} - -
- ) : null} -
-
- - - - - {getRate(rate_need_to_reverse_display ? 'right' : 'left')} - - - - - - - {getRate(rate_need_to_reverse_display ? 'left' : 'right')} - - - {getRateMapTokens()} - -
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
-
-
- {liquidity_your_apr && - (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( -
- - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )}{' '} - {liquidity_your_apr} - -
{ - openUrl(liquidity_link); - }} - > - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - + function get_your_liquidity( + current_point: number, + left_point: number, + right_point: number + ) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + let total_price; + // in range + if (current_point >= left_point && right_point > current_point) { + let tokenYAmount = getY(left_point, current_point, L, tokenY) || 0; + let tokenXAmount = getX(current_point + 1, right_point, L, tokenX) || 0; + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + tokenXAmount = new BigNumber(tokenXAmount).plus(amountx).toFixed(); + tokenYAmount = new BigNumber(tokenYAmount).plus(amounty).toFixed(); + const tokenYTotalPrice = new BigNumber(tokenYAmount).multipliedBy(priceY); + const tokenXTotalPrice = new BigNumber(tokenXAmount).multipliedBy(priceX); + total_price = tokenYTotalPrice.plus(tokenXTotalPrice).toFixed(); + } + // only y token + if (current_point >= right_point) { + const tokenYAmount = getY(left_point, right_point, L, tokenY); + const tokenYTotalPrice = new BigNumber(tokenYAmount).multipliedBy(priceY); + total_price = tokenYTotalPrice.toFixed(); + } + // only x token + if (left_point > current_point) { + const tokenXAmount = getX(left_point, right_point, L, tokenX); + const tokenXTotalPrice = new BigNumber(tokenXAmount).multipliedBy(priceX); + total_price = tokenXTotalPrice.toFixed(); + } + + return total_price; + } + function getY( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + + function getTokenFeeAmount(p: string) { + if (liquidityDetail && tokenMetadata_x_y && tokenPriceList) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { unclaimed_fee_x, unclaimed_fee_y } = liquidityDetail; + const fee_x_amount = toReadableNumber( + tokenX.decimals, + unclaimed_fee_x || '0' + ); + const fee_y_amount = toReadableNumber( + tokenY.decimals, + unclaimed_fee_y || '0' + ); + if (p == 'l') { + return fee_x_amount; + } else if (p == 'r') { + return fee_y_amount; + } else if (p == 'p') { + const tokenxSinglePrice = tokenPriceList[tokenX.id]?.price || '0'; + const tokenySinglePrice = tokenPriceList[tokenY.id]?.price || '0'; + const priceX = new BigNumber(fee_x_amount).multipliedBy( + tokenxSinglePrice + ); + const priceY = new BigNumber(fee_y_amount).multipliedBy( + tokenySinglePrice + ); + const totalPrice = priceX.plus(priceY); + + return scientificNotationToString(totalPrice.toString()); + } + } + } + + const tokenFeeLeft = getTokenFeeAmount('l'); + + const tokenFeeRight = getTokenFeeAmount('r'); + + function getRate(direction: string) { + let value = ''; + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + if (direction == 'left') { + value = getPriceByPoint(left_point, decimalRate); + } else if (direction == 'right') { + value = getPriceByPoint(right_point, decimalRate); + } + if (rate_need_to_reverse_display && +value !== 0) { + value = new BigNumber(1).dividedBy(value).toFixed(); + } + } + return value; + } + + function go_farm() { + const [fixRange, pool_id, left_point, right_point] = + liquidity.mft_id.split('&'); + const link_params = `${get_pool_name( + pool_id + )}[${left_point}-${right_point}]`; + const actives = related_farms.filter((farm: FarmBoost) => { + return farm.status != 'Ended'; + }); + let url; + if (related_farms.length > 0 && actives.length == 0) { + url = `/v2farms/${link_params}-e`; + } else { + url = `/v2farms/${link_params}-r`; + } + openUrl(url); + } + + const rangeMin = getRate(rate_need_to_reverse_display ? 'right' : 'left'); + + const rangeMax = getRate(rate_need_to_reverse_display ? 'left' : 'right'); + + function getRateMapTokens() { + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + if (rate_need_to_reverse_display) { + return `${tokenX.symbol}/${tokenY.symbol}`; + } else { + return `${tokenY.symbol}/${tokenX.symbol}`; + } + } + } + + const ratedMapTokens = getRateMapTokens(); + + const your_liquidity_farm = get_your_liquidity_in_farm_range(); + + return { + fee, + your_liquidity, + your_liquidity_farm, + related_seed_info, + go_farm, + tokenFeeLeft: tokenFeeLeft || '0', + tokenFeeRight: tokenFeeRight || '0', + rate_need_to_reverse_display, + related_farms, + is_in_farming, + liquidity, + pool_id, + rangeMax, + rangeMin, + ratedMapTokens, + tokenMetadata_x_y, + liquidityDetail, + }; +} + +function UserLiquidityLineStyle1() { + const { + hover, + setHover, + getLpt_id, + goYourLiquidityDetailPage, + tokenMetadata_x_y, + fee, + Liquidity_icon, + liquidity_link, + is_in_farming, + liquidity_inRange, + getRate, + rate_need_to_reverse_display, + getRateMapTokens, + isInrange, + liquidity_your_apr, + liquidity_staked_farm_status, + your_liquidity, + setShowAddBox, + setShowRemoveBox, + getTokenFeeAmount, + canClaim, + go_farm, + mobile_ReferenceToken, + claimRewards, + claimLoading, + showRemoveBox, + poolDetail, + tokenPriceList, + liquidityDetail, + showAddBox, + } = useContext(LiquidityContext); + + const history = useHistory(); + + function goDetailV2() { + const url_pool_id = get_pool_name(poolDetail.pool_id); + history.push(`/poolV2/${url_pool_id}`); + } + + const tokens = sort_tokens_by_base(tokenMetadata_x_y); + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + > + {/* for PC */} +
+
+
+
+
+
+ + +
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + +
+ + + + {+fee / 10000}% +
+ {Liquidity_icon ? ( +
{ + e.stopPropagation(); + if (liquidity_link) { + openUrl(liquidity_link); + } + }} + className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ + liquidity_link ? 'cursor-pointer' : '' + } ${ + is_in_farming || liquidity_inRange ? '' : 'opacity-40' + }`} + > + + + {' '} + +
+ ) : null} +
+
+ + + + + {getRate(rate_need_to_reverse_display ? 'right' : 'left')} + + + + + + + {getRate(rate_need_to_reverse_display ? 'left' : 'right')} + + + {getRateMapTokens()} + +
+ + + {isInrange ? ( + + ) : ( + + )} + +
+
+
+
+
+ {liquidity_your_apr && + (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( +
+ + + {liquidity_staked_farm_status == 'end' ? ( + + ) : ( + + )}{' '} + {liquidity_your_apr} + +
- ) : null} + ) : null} + +
+
+ + + + + ${your_liquidity || '-'} + + { + e.stopPropagation(); + setShowAddBox(true); + }} + color="#fff" + minWidth="5rem" + disabled={is_in_farming} + borderRadius="8px" + btnClassName={is_in_farming ? 'cursor-not-allowed' : ''} + className={`px-3 h-8 text-center text-sm text-white gotham_bold focus:outline-none mr-2.5 ${ + is_in_farming ? 'opacity-40 ' : '' + }`} + > + + + { + e.stopPropagation(); + setShowRemoveBox(true); + }} + rounded="rounded-lg" + disabled={is_in_farming} + px="px-0" + py="py-1" + style={{ minWidth: '5rem' }} + className={`flex-grow gotham_bold text-sm text-greenColor h-8 ${ + is_in_farming ? 'opacity-40' : '' + }`} + > + + + {is_in_farming ? ( +
+ +
{ + e.stopPropagation(); + go_farm(); + }} + > + + {liquidity_staked_farm_status == 'end' ? ( + + ) : ( + + )} + + +
+
+ ) : null} +
+
+ + + + + + {getTokenFeeAmount('l') || '-'} + + + + {getTokenFeeAmount('r') || '-'} + +
+ } + /> +
+
+
+
+
+
+ {/* for Mobile */} +
+
+
+
+ + + NFT ID #{getLpt_id()} + +
+
+
+
+ + +
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + + {Liquidity_icon ? ( +
{ + e.stopPropagation(); + if (liquidity_link) { + openUrl(liquidity_link); + } + }} + className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ + is_in_farming || liquidity_inRange ? '' : 'opacity-40' + }`} + > + + + {' '} + +
+ ) : null} +
+
+ + + {isInrange ? ( + + ) : ( + + )} + +
+
+
+
+
+ + + + {+fee / 10000}% +
+
+ + (1{' '} + {mobile_ReferenceToken('left')}) + + + {getRate(rate_need_to_reverse_display ? 'right' : 'left')}  + {mobile_ReferenceToken('right')} + +
+
+ + (1{' '} + {mobile_ReferenceToken('left')}) + + + {getRate(rate_need_to_reverse_display ? 'left' : 'right')}  + {mobile_ReferenceToken('right')} + +
+
+ + + +
+ + + {getTokenFeeAmount('l') || '-'} + + + + {getTokenFeeAmount('r') || '-'} + +
+ } + /> +
+
+
+
+
+
+
+ + + + ${your_liquidity || '-'} +
+
+ { + e.stopPropagation(); + setShowAddBox(true); + }} + disabled={is_in_farming ? true : false} + color="#fff" + className={`w-1 flex-grow h-8 text-center text-sm text-white focus:outline-none mr-3 ${ + is_in_farming ? 'opacity-40' : '' + }`} + > + + + { + e.stopPropagation(); + setShowRemoveBox(true); + }} + disabled={is_in_farming ? true : false} + rounded="rounded-md" + px="px-0" + py="py-1" + className={`w-1 flex-grow text-sm text-greenColor h-8 ${ + is_in_farming ? 'opacity-40' : '' + }`} + > + + +
+ {is_in_farming ? ( +
+ + { + e.stopPropagation(); + go_farm(); + }} + > + {liquidity_staked_farm_status == 'end' ? ( + + ) : ( + + )} + + +
+ ) : null} + {liquidity_your_apr && + (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( +
+
+
+ +
+ +
{ + openUrl(liquidity_link); + }} + > + + + + +
+
+
+
+ + {liquidity_your_apr} +
+
+
+
+ + + + +
+
+
{ + openUrl(liquidity_link); + }} + > + + + + +
+ , + + {liquidity_your_apr} +
+
+
+ ) : null} +
+
+ {showRemoveBox ? ( + { + setShowRemoveBox(false); + }} + tokenMetadata_x_y={tokenMetadata_x_y} + poolDetail={poolDetail} + tokenPriceList={tokenPriceList} + userLiquidity={liquidityDetail} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > + ) : null} + { + setShowAddBox(false); + }} + tokenMetadata_x_y={tokenMetadata_x_y} + poolDetail={poolDetail} + tokenPriceList={tokenPriceList} + userLiquidity={liquidityDetail} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > +
+ ); +} + +export function findRangeIntersection(arr: number[][]) { + if (!Array.isArray(arr) || arr.length === 0) { + return []; + } + + arr.sort((a, b) => a[0] - b[0]); + + let intersection = [arr[0]]; + + for (let i = 1; i < arr.length; i++) { + const current = arr[i]; + const [currentStart, currentEnd] = current; + const [prevStart, prevEnd] = intersection[intersection.length - 1]; + + if (currentStart > prevEnd) { + intersection.push(current); + } else { + const start = Math.min(currentStart, prevStart); + const end = Math.max(currentEnd, prevEnd); + intersection[intersection.length - 1] = [start, end]; + } + } + + return intersection; +} + +function UserLiquidityLineStyleGroup1({ + groupYourLiquidityList, +}: { + groupYourLiquidityList: any[]; +}) { + // const { + // goYourLiquidityDetailPage, + // Liquidity_icon, + // liquidity_link, + + // // liquidity_staked_farm_status, + // // setShowAddBox, + // // setShowRemoveBox, + // getTokenFeeAmount, + // // canClaim, + // // go_farm, + // mobile_ReferenceToken, + // // claimRewards, + // // claimLoading, + // showRemoveBox, + // poolDetail, + // tokenPriceList, + // liquidityDetail, + // showAddBox, + // } = useContext(LiquidityContext); + + const [hover, setHover] = useState(false); + + const publicData = groupYourLiquidityList[0]; + + const { + tokenMetadata_x_y, + fee, + pool_id, + related_seed_info, + ratedMapTokens, + related_farms, + poolDetail, + go_farm, + } = publicData; + + const history = useHistory(); + + const groupList = () => { + const your_liquidity = groupYourLiquidityList.reduce((prev, cur) => { + return new Big(prev || '0').plus(new Big(cur.your_liquidity || '0')); + }, new Big(0)); + + const tokenFeeLeft = groupYourLiquidityList.reduce((prev, cur) => { + return new Big(prev || '0').plus(new Big(cur.tokenFeeLeft || '0')); + }, new Big(0)); + + const tokenFeeRight = groupYourLiquidityList.reduce((prev, cur) => { + return new Big(prev || '0').plus(new Big(cur.tokenFeeRight || '0')); + }, new Big(0)); + + const rangeList = groupYourLiquidityList.map((g) => [ + Number(g.rangeMin), + Number(g.rangeMax), + ]); + + const pointRangeList = groupYourLiquidityList.map((g) => [ + g.liquidity.left_point, + g.liquidity.right_point, + ]); + + const is_in_farming = groupYourLiquidityList.reduce( + (pre, cur) => !!pre || !!cur.is_in_farming, + false + ); + + const intersectionRangeList = findRangeIntersection(rangeList); + + const intersectionPointRangeList = findRangeIntersection(pointRangeList); + + const current_point = poolDetail.current_point; + + const isInRange = intersectionPointRangeList.some( + (range) => current_point >= range[0] && current_point <= range[1] + ); + + const canClaim = groupYourLiquidityList.reduce((pre, cur) => { + const liquidityDetail = cur.liquidityDetail; + const { unclaimed_fee_x, unclaimed_fee_y } = liquidityDetail; + + return !!pre && !!(+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0); + }, true); + + // on farm not on myself + const farmApr: Big = !publicData.related_seed_info.your_apr + ? '' + : groupYourLiquidityList.reduce((prev: any, cur: any) => { + return new Big(prev || '0').plus( + new Big(cur.related_seed_info.your_apr_raw || '0') + ); + }, new Big(0)); + + let farmRangeLeft; + let farmRangeRight; + + if (related_seed_info.targetSeed) { + const seed_info = related_seed_info.targetSeed.seed_id.split('&'); + farmRangeLeft = Number(seed_info[2]); + farmRangeRight = Number(seed_info[3]); + } + + const totalLiquidityAmount = groupYourLiquidityList.reduce( + (pre, cur) => { + const { mft_id } = cur.liquidity; + + const { your_liquidity_farm } = cur; + + return { + total: pre.total.plus(new Big(your_liquidity_farm)), + in_farm: mft_id + ? pre.in_farm.plus(new Big(your_liquidity_farm)) + : pre.in_farm, + }; + }, + { + total: new Big(0), + in_farm: new Big(0), + } + ); + + return { + ...publicData, + your_liquidity: + '$' + + (your_liquidity.toNumber() < 0.01 + ? '< 0.01' + : your_liquidity.toFixed(2, 0)), + tokenFeeLeft: + tokenFeeLeft.toNumber() < 0.01 ? '< 0.01' : tokenFeeLeft.toFixed(2, 0), + tokenFeeRight: + tokenFeeRight.toNumber() < 0.01 + ? '< 0.01' + : tokenFeeRight.toFixed(2, 0), + intersectionRangeList: intersectionRangeList.map((range) => [ + new Big(range[0]).toFixed(), + new Big(range[1]).toFixed(), + ]), + is_in_farming, + intersectionPointRangeList, + isInRange, + canClaim, + farmApr: !farmApr + ? '' + : farmApr.eq(new Big(0)) + ? '0%' + : farmApr.lt(0.01) + ? '<0.01%' + : farmApr.toFixed(2, 0) + '%', + in_farm_percent: totalLiquidityAmount.total.eq(0) + ? '0%' + : toPrecision( + scientificNotationToString( + totalLiquidityAmount.in_farm + .div(totalLiquidityAmount.total) + .times(100) + .toString() + ), + 2 + ) + '%', + }; + }; + + const groupedData = groupList(); + + const { + your_liquidity, + tokenFeeLeft, + tokenFeeRight, + is_in_farming, + intersectionRangeList, + isInRange, + canClaim, + farmApr, + in_farm_percent, + } = groupedData; + + function goDetailV2() { + const url_pool_id = get_pool_name(pool_id); + history.push(`/poolV2/${url_pool_id}`); + } + + const tokens = sort_tokens_by_base(tokenMetadata_x_y); + + const { accountId } = useWalletSelector(); + + const poolApr = useDCLAccountAPR({ + pool_id, + account_id: accountId, + }); + + const [claim_loading, set_claim_loading] = useState(false); + + function claimRewards() { + if (!canClaim) return; + + set_claim_loading(true); + const lpt_ids: string[] = []; + groupYourLiquidityList + .map((g) => g.liquidityDetail) + .forEach((liquidity: UserLiquidityInfo) => { + const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; + if (+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0) { + lpt_ids.push(liquidity.lpt_id); + } + }); + claim_all_liquidity_fee({ + token_x: tokenMetadata_x_y[0], + token_y: tokenMetadata_x_y[1], + lpt_ids, + }); + } + + function getTotalAprForSeed() { + const farms = related_seed_info.targetSeed.farmList; + let apr = 0; + const allPendingFarms = isPending(related_seed_info.targetSeed); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { + apr = +new BigNumber(item.apr).plus(apr).toFixed(); + } + }); + if (apr == 0) { + return '-'; + } else { + apr = +new BigNumber(apr).multipliedBy(100).toFixed(); + return toPrecision(apr.toString(), 2) + '%'; + } + } + + function isPending(seed: Seed) { + let pending: boolean = true; + const farms = seed.farmList; + for (let i = 0; i < farms.length; i++) { + if (farms[i].status != 'Created' && farms[i].status != 'Pending') { + pending = false; + break; + } + } + return pending; + } + + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + > + {/* for PC */} +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + goDetailV2(); + }} + > + {/* 1/3 */} +
+
+ + +
+
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + + +
+
+ DCL +
+
+ + + + {+fee / 10000}% +
+
+
+
-
-
- - + {/* 2/3 */} + +
+
+ {intersectionRangeList.map((range: string[], i: number) => { + return ( +
+ + {displayNumberToAppropriateDecimals(range[0])} + + - + + + {displayNumberToAppropriateDecimals(range[1])} + + {intersectionRangeList.length > 1 && + i < intersectionRangeList.length - 1 && ( + , + )} +
+ ); + })} + + {ratedMapTokens} - - ${your_liquidity || '-'} +
+
+ + {isInRange ? ( + + ) : ( + + )} + +
+
+ +
+ {poolApr} + + {related_seed_info.your_apr && ( + + + + )} + + {related_seed_info.your_apr + ? !is_in_farming || related_seed_info.status == 'end' + ? '0%' + : getTotalAprForSeed() + : '-'} + +
+ +
+ {your_liquidity} + + {related_seed_info.your_apr ? ( +
+ {in_farm_percent} + + + + + { + e.preventDefault(); + e.stopPropagation(); + go_farm(); + }} + > + + +
+ ) : ( + - + )} +
+
+ {farmApr && (!is_in_farming || related_seed_info.status == 'end') ? ( +
+ + + {related_seed_info.status == 'end' ? ( + + ) : ( + + )}{' '} + {getTotalAprForSeed()} + +
{ + openUrl(related_seed_info.link); + }} + > + + {related_seed_info.status == 'end' ? ( + + ) : ( + + )} + + +
+
+ ) : null} +
+
+ {/* unclaimed fees */} +
+ + + + + + {tokenFeeLeft || '-'} + + + + {tokenFeeRight || '-'} +
+ +
+
+ } + /> +
{ e.stopPropagation(); - setShowAddBox(true); + // setShowAddBox(true); }} color="#fff" minWidth="5rem" disabled={is_in_farming} borderRadius="8px" btnClassName={is_in_farming ? 'cursor-not-allowed' : ''} - className={`px-3 h-8 text-center text-sm text-white gotham_bold focus:outline-none mr-2.5 ${ + className={`px-3 w-24 h-8 text-center text-sm text-white focus:outline-none mr-2.5 ${ is_in_farming ? 'opacity-40 ' : '' }`} > @@ -869,20 +2145,20 @@ function UserLiquidityLineStyle1() { { e.stopPropagation(); - setShowRemoveBox(true); + // setShowRemoveBox(true); }} rounded="rounded-lg" disabled={is_in_farming} px="px-0" py="py-1" style={{ minWidth: '5rem' }} - className={`flex-grow gotham_bold text-sm text-greenColor h-8 ${ + className={`flex-grow w-24 text-sm text-greenColor h-8 ${ is_in_farming ? 'opacity-40' : '' }`} > - {is_in_farming ? ( + {/* {is_in_farming ? (
- ) : null} -
-
- - - - - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - -
- } - /> -
+ ) : null} */}
{/* for Mobile */} -
+ {/*
-
- - - NFT ID #{getLpt_id()} - -
@@ -968,24 +2206,6 @@ function UserLiquidityLineStyle1() { {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - {Liquidity_icon ? ( -
{ - e.stopPropagation(); - if (liquidity_link) { - openUrl(liquidity_link); - } - }} - className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ - is_in_farming || liquidity_inRange ? '' : 'opacity-40' - }`} - > - - - {' '} - -
- ) : null}
) : null}
-
- {showRemoveBox ? ( +
*/} + {/* {showRemoveBox ? ( { @@ -1234,10 +2454,11 @@ function UserLiquidityLineStyle1() { transform: 'translate(-50%, -50%)', }, }} - > + > */}
); } + function UserLiquidityLineStyle2() { const { getLpt_id, diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index 484c1fa0c..d72065072 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1189,7 +1189,7 @@ const en_US = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/es.ts b/src/locales/es.ts index 84a500567..0c413f3c9 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -1070,7 +1070,7 @@ const es = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/ja.ts b/src/locales/ja.ts index cebca6fdc..e717f6d38 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -1038,7 +1038,7 @@ const ja = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 2cd834386..0f72fe333 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -1030,7 +1030,7 @@ const ko = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 5653f53a6..54ff78a13 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -1042,7 +1042,7 @@ const ru = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/uk_UA.ts b/src/locales/uk_UA.ts index 7bb234494..c8adc79dd 100644 --- a/src/locales/uk_UA.ts +++ b/src/locales/uk_UA.ts @@ -1013,7 +1013,7 @@ const uk_UA = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/locales/vi.ts b/src/locales/vi.ts index 2e8528a26..bc2e66131 100644 --- a/src/locales/vi.ts +++ b/src/locales/vi.ts @@ -1042,7 +1042,7 @@ const vi = { your: 'Your', max_apr: 'Max.APR', faming_positions: 'Farming Positions', - you_can_earn_tip: 'You can earn rewards by farming, est. APR is', + you_can_earn_tip: 'Farm available, farm APR up to', you_can_earn_current_tip: 'Your current staked farm ended, and new farm is coming, est. APR is', go_new_farm: 'Go New Farm', diff --git a/src/pages/Orderly/components/Common/Icons.tsx b/src/pages/Orderly/components/Common/Icons.tsx index b1236ef94..409ff8fa4 100644 --- a/src/pages/Orderly/components/Common/Icons.tsx +++ b/src/pages/Orderly/components/Common/Icons.tsx @@ -35,7 +35,7 @@ export function OrderStateOutline() { r="6" stroke="#62C440" stroke-width="1.4" - stroke-dasharray="1.4 1.4" + strokeDasharray="1.4 1.4" /> ); @@ -1457,7 +1457,7 @@ export const GoToOrderbookTip = (props: any) => { x2="377" y2="137.5" stroke="#00D6AF" - stroke-dasharray="2 2" + strokeDasharray="2 2" /> { x2="12.5" y2="49" stroke="#00D6AF" - stroke-dasharray="2 2" + strokeDasharray="2 2" /> ); diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index c816e6944..7b16c1324 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -102,6 +102,7 @@ import { useV3TvlChart, useDCLPoolTransaction, useDCLTopBinFee, + useDCLAccountAPR, } from '~state/pool'; import { getV3Pool24VolumeById } from '~services/indexer'; import { @@ -116,6 +117,7 @@ import { FarmBoardInDetailPool, Fire, JumpLinkIcon, + FarmBoardInDetailDCLPool, } from '../../components/icon/V3'; import _ from 'lodash'; import { PoolRPCView } from '../../services/api'; @@ -123,6 +125,7 @@ import { FarmStampNew, FarmStampNewDCL } from '../../components/icon/FarmStamp'; import { numberWithCommas } from '~pages/Orderly/utiles'; import { HiOutlineExternalLink } from 'react-icons/hi'; import Big from 'big.js'; +import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); export default function PoolDetailV3() { @@ -331,7 +334,7 @@ export default function PoolDetailV3() { ]); return ( <> -
+
@@ -491,7 +494,7 @@ export default function PoolDetailV3() {
{!isSignedIn || @@ -615,9 +618,13 @@ function YourLiquidityBox(props: { matched_seeds: Seed[]; }) { const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; + console.log('liquidities: ', liquidities); const [user_liquidities_detail, set_user_liquidities_detail] = useState< UserLiquidityDetail[] >([]); + + console.log('user_liquidities_detail: ', user_liquidities_detail); + const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); const [operationType, setOperationType] = useState('add'); const { token_x_metadata, token_y_metadata } = poolDetail; @@ -661,6 +668,9 @@ function YourLiquidityBox(props: { total_fees_price: total_fees_price.toString(), amount_x, amount_y, + unclaimed_fee_x_amount, + unclaimed_fee_y_amount, + hashId: lpt_id.split('#')[1], l_price, r_price, @@ -813,6 +823,60 @@ function YourLiquidityBox(props: { total_y: display_total_y, }; } + + function getTotalFee() { + let total_x = 0; + let total_y = 0; + + let total_price = 0; + + user_liquidities_detail.forEach((liquidityDetail: UserLiquidityDetail) => { + const { + unclaimed_fee_x_amount, + unclaimed_fee_y_amount, + total_fees_price, + } = liquidityDetail; + total_x += +unclaimed_fee_x_amount; + total_y += +unclaimed_fee_y_amount; + + total_price += +total_fees_price; + }); + + let display_total_price = '$'; + + if (total_price == 0) { + display_total_price = display_total_price + '0'; + } else if (total_price < 0.01) { + display_total_price = display_total_price + '<0.01'; + } else { + display_total_price = + display_total_price + + toInternationalCurrencySystem(total_price.toString(), 3); + } + + let display_total_x = '0'; + let display_total_y = '0'; + if (total_x == 0) { + display_total_x = '0'; + } else if (total_x < 0.01) { + display_total_x = '<0.01'; + } else { + display_total_x = toInternationalCurrencySystem(total_x.toString(), 3); + } + if (total_y == 0) { + display_total_y = '0'; + } else if (total_y < 0.01) { + display_total_y = '<0.01'; + } else { + display_total_y = toInternationalCurrencySystem(total_y.toString(), 3); + } + return { + total_fee_x: display_total_x, + total_fee_y: display_total_y, + total_fee_price: display_total_price, + }; + } + function addLiquidity() { setOperationType('add'); setShowSelectLiquidityBox(true); @@ -821,31 +885,233 @@ function YourLiquidityBox(props: { setOperationType('remove'); setShowSelectLiquidityBox(true); } + + const { accountId } = useWalletSelector(); + + const accountAPR = useDCLAccountAPR({ + pool_id: poolDetail.pool_id, + account_id: accountId, + }); + + const [hover, setHover] = useState(false); + + const [noReverseRange, setNoReverseRange] = useState(true); + + function getGroupLiquidities() { + const tokenMetadata_x_y = [token_x_metadata, token_y_metadata]; + + let rate_need_to_reverse_display: boolean; + + if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { + rate_need_to_reverse_display = true; + } + + if (!noReverseRange) { + rate_need_to_reverse_display = !rate_need_to_reverse_display; + } + + function getRateMapTokens() { + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + if (rate_need_to_reverse_display) { + return `${tokenX.symbol}/${tokenY.symbol}`; + } else { + return `${tokenY.symbol}/${tokenX.symbol}`; + } + } + } + + function getRate( + direction: string, + left_point: number, + right_point: number + ) { + let value = ''; + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + if (direction == 'left') { + value = getPriceByPoint(left_point, decimalRate); + } else if (direction == 'right') { + value = getPriceByPoint(right_point, decimalRate); + } + if (rate_need_to_reverse_display && +value !== 0) { + value = new BigNumber(1).dividedBy(value).toFixed(); + } + } + + console.log('value: ', value); + + return value; + } + + const priceRangeList = + liquidities?.map((l) => { + return [ + +getRate( + rate_need_to_reverse_display ? 'right' : 'left', + l.left_point, + l.right_point + ), + +getRate( + rate_need_to_reverse_display ? 'left' : 'right', + l.left_point, + l.right_point + ), + ]; + }) || []; + + const rangeList = findRangeIntersection(priceRangeList); + console.log('rangeList: ', rangeList); + + return { + rangeList, + rateMapTokens: getRateMapTokens(), + }; + } + + const groupedData = getGroupLiquidities(); + return (
-
- - - - - - -
- {liquidities?.length > 1 ? ( + + + + + {/* {liquidities?.length > 1 ? ( {liquidities.length} NFTs - ) : null} + ) : null} */} + + ~{getTotalLiquditiesTvl()} +
-
- {getTotalLiquditiesTvl()} + {/*
{liquidities?.length > 1 ? ( {liquidities.length} NFTs ) : null} +
*/} + +
+
+ + + + + + {groupedData.rangeList.map((range: number[], i: number) => { + return ( +
+ {displayNumberToAppropriateDecimals(range[0])} + - + {displayNumberToAppropriateDecimals(range[1])} + {groupedData.rangeList.length > 1 && + i < groupedData.rangeList.length - 1 && ( + , + )} +
+ ); + })} + + { + setNoReverseRange(!noReverseRange); + }} + > + {groupedData.rateMapTokens} + +
+
+ +
+ + + + +
+ {getTotalTokenAmount().total_x} + + {token_x_metadata.symbol} + + + + + {getTotalTokenAmount().total_y} + + {token_y_metadata.symbol} +
+
+ +
+ + + + +
+ {accountAPR} +
+
+ +
+ + + + +
{ + setHover(true); + }} + onMouseLeave={() => { + setHover(false); + }} + > +
+ {hover && ( +
+
+ {toRealSymbol(token_x_metadata.symbol)} + {getTotalFee().total_fee_x} +
+ +
+ {toRealSymbol(token_y_metadata.symbol)} + + {getTotalFee().total_fee_y} +
+
+ )} +
+ + {getTotalFee().total_fee_price} +
+
+ + {/* +
@@ -867,7 +1133,8 @@ function YourLiquidityBox(props: { {getTotalTokenAmount().total_y} -
+
*/} +
{ @@ -1010,63 +1277,57 @@ function UnclaimedFeesBox(props: any) { getTotalFeeAmount(); return (
-
-
- - - - - - -
- {liquidities?.length > 1 ? ( +
+ + + + + {/* {liquidities?.length > 1 ? ( {liquidities.length} NFTs - ) : null} + ) : null} */} + + + ~{getTotalLiquditiesFee()} +
-
- {getTotalLiquditiesFee()} +
{liquidities?.length > 1 ? ( {liquidities.length} NFTs ) : null}
-
-
- - - {token_x_metadata.symbol} - + +
+
+
+ + {display_amount_x} +
+
+ + {display_amount_y} +
- {display_amount_x} -
-
-
- - - {token_y_metadata.symbol} - + +
+ } + />
- {display_amount_y} -
-
- ( - 1 ? 'claim_all' : 'claim'} - /> - )} - />
); @@ -1162,7 +1423,7 @@ function RelatedFarmsBox(props: any) { if (!related_seed) return null; return (
- + >
Farm APR
@@ -1946,14 +2207,14 @@ export function RecentTransactions({ const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_x); const displayInAmount = - Number(AmountIn) < 0.01 + Number(AmountIn) < 0.01 && Number(AmountIn) > 0 ? '<0.01' : numberWithCommas(toPrecision(AmountIn, 6)); const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_y); const displayOutAmount = - Number(AmountOut) < 0.01 + Number(AmountOut) < 0.01 && Number(AmountOut) > 0 ? '<0.01' : numberWithCommas(toPrecision(AmountOut, 6)); @@ -2623,4 +2884,6 @@ interface UserLiquidityDetail { hashId: string; l_price: string; r_price: string; + unclaimed_fee_y_amount?: string; + unclaimed_fee_x_amount?: string; } diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index b4e586be3..4fa39f02d 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -25,6 +25,7 @@ import { toPrecision, toReadableNumber, formatWithCommas, + scientificNotationToString, } from '~utils/numbers'; import { TokenMetadata } from '../../services/ft-contract'; import { useTokens } from '../../state/token'; @@ -414,6 +415,7 @@ export default function YourLiquidityPageV3() { setLiquidityLoadingDone={setV2LiquidityLoadingDone} setLiquidityQuantity={setV2LiquidityQuantity} styleType="1" + liquidityLoadingDone={v2LiquidityLoadingDone} >
{/* your v1 liquidity */} @@ -520,6 +522,50 @@ export function get_your_apr( return '-'; } } + +export function get_your_apr_raw( + liquidity: UserLiquidityInfo, + seed: Seed, + tokenPriceList: Record +) { + const { farmList, total_seed_amount, total_seed_power, seed_id } = seed; + // principal + const total_principal = get_liquidity_value(liquidity, seed, tokenPriceList); + // seed total rewards + let total_rewards = '0'; + const effectiveFarms = getEffectiveFarmList(farmList); + effectiveFarms.forEach((farm: FarmBoost) => { + const { token_meta_data } = farm; + const { daily_reward, reward_token } = farm.terms; + const quantity = toReadableNumber(token_meta_data.decimals, daily_reward); + const reward_token_price = Number(tokenPriceList[reward_token]?.price || 0); + const cur_token_rewards = new BigNumber(quantity) + .multipliedBy(reward_token_price) + .multipliedBy(365); + total_rewards = cur_token_rewards.plus(total_rewards).toFixed(); + }); + // lp percent + let percent; + const mint_amount = mint_liquidity(liquidity, seed_id); + const temp_total = new BigNumber(total_seed_power || 0).plus(mint_amount); + if (temp_total.isGreaterThan(0)) { + percent = new BigNumber(mint_amount).dividedBy(temp_total); + } + // profit + let profit; + if (percent) { + profit = percent.multipliedBy(total_rewards); + } + + // your apr + if (profit && +total_principal > 0) { + const your_apr = profit.dividedBy(total_principal).multipliedBy(100); + + return your_apr.toFixed(); + } else { + return ''; + } +} function get_liquidity_value( liquidity: UserLiquidityInfo, seed: Seed, @@ -562,6 +608,8 @@ export function get_detail_the_liquidity_refer_to_seed({ const { mft_id, left_point, right_point, amount } = liquidity; let Icon; let your_apr; + let your_apr_raw; + let link; let inRange; let status; @@ -569,6 +617,7 @@ export function get_detail_the_liquidity_refer_to_seed({ seeds: all_seeds, pool_id: liquidity.pool_id, }); + const canFarmSeed = active_seeds.find((seed: Seed) => { const { min_deposit, seed_id } = seed; const [fixRange, dcl_pool_id, left_point_seed, right_point_seed] = seed_id @@ -591,6 +640,7 @@ export function get_detail_the_liquidity_refer_to_seed({ if (condition1 && condition2 && condition3) return true; }); const targetSeed = canFarmSeed || active_seeds[0]; + console.log('targetSeed: ', targetSeed); if (targetSeed) { const { seed_id } = targetSeed; const [fixRange, dcl_pool_id, left_point_seed, right_point_seed] = seed_id @@ -604,6 +654,7 @@ export function get_detail_the_liquidity_refer_to_seed({ }); if (canFarmSeed) { your_apr = get_your_apr(liquidity, targetSeed, tokenPriceList); + your_apr_raw = get_your_apr_raw(liquidity, targetSeed, tokenPriceList); } Icon = get_intersection_icon_by_radio(radio); inRange = +radio > 0; @@ -631,6 +682,8 @@ export function get_detail_the_liquidity_refer_to_seed({ link, inRange, status, + your_apr_raw, + targetSeed, }; } export function NoLiquidity({ diff --git a/src/services/config.ts b/src/services/config.ts index cfc43c687..1d8c18899 100644 --- a/src/services/config.ts +++ b/src/services/config.ts @@ -317,9 +317,9 @@ export default function getConfig(env: string = process.env.NEAR_ENV) { VotingGauge: ['5%', '10%'], REF_UNI_V3_SWAP_CONTRACT_ID: process.env.REF_UNI_V3_SWAP_CONTRACT_ID || - 'dclv2-dev.ref-dev.testnet', + 'refv2-dev.ref-dev.testnet', REF_UNI_SWAP_CONTRACT_ID: - process.env.REF_UNI_SWAP_CONTRACT_ID || 'dclv1-dev.ref-dev.testnet', + process.env.REF_UNI_SWAP_CONTRACT_ID || 'refv2-dev.ref-dev.testnet', kitWalletOn: true, FARM_BLACK_LIST_V2: process.env.FARM_BLACK_LIST_V2 || ['666'], boostBlackList: process.env.FARM__BOOST_BLACK_LIST || [''], diff --git a/src/state/pool.ts b/src/state/pool.ts index 489812501..290f6d1e8 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -1430,6 +1430,8 @@ export const useDCLAccountAPR = ({ const [accountAPR, setAccountAPR] = useState('-'); useEffect(() => { + if (!account_id || !pool_id) return; + getDCLAccountFee({ pool_id, account_id, diff --git a/tailwind.config.js b/tailwind.config.js index 9e673d4c4..05ad8189e 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -83,6 +83,7 @@ module.exports = { 'linear-gradient(180deg, rgba(0,198,162,0.8) 0%, rgba(0,139,114,0.8) 100%)', limitOrderButtonHover: 'linear-gradient(180deg, #2F404D 0%, #233039 100%)', + DCLIconGradient: 'linear-gradient(90deg, #4E4AFF 0%, #00C6A2 100%)', darkGradientBg: 'linear-gradient(180deg, #1D2932 0%, #001320 100%)', orderGradient: 'linear-gradient(180deg, #455765 0%, #223746 100%)', orderGraidentHover: 'linear-gradient(180deg, #62798A 0%, #334B5E 100%)', @@ -112,6 +113,7 @@ module.exports = { dclBannerColor: 'linear-gradient(90deg, #00C6A2 2.54%, rgba(91, 64, 255, 0.5) 70%, rgba(91, 64, 255, 0) 100%)', dclIconBgColor: 'linear-gradient(180deg, #00C6A2 0%, #5B40FF 100%)', + dclFarmTipColor: 'linear-gradient(270deg, #00C6A2 0%, #5B40FF 68%)', sellGradientRed: 'linear-gradient(180deg, #944A8C 0%, #D26060 100%)', swapCardGradient: 'linear-gradient(180deg, #213441 0%, #15242F 100%)', sellGradientRedReverse: @@ -389,18 +391,18 @@ module.exports = { burrowTitleGreenColor: '#78FF9E', burrowPurpleColor: '#BCAB8F', burrowDarkColor: '#04121F', - burrowTabColor:'#22333E', - burrowTableBorderColor:"rgba(48, 67, 82, 0.5)", - burrowTitleGreenColor:'#78FF9E', - burrowPurpleColor:'#BCAB8F', - burrowDarkColor:'#04121F', - overviewBurrowColor:'#93806E', - overviewBurrowRedColor:'#F083BE', - overviewBorderColor:'#263540', - overviewLightBlueColor:'#ACE1FF', - overviewMaskColor:'rgba(13, 29, 39, 0.5)', - overviewPurpleColor:'#816CFF', - overviewGreyColor:'#314758', + burrowTabColor: '#22333E', + burrowTableBorderColor: 'rgba(48, 67, 82, 0.5)', + burrowTitleGreenColor: '#78FF9E', + burrowPurpleColor: '#BCAB8F', + burrowDarkColor: '#04121F', + overviewBurrowColor: '#93806E', + overviewBurrowRedColor: '#F083BE', + overviewBorderColor: '#263540', + overviewLightBlueColor: '#ACE1FF', + overviewMaskColor: 'rgba(13, 29, 39, 0.5)', + overviewPurpleColor: '#816CFF', + overviewGreyColor: '#314758', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], @@ -434,6 +436,7 @@ module.exports = { '1000px': '1000px', '1024px': '1024px', '1050px': '1050px', + '1200px': '1200px', 54: '13.5rem', 34: '8.5rem', }, From f1c390aff1fb424e4a5e0f35c937d9855480cd6d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 9 Jun 2023 00:46:11 +0800 Subject: [PATCH 008/204] add liquidity chart --- src/components/d3Chart/AddChart.tsx | 1039 +++++++++++++++++++++++++ src/components/d3Chart/config.tsx | 66 ++ src/components/d3Chart/interfaces.tsx | 39 + src/components/d3Chart/utils.ts | 60 ++ src/services/indexer.ts | 19 + tailwind.config.js | 26 +- 6 files changed, 1237 insertions(+), 12 deletions(-) create mode 100644 src/components/d3Chart/AddChart.tsx create mode 100644 src/components/d3Chart/config.tsx create mode 100644 src/components/d3Chart/interfaces.tsx create mode 100644 src/components/d3Chart/utils.ts diff --git a/src/components/d3Chart/AddChart.tsx b/src/components/d3Chart/AddChart.tsx new file mode 100644 index 000000000..51ad59515 --- /dev/null +++ b/src/components/d3Chart/AddChart.tsx @@ -0,0 +1,1039 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { isMobile } from '../../utils/device'; +import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; +import { get_pool, PoolInfo } from '../../services/swapV3'; +import { + getPriceByPoint, + getPointByPrice, + POINTLEFTRANGE, + POINTRIGHTRANGE, +} from '../../services/commonV3'; +import { ONLY_ZEROS, toPrecision } from '../../utils/numbers'; +import { getDclPoolPoints } from '../../services/indexer'; +import { sortBy } from 'lodash'; +import { + IChartData, + IChartItemConfig, + IChartConfig, + IBinDetail, +} from './interfaces'; +import { formatPrice, formatNumber, formatPercentage } from './utils'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, +} from './config'; +import Big from 'big.js'; +import * as d3 from 'd3'; +export default function AddChart({ + pool_id, + leftPoint, + rightPoint, + setLeftPoint, + setRightPoint, +}: { + pool_id: string; + leftPoint?: number; + rightPoint?: number; + setLeftPoint?: Function; + setRightPoint?: Function; +}) { + const [pool, setPool] = useState(); + const [price_range, set_price_range] = useState(); + const [chartDataList, setChartDataList] = useState(); + const [binDetail, setBinDetail] = useState(); + const [dragLeftPoint, setDragLeftPoint] = useState(leftPoint); + const [dragRightPoint, setDragRightPoint] = useState(rightPoint); + const [zoom, setZoom] = useState(); + /** constant start */ + const svgWidth = 520; + const svgHeight = 250; + const axisHeight = 21; + const wholeBarHeight = svgHeight - axisHeight; + const dragBarWidth = 28; + const disFromHoverBoxToPointer = 20; + const percentBoxWidth = 44; + const disFromPercentBoxToDragBar = 2; + const svgPaddingX = 5; + const defaultPercent = 10; // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + /** constant end */ + useEffect(() => { + if (pool_id) { + get_pool_detail(pool_id); + } + }, [pool_id]); + useEffect(() => { + if (pool) { + get_chart_data(); + } + }, [pool]); + useEffect(() => { + if (price_range && chartDataList) { + drawChart(); + } + }, [price_range, chartDataList]); + useEffect(() => { + if (isValid(dragLeftPoint)) { + const scale = scaleAxis(); + const newPoint = dragLeftPoint; + const newPrice = get_price_by_point(newPoint); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice) - dragBarWidth / 2; + d3.select('.drag-left').attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select('.percentLeft') + .attr( + 'transform', + `translate(${ + x - + (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + const rightX = Number( + d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + ); + const W = rightX - x - dragBarWidth / 2; + d3.select('.overlap rect') + .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) + .attr('width', W); + setLeftPoint && setLeftPoint(newPoint); + } + }, [dragLeftPoint]); + useEffect(() => { + if (isValid(leftPoint) && leftPoint !== dragLeftPoint) { + setDragLeftPoint(leftPoint); + } + }, [leftPoint]); + useEffect(() => { + if (isValid(dragRightPoint)) { + const scale = scaleAxis(); + const newPoint = dragRightPoint; + const newPrice = get_price_by_point(newPoint); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice); + d3.select('.drag-right').attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select('.percentRight') + .attr( + 'transform', + `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + + const leftX = Number( + d3.select('.drag-left').attr('transform').split(',')[0].slice(10) + ); + const W = x - leftX - dragBarWidth / 2; + d3.select('.overlap rect').attr('width', W); + setRightPoint && setRightPoint(newPoint); + } + }, [dragRightPoint]); + useEffect(() => { + if (isValid(rightPoint) && rightPoint !== dragRightPoint) { + setDragRightPoint(rightPoint); + } + }, [rightPoint]); + async function get_pool_detail(pool_id: string) { + const p: PoolInfo = await get_pool(pool_id); + const { token_x, token_y } = p; + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + setPool(p); + } + async function get_chart_data() { + const { point_delta, token_x_metadata, token_y_metadata, pool_id } = pool; + const { bin: bin_final, rangeGear, range } = getConfig(); + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); + const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); + const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); + const list = await getDclPoolPoints(pool_id, bin_final, point_l, point_r); + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + setChartDataList(list); + } + function get_price_range_by_percent(percent: number): [string, string] { + const p_l_r = percent / 100; + const price = get_current_price(); + const price_l_temp = Big(1 - p_l_r).mul(price); + const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); + const price_r = Big(1 + p_l_r) + .mul(price) + .toFixed(); + + return [price_l, price_r]; + } + function drawChart() { + const data: IChartData[] = getDataDisplayInChart(); + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + // 创建横坐标轴 + draw_axis({ scale }); + // down bars + draw_down_bars({ data, scale, scaleBar }); + // up bars + draw_up_bars({ data, scale, scaleBar }); + // background bars + draw_background_bars({ data, scale }); + // current line + draw_current_lint({ scale }); + // drag left + draw_drag_left({ scale }); + // drag right + draw_drag_right({ scale }); + // init overlap area + draw_init_overlap_area({ scale }); + } + function getDataDisplayInChart() { + const { bin: bin_final } = getConfig(); + const { token_x_metadata, token_y_metadata, point_delta } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + const data: IChartData[] = chartDataList.map((o: IChartData) => { + const { point } = o; + const price_l = getPriceByPoint(+point, decimalRate_price); + const point_r = +point + point_delta * bin_final; + const point_r_close = +point + point_delta * bin_final + 1; + const price_r = getPriceByPoint(point_r, decimalRate_price); + const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); + + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + price: price_l.toString(), + price_r: price_r.toString(), + point_r: point_r.toString(), + price_r_close: price_r_close.toString(), + }; + }); + return data; + } + function hoverBox(e: any, d: IChartData) { + d3.select('.overBox').attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + const { + point, + token_x, + token_y, + order_x, + order_y, + liquidity, + order_liquidity, + fee, + total_liquidity, + } = d; + const { current_point } = pool; + const { colors } = getConfig(); + + const total_token_x = Big(token_x).plus(order_x); + const total_token_y = Big(token_y).plus(order_y); + const price_by_token_x = get_price_by_point(+point); + const price_by_token_y = reverse_price(price_by_token_x); + const apr = Big(total_liquidity).gt(0) + ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() + : '0'; + setBinDetail({ + // point, + feeApr: formatPercentage(apr), + color: +point > current_point ? colors[1] : colors[0], + ...(total_token_x.gt(0) + ? { + token_x_amount: formatNumber(total_token_x.toFixed()), + token_x_amount_in_liquidity: formatNumber(token_x), + token_x_amount_in_order: formatNumber(order_x), + } + : {}), + ...(total_token_y.gt(0) + ? { + token_y_amount: formatNumber(total_token_y.toFixed()), + token_y_amount_in_liquidity: formatNumber(token_y), + token_y_amount_in_order: formatNumber(order_y), + } + : {}), + price_by_token_x: formatPrice(price_by_token_x), + price_by_token_y: formatPrice(price_by_token_y), + }); + } + function LeaveBox(e: any, d: IChartData) { + d3.select('.overBox').attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function draw_axis({ scale }: { scale: any }) { + const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); + d3.select('svg .axis').call(axis).selectAll('text').attr('fill', '#7E8A93'); + d3.select('svg .axis') + .attr('transform', `translate(0, ${svgHeight - axisHeight})`) + .select('.domain') + .attr('stroke', 'transparent'); + } + function draw_down_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { current_point } = pool; + const { colors } = getConfig(); + d3.select('.bars_liquidity') + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return scaleBar(+d.liquidity); + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return wholeBarHeight - scaleBar(+d.liquidity); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }); + } + function draw_up_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { colors } = getConfig(); + const { current_point } = pool; + d3.select('.bars_order') + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return scaleBar(+d.order_liquidity); + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return ( + wholeBarHeight - scaleBar(+d.liquidity) - scaleBar(+d.order_liquidity) + ); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }) + .attr('opacity', '0.7'); + } + function draw_background_bars({ + data, + scale, + }: { + data: IChartData[]; + scale: Function; + }) { + d3.select('.bars_background') + .selectAll('rect') + .data(data) + .join('rect') + .on('mousemove', function (e, d) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverBox(e, d); + }) + .on('mouseleave', function (e, d) { + d3.select(this).attr('fill', 'transparent'); + LeaveBox(e, d); + }) + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r_close).toNumber()) - + scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return wholeBarHeight; + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return 0; + }) + .attr('rx', 2) + .attr('fill', 'transparent'); + } + function draw_current_lint({ scale }: { scale: Function }) { + d3.select('.currentLine').attr( + 'style', + `transform:translate(${ + scale(+get_current_price()) + svgPaddingX + }px, -${axisHeight}px)` + ); + } + function draw_drag_left({ scale }: { scale: any }) { + // 初始化左的位置 + const price = get_current_price(); + let price_l; + if (leftPoint) { + price_l = get_price_by_point(leftPoint); + } else { + const price_l_temp = Big(1 - defaultPercent / 100) + .mul(price) + .toFixed(); + const newLeftPoint = get_nearby_bin_left_point( + get_point_by_price(price_l_temp) + ); + price_l = get_price_by_point(newLeftPoint); + setDragLeftPoint(newLeftPoint); + } + const x = scale(price_l) - dragBarWidth / 2; + d3.select('.drag-left').attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select('.percentLeft') + .attr( + 'transform', + `translate(${ + x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(`${diffPrices(price_l)}%`) + .attr('fill', 'white'); + const dragLeft = d3.drag().on('drag', function (e) { + const rightX = Number( + d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + ); + if (rightX < e.x || e.x < dragBarWidth / 2) return; + const p = scale.invert(e.x); + const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); + setDragLeftPoint(newLeftPoint); + }); + d3.select('.drag-left').call(dragLeft); + } + function draw_drag_right({ scale }: { scale: any }) { + // 初始化右的位置 + const price = get_current_price(); + let price_r; + if (rightPoint) { + price_r = get_price_by_point(rightPoint); + } else { + const price_r_temp = Big(1 + defaultPercent / 100) + .mul(price) + .toFixed(); + const newRightPoint = get_nearby_bin_right_point( + get_point_by_price(price_r_temp) + ); + price_r = get_price_by_point(newRightPoint); + setDragRightPoint(newRightPoint); + } + const x = scale(price_r); + d3.select('.drag-right').attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select('.percentRight') + .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) + .select('text') + .text(`${diffPrices(price_r)}%`) + .attr('fill', 'white'); + const dragRight = d3.drag().on('drag', (e) => { + const leftX = Number( + d3.select('.drag-left').attr('transform').split(',')[0].slice(10) + ); + const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); + console.log('limitx', limitx); + if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; + console.log('e.x', e.x); + const p = scale.invert(e.x); + const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); + setDragRightPoint(newRightPoint); + }); + d3.select('.drag-right').call(dragRight); + } + function draw_init_overlap_area({ scale }: { scale: any }) { + const price = get_current_price(); + let price_l; + if (leftPoint) { + price_l = get_price_by_point(leftPoint); + } else { + const price_l_temp = Big(1 - defaultPercent / 100) + .mul(price) + .toFixed(); + const newLeftPoint = get_nearby_bin_left_point( + get_point_by_price(price_l_temp) + ); + price_l = get_point_by_price(newLeftPoint.toString()); + } + const x = scale(price_l); + const rightX = Number( + d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + ); + const W = rightX - x; + d3.select('.overlap rect') + .attr('transform', `translate(${x}, 0)`) + .attr('width', W); + } + function get_current_price() { + return get_price_by_point(pool.current_point); + } + function get_point_by_price(price: string) { + const { point_delta, token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_nearby_bin_right_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin + slots; + } + function get_nearby_bin_left_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin; + } + function clickToLeft() { + const { bin } = getConfig(); + const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); + setDragLeftPoint(get_nearby_bin_left_point(newPoint)); + } + function clickToRight() { + const { bin } = getConfig(); + const newPoint = dragRightPoint + pool.point_delta * (bin + 1); + setDragRightPoint(get_nearby_bin_left_point(newPoint)); + } + function scaleAxis() { + return d3 + .scaleLinear() + .domain(price_range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxisY() { + const L: number[] = []; + chartDataList.forEach((o: IChartData) => { + const { liquidity, order_liquidity } = o; + L.push( + +liquidity, + +order_liquidity, + Big(liquidity).plus(order_liquidity).toNumber() + ); + }); + const sortL = sortBy(L); + return d3 + .scaleLinear() + .domain([+sortL[0], +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); + } + function diffPrices(newPrice: string, peferencePrice?: string) { + let movePercent; + const price = peferencePrice || get_current_price(); + if (+price > +newPrice) { + movePercent = -Big(1) + .minus(Big(newPrice).div(price)) + .mul(100) + .toFixed(0, 1); + } else { + movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); + } + return movePercent; + } + function getConfig(): IChartItemConfig { + const { bin, range, colors, rangeGear } = + get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const percent_final = custom_config[pool_id]?.range || range; + const bin_final = custom_config[pool_id]?.bin || bin; + const colors_final = custom_config[pool_id]?.colors || colors; + const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; + return { + bin: bin_final, + range: percent_final, + colors: colors_final, + rangeGear: rangeGear_final, + }; + } + function get_current_price_by_token_x() { + if (pool) { + return formatPrice(get_current_price()); + } + return '-'; + } + function get_current_price_by_token_y() { + if (pool) { + return formatPrice(reverse_price(get_current_price())); + } + return '-'; + } + function reverse_price(price: string) { + if (Big(price).eq(0)) return 0; + return Big(1).div(price).toFixed(); + } + function zoomOut() { + const { rangeGear } = getConfig(); + const targetPercent = rangeGear.find((item) => item < zoom); + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + function zoomIn() { + const { rangeGear } = getConfig(); + const index = rangeGear.findIndex((item) => item == zoom); + let targetPercent; + if (index !== 0) { + targetPercent = rangeGear[index - 1]; + } + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + return ( +
+ {/* 控件按钮*/} +
+
+ +
+
+ +
+
+ +
+
+ +
+
+ + + + + + {/* 横坐标轴 */} + + {/* 拖拽线 left */} + + + + + + + + + + + + + + {/* 拖拽线 right */} + + + + + + + + + + + + + + {/* 左右坐标轴中间的重叠区域 */} + + + + + + {/* hover到柱子上的悬浮框 */} +
+
+ Fee APR (24h) + {/* todo test 数据 */} + {/* {binDetail?.point} */} + + {binDetail?.feeApr} + +
+
+ Price + + {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} /{' '} + {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} + +
+ {binDetail?.token_x_amount ? ( + <> +
+ + {pool?.token_x_metadata?.symbol} Amount + + + {binDetail.token_x_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_x_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_x_amount_in_order} + +
+ + ) : null} + {binDetail?.token_y_amount ? ( + <> +
+ + {pool?.token_y_metadata?.symbol} Amount + + + {binDetail.token_y_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_y_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_y_amount_in_order} + +
+ + ) : null} +
+ {/* current 价格 */} +
+
+
+
+ + {pool?.token_x_metadata?.symbol}:{' '} + + + + {pool?.token_y_metadata?.symbol} + +
+
+ + {pool?.token_y_metadata?.symbol}:{' '} + + + + {pool?.token_x_metadata?.symbol} + +
+
+
+
+ ); +} +function isValid(n: number) { + if (n !== undefined && n !== null) return true; + return false; +} +function LeftArrowIcon(props: any) { + return ( + + + + ); +} +function RightArrowIcon(props: any) { + return ( + + + + ); +} + +function AddIcon(props: any) { + return ( + + + + + ); +} + +function SubIcon(props: any) { + return ( + + + + ); +} diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx new file mode 100644 index 000000000..f3bf2802a --- /dev/null +++ b/src/components/d3Chart/config.tsx @@ -0,0 +1,66 @@ +import { IChartConfig, IChartItemConfig } from './interfaces'; +export function get_custom_config_for_chart(): IChartConfig { + const env: string = process.env.NEAR_ENV; + if (env == 'pub-testnet') { + return {}; + } else if (env == 'testnet') { + return {}; + } else { + return { + 'phoenix-bonds.near|wrap.near|2000': + // pNEAR<>NEAR@20000 + { + bin: 2, + range: 20, + rangeGear: [100, 80, 60, 40, 20, 10], + colors: ['#A36949', '#717C84'], + }, + 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|aurora|2000': + // ETH<>USDC.e@2000 + { + bin: 4, + range: 50, + rangeGear: [130, 110, 90, 70, 50, 30, 20, 10], + colors: ['#626CA3', '#2775CA'], + }, + 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near|2000': + // AURORA<>USDC.e@2000 + { + bin: 10, + range: 100, + rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], + colors: ['#79BD84', '#2775CA'], + }, + 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|wrap.near|2000': + // NEAR<>USDC.e@2000 + { + bin: 10, + range: 100, + rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], + colors: ['#707C84', '#2775CA'], + }, + }; + } +} +/** + * + * @returns + * bin: 一个bin里slot的数量 + * range: 当前价格的百分比,获取以当前价格为中心点的左右range 默认 + * rangeGear: range 的档位,用来实现缩放 + */ +export function get_default_config_for_chart(): IChartItemConfig { + const env: string = process.env.NEAR_ENV; + if (env == 'pub-testnet') { + return {}; + } else if (env == 'testnet') { + return {}; + } else { + return { + bin: 20, + range: 10, + rangeGear: [100, 80, 60, 40, 20, 10], + colors: ['#707C84', '#2775CA'], + }; + } +} diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx new file mode 100644 index 000000000..5481d50b5 --- /dev/null +++ b/src/components/d3Chart/interfaces.tsx @@ -0,0 +1,39 @@ +export interface IChartData { + pool_id: string; + point: string; + price?: string; + price_r?: string; + point_r?: string; + price_r_close?: string; + liquidity: string; + token_x: string; + token_y: string; + order_x: string; + order_y: string; + order_liquidity: string; + fee: string; + total_liquidity: string; + sort_number: number; +} + +export interface IChartItemConfig { + bin?: number; + range?: number; + colors?: string[]; + rangeGear?: number[]; +} +export interface IChartConfig { + [token_id: string]: IChartItemConfig; +} +export interface IBinDetail { + feeApr?: string; + token_x_amount?: string; + token_x_amount_in_liquidity?: string; + token_x_amount_in_order?: string; + token_y_amount?: string; + token_y_amount_in_liquidity?: string; + token_y_amount_in_order?: string; + price_by_token_x?: string; + price_by_token_y?: string; + color: string; +} diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts new file mode 100644 index 000000000..c7b7ed9e8 --- /dev/null +++ b/src/components/d3Chart/utils.ts @@ -0,0 +1,60 @@ +import Big from 'big.js'; +import { + formatWithCommas, + toInternationalCurrencySystem, +} from '../../utils/numbers'; +export const formatWithCommas_usd = (v: string | number) => { + if (isInvalid(v)) return '$-'; + const big = Big(v); + if (big.eq(0)) { + return '$0'; + } else if (big.lt(0.01)) { + return '<$0.01'; + } else if (big.lt(10000)) { + return '$' + formatWithCommas(big.toFixed(2, 1)); + } else { + return '$' + formatWithCommas(big.toFixed(0, 1)); + } +}; + +export const formatPercentage = (v: string | number) => { + if (isInvalid(v)) return '-%'; + const big = Big(v); + if (big.eq(0)) { + return '0%'; + } else if (big.lt(0.01)) { + return '<0.01%'; + } else { + return big.toFixed(2, 1) + '%'; + } +}; +export const formatNumber = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return big.toFixed(2, 1); + } +}; +export const formatPrice = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.0001'; + } else { + return big.toFixed(4, 1); + } +}; +export const formatToInternationalCurrencySystem$ = (v: string | number) => { + if (isInvalid(v)) return '$-'; + return '$' + toInternationalCurrencySystem(Big(v || 0).toFixed(), 2); +}; +export const isInvalid = function (v: any) { + if (v === '' || v === undefined || v === null) return true; + return false; +}; diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 28dafd6b1..7420313d3 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -798,3 +798,22 @@ export const getTokenPairRate = async ({ }; }); }; + +export const getDclPoolPoints = async ( + pool_id: string, + bin: number, + start_point: number, + end_point: number +) => { + return await fetch( + config.indexerUrl + + `/get-dcl-points?pool_id=${pool_id}&slot_number=${bin}&start_point=${start_point}&end_point=${end_point}` + ) + .then(async (res) => { + const data = await res.json(); + return data; + }) + .catch(() => { + return []; + }); +}; diff --git a/tailwind.config.js b/tailwind.config.js index 05ad8189e..718dd34a3 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -391,18 +391,20 @@ module.exports = { burrowTitleGreenColor: '#78FF9E', burrowPurpleColor: '#BCAB8F', burrowDarkColor: '#04121F', - burrowTabColor: '#22333E', - burrowTableBorderColor: 'rgba(48, 67, 82, 0.5)', - burrowTitleGreenColor: '#78FF9E', - burrowPurpleColor: '#BCAB8F', - burrowDarkColor: '#04121F', - overviewBurrowColor: '#93806E', - overviewBurrowRedColor: '#F083BE', - overviewBorderColor: '#263540', - overviewLightBlueColor: '#ACE1FF', - overviewMaskColor: 'rgba(13, 29, 39, 0.5)', - overviewPurpleColor: '#816CFF', - overviewGreyColor: '#314758', + burrowTabColor:'#22333E', + burrowTableBorderColor:"rgba(48, 67, 82, 0.5)", + burrowTitleGreenColor:'#78FF9E', + burrowPurpleColor:'#BCAB8F', + burrowDarkColor:'#04121F', + overviewBurrowColor:'#93806E', + overviewBurrowRedColor:'#F083BE', + overviewBorderColor:'#263540', + overviewLightBlueColor:'#ACE1FF', + overviewMaskColor:'rgba(13, 29, 39, 0.5)', + overviewPurpleColor:'#816CFF', + overviewGreyColor:'#314758', + chartHoverBoxBg:'rgba(26, 39, 48, 0.9)', + chartBorderColor:'#344451', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From 1d13ed407e45aac63d8483fce39a8fc81b8d78a5 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 9 Jun 2023 12:07:51 +0800 Subject: [PATCH 009/204] add demo --- .../{AddChart.tsx => DclPoolChart.tsx} | 193 +++++++++++++----- src/components/d3Chart/interfaces.tsx | 13 ++ src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 10 + 3 files changed, 160 insertions(+), 56 deletions(-) rename src/components/d3Chart/{AddChart.tsx => DclPoolChart.tsx} (86%) diff --git a/src/components/d3Chart/AddChart.tsx b/src/components/d3Chart/DclPoolChart.tsx similarity index 86% rename from src/components/d3Chart/AddChart.tsx rename to src/components/d3Chart/DclPoolChart.tsx index 51ad59515..7cec0067e 100644 --- a/src/components/d3Chart/AddChart.tsx +++ b/src/components/d3Chart/DclPoolChart.tsx @@ -9,7 +9,6 @@ import { POINTLEFTRANGE, POINTRIGHTRANGE, } from '../../services/commonV3'; -import { ONLY_ZEROS, toPrecision } from '../../utils/numbers'; import { getDclPoolPoints } from '../../services/indexer'; import { sortBy } from 'lodash'; import { @@ -17,6 +16,7 @@ import { IChartItemConfig, IChartConfig, IBinDetail, + IPoolChartConfig, } from './interfaces'; import { formatPrice, formatNumber, formatPercentage } from './utils'; import { @@ -25,18 +25,20 @@ import { } from './config'; import Big from 'big.js'; import * as d3 from 'd3'; -export default function AddChart({ +export default function DclPoolChart({ pool_id, leftPoint, rightPoint, setLeftPoint, setRightPoint, + config, }: { pool_id: string; leftPoint?: number; rightPoint?: number; setLeftPoint?: Function; setRightPoint?: Function; + config?: IPoolChartConfig; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -45,17 +47,27 @@ export default function AddChart({ const [dragLeftPoint, setDragLeftPoint] = useState(leftPoint); const [dragRightPoint, setDragRightPoint] = useState(rightPoint); const [zoom, setZoom] = useState(); + const [randomId, setRandomId] = useState('.' + createRandomString()); /** constant start */ - const svgWidth = 520; - const svgHeight = 250; - const axisHeight = 21; - const wholeBarHeight = svgHeight - axisHeight; + const appearanceConfig: IPoolChartConfig = config || {}; const dragBarWidth = 28; - const disFromHoverBoxToPointer = 20; const percentBoxWidth = 44; - const disFromPercentBoxToDragBar = 2; - const svgPaddingX = 5; - const defaultPercent = 10; // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + const svgWidth = +(appearanceConfig.svgWidth || 520); + const svgHeight = +(appearanceConfig.svgHeight || 250); + const axisHeight = appearanceConfig.axisHidden + ? appearanceConfig.controlHidden + ? 0 + : 5 + : 21; + const wholeBarHeight = svgHeight - axisHeight; + const disFromHoverBoxToPointer = +( + appearanceConfig.disFromPercentBoxToDragBar || 20 + ); + const disFromPercentBoxToDragBar = +( + appearanceConfig.disFromPercentBoxToDragBar || 2 + ); + const svgPaddingX = +(appearanceConfig.svgPaddingX || 5); + const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% /** constant end */ useEffect(() => { if (pool_id) { @@ -73,17 +85,17 @@ export default function AddChart({ } }, [price_range, chartDataList]); useEffect(() => { - if (isValid(dragLeftPoint)) { + if (isValid(dragLeftPoint) && !appearanceConfig.controlHidden) { const scale = scaleAxis(); const newPoint = dragLeftPoint; const newPrice = get_price_by_point(newPoint); const movePercent = diffPrices(newPrice); const x = scale(+newPrice) - dragBarWidth / 2; - d3.select('.drag-left').attr( + d3.select(`${randomId} .drag-left`).attr( 'transform', `translate(${x}, -${axisHeight})` ); - d3.select('.percentLeft') + d3.select(`${randomId} .percentLeft`) .attr( 'transform', `translate(${ @@ -95,32 +107,36 @@ export default function AddChart({ .text(movePercent + '%') .attr('fill', 'white'); const rightX = Number( - d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) ); const W = rightX - x - dragBarWidth / 2; - d3.select('.overlap rect') + d3.select(`${randomId} .overlap rect`) .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) .attr('width', W); setLeftPoint && setLeftPoint(newPoint); } - }, [dragLeftPoint]); + }, [dragLeftPoint, price_range]); useEffect(() => { if (isValid(leftPoint) && leftPoint !== dragLeftPoint) { setDragLeftPoint(leftPoint); } }, [leftPoint]); useEffect(() => { - if (isValid(dragRightPoint)) { + if (isValid(dragRightPoint) && !appearanceConfig.controlHidden) { const scale = scaleAxis(); const newPoint = dragRightPoint; const newPrice = get_price_by_point(newPoint); const movePercent = diffPrices(newPrice); const x = scale(+newPrice); - d3.select('.drag-right').attr( + d3.select(`${randomId} .drag-right`).attr( 'transform', `translate(${x}, -${axisHeight})` ); - d3.select('.percentRight') + d3.select(`${randomId} .percentRight`) .attr( 'transform', `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` @@ -130,13 +146,17 @@ export default function AddChart({ .attr('fill', 'white'); const leftX = Number( - d3.select('.drag-left').attr('transform').split(',')[0].slice(10) + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) ); const W = x - leftX - dragBarWidth / 2; - d3.select('.overlap rect').attr('width', W); + d3.select(`${randomId} .overlap rect`).attr('width', W); setRightPoint && setRightPoint(newPoint); } - }, [dragRightPoint]); + }, [dragRightPoint, price_range]); useEffect(() => { if (isValid(rightPoint) && rightPoint !== dragRightPoint) { setDragRightPoint(rightPoint); @@ -180,22 +200,37 @@ export default function AddChart({ const data: IChartData[] = getDataDisplayInChart(); const scale = scaleAxis(); const scaleBar = scaleAxisY(); - // 创建横坐标轴 - draw_axis({ scale }); // down bars draw_down_bars({ data, scale, scaleBar }); // up bars draw_up_bars({ data, scale, scaleBar }); + // 创建横坐标轴 + if (appearanceConfig.axisHidden) { + d3.select(`${randomId} .axis`).remove(); + } else { + draw_axis({ scale }); + } // background bars - draw_background_bars({ data, scale }); + if (appearanceConfig.hoverBoxHidden) { + d3.select(`${randomId} .bars_background`).remove(); + d3.select(`${randomId} .overBox`).remove(); + } else { + draw_background_bars({ data, scale }); + } // current line - draw_current_lint({ scale }); - // drag left - draw_drag_left({ scale }); - // drag right - draw_drag_right({ scale }); - // init overlap area - draw_init_overlap_area({ scale }); + if (appearanceConfig.currentBarHidden) { + d3.select(`${randomId} .currentLine`).remove(); + } else { + draw_current_bar({ scale }); + } + if (appearanceConfig.controlHidden) { + remove_control(); + } else { + // drag left + draw_drag_left({ scale }); + // drag right + draw_drag_right({ scale }); + } } function getDataDisplayInChart() { const { bin: bin_final } = getConfig(); @@ -230,7 +265,7 @@ export default function AddChart({ return data; } function hoverBox(e: any, d: IChartData) { - d3.select('.overBox').attr( + d3.select(`${randomId} .overBox`).attr( 'style', `visibility:visible;transform:translate(${ e.offsetX + disFromHoverBoxToPointer @@ -280,17 +315,26 @@ export default function AddChart({ }); } function LeaveBox(e: any, d: IChartData) { - d3.select('.overBox').attr( + d3.select(`${randomId} .overBox`).attr( 'style', `visibility:hidden;transform:translate(${ e.offsetX + disFromHoverBoxToPointer }px, ${e.offsetY / 2}px)` ); } + function remove_control() { + d3.select(`${randomId} .control`).remove(); + d3.select(`${randomId} .overlap`).remove(); + d3.select(`${randomId} .rightBar`).remove(); + d3.select(`${randomId} .leftBar`).remove(); + } function draw_axis({ scale }: { scale: any }) { const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); - d3.select('svg .axis').call(axis).selectAll('text').attr('fill', '#7E8A93'); - d3.select('svg .axis') + d3.select(`${randomId} svg .axis`) + .call(axis) + .selectAll('text') + .attr('fill', '#7E8A93'); + d3.select(`${randomId} svg .axis`) .attr('transform', `translate(0, ${svgHeight - axisHeight})`) .select('.domain') .attr('stroke', 'transparent'); @@ -306,7 +350,7 @@ export default function AddChart({ }) { const { current_point } = pool; const { colors } = getConfig(); - d3.select('.bars_liquidity') + d3.select(`${randomId} .bars_liquidity`) .selectAll('rect') .data(data) .join('rect') @@ -341,7 +385,7 @@ export default function AddChart({ }) { const { colors } = getConfig(); const { current_point } = pool; - d3.select('.bars_order') + d3.select(`${randomId} .bars_order`) .selectAll('rect') .data(data) .join('rect') @@ -375,7 +419,7 @@ export default function AddChart({ data: IChartData[]; scale: Function; }) { - d3.select('.bars_background') + d3.select(`${randomId} .bars_background`) .selectAll('rect') .data(data) .join('rect') @@ -406,8 +450,8 @@ export default function AddChart({ .attr('rx', 2) .attr('fill', 'transparent'); } - function draw_current_lint({ scale }: { scale: Function }) { - d3.select('.currentLine').attr( + function draw_current_bar({ scale }: { scale: Function }) { + d3.select(`${randomId} .currentLine`).attr( 'style', `transform:translate(${ scale(+get_current_price()) + svgPaddingX @@ -431,11 +475,11 @@ export default function AddChart({ setDragLeftPoint(newLeftPoint); } const x = scale(price_l) - dragBarWidth / 2; - d3.select('.drag-left').attr( + d3.select(`${randomId} .drag-left`).attr( 'transform', `translate(${x}, -${axisHeight})` ); - d3.select('.percentLeft') + d3.select(`${randomId} .percentLeft`) .attr( 'transform', `translate(${ @@ -447,14 +491,18 @@ export default function AddChart({ .attr('fill', 'white'); const dragLeft = d3.drag().on('drag', function (e) { const rightX = Number( - d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) ); if (rightX < e.x || e.x < dragBarWidth / 2) return; const p = scale.invert(e.x); const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); setDragLeftPoint(newLeftPoint); }); - d3.select('.drag-left').call(dragLeft); + d3.select(`${randomId} .drag-left`).call(dragLeft); } function draw_drag_right({ scale }: { scale: any }) { // 初始化右的位置 @@ -473,28 +521,31 @@ export default function AddChart({ setDragRightPoint(newRightPoint); } const x = scale(price_r); - d3.select('.drag-right').attr( + d3.select(`${randomId} .drag-right`).attr( 'transform', `translate(${x}, -${axisHeight})` ); - d3.select('.percentRight') + d3.select(`${randomId} .percentRight`) .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) .select('text') .text(`${diffPrices(price_r)}%`) .attr('fill', 'white'); const dragRight = d3.drag().on('drag', (e) => { const leftX = Number( - d3.select('.drag-left').attr('transform').split(',')[0].slice(10) + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) ); const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); - console.log('limitx', limitx); if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; console.log('e.x', e.x); const p = scale.invert(e.x); const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); setDragRightPoint(newRightPoint); }); - d3.select('.drag-right').call(dragRight); + d3.select(`${randomId} .drag-right`).call(dragRight); } function draw_init_overlap_area({ scale }: { scale: any }) { const price = get_current_price(); @@ -512,10 +563,22 @@ export default function AddChart({ } const x = scale(price_l); const rightX = Number( - d3.select('.drag-right').attr('transform').split(',')[0].slice(10) + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) ); const W = rightX - x; - d3.select('.overlap rect') + console.log( + 'price_range transform, x, rightX W', + price_range, + d3.select(`${randomId} .drag-right`).attr('transform'), + x, + rightX, + W + ); + d3.select(`${randomId} .overlap rect`) .attr('transform', `translate(${x}, 0)`) .attr('width', W); } @@ -661,10 +724,12 @@ export default function AddChart({ } return (
{/* 控件按钮*/} -
+
{/* 拖拽线 left */} - + {/* 拖拽线 right */} - + ); } +function createRandomChar(c = 'a-z') { + switch (c) { + case 'A-Z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 65); + case 'a-z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 97); + case '0-9': + default: + return String.fromCharCode(Math.trunc(Math.random() * 10) + 48); + } +} +function createRandomString(length = 4) { + let str = ''; + for (let i = 0; i < length; i++) str += createRandomChar(); + return str; +} diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 5481d50b5..5497b20f1 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -37,3 +37,16 @@ export interface IBinDetail { price_by_token_y?: string; color: string; } + +export interface IPoolChartConfig { + svgWidth?: string | number; + svgHeight?: string | number; + disFromHoverBoxToPointer?: string | number; + disFromPercentBoxToDragBar?: string | number; + svgPaddingX?: string | number; + defaultPercent?: string | number; + controlHidden?: boolean; + axisHidden?: boolean; + currentBarHidden?: boolean; + hoverBoxHidden?: boolean; +} diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index e1a09d3f7..2ccefa0cd 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -91,6 +91,7 @@ const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); import Big from 'big.js'; import { SelectTokenDCL } from '../../components/forms/SelectToken'; +import DclPoolChart from '../../components/d3Chart/DclPoolChart'; export default function AddYourLiquidityPageV3() { const [tokenX, setTokenX] = useState(null); @@ -660,6 +661,15 @@ export default function AddYourLiquidityPageV3() { const mobileDevice = isMobile(); return ( <> +
+ {/* 缩略图 */} + {/* */} + {/* 详情页图 */} + {/* */} + {/* 添加页图 */} + {/* */} +
+
Date: Fri, 9 Jun 2023 16:53:50 +0800 Subject: [PATCH 010/204] update return value of getDclPoolPoints --- src/services/indexer.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 7420313d3..29e40aecd 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -811,7 +811,7 @@ export const getDclPoolPoints = async ( ) .then(async (res) => { const data = await res.json(); - return data; + return data?.point_data || []; }) .catch(() => { return []; From 8a8561a8e40506cd19cfca227bb97301c0670e9a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 9 Jun 2023 17:04:03 +0800 Subject: [PATCH 011/204] update --- src/components/d3Chart/DclPoolChart.tsx | 3 ++- src/services/indexer.ts | 4 ++-- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/DclPoolChart.tsx b/src/components/d3Chart/DclPoolChart.tsx index 7cec0067e..78b2ceaa7 100644 --- a/src/components/d3Chart/DclPoolChart.tsx +++ b/src/components/d3Chart/DclPoolChart.tsx @@ -178,7 +178,8 @@ export default function DclPoolChart({ const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); - const list = await getDclPoolPoints(pool_id, bin_final, point_l, point_r); + const result = await getDclPoolPoints(pool_id, bin_final, point_l, point_r); + const list = result.point_data || []; const [price_l_default, price_r_default] = get_price_range_by_percent(range); set_price_range([+price_l_default, +price_r_default]); diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 29e40aecd..79b74be34 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -811,9 +811,9 @@ export const getDclPoolPoints = async ( ) .then(async (res) => { const data = await res.json(); - return data?.point_data || []; + return data; }) .catch(() => { - return []; + return {} }); }; From f710ddcc74ef52856b9ab198e1af80452b37f7ab Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 9 Jun 2023 20:14:13 +0800 Subject: [PATCH 012/204] update add liquidity page --- package.json | 5 +- src/components/icon/Info.tsx | 19 + src/components/icon/V3.tsx | 2 +- src/components/pool/YourLiquidityV2.tsx | 3 + src/global.css | 42 + src/pages/Orderly/components/Common/Icons.tsx | 257 +++ .../Orderly/components/UserBoard/index.tsx | 4 - src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 1967 +++++++++++------ src/services/indexer.ts | 10 +- src/services/near.ts | 2 + src/services/swapV3.ts | 78 +- src/state/pool.ts | 2 +- src/state/swapV3.ts | 2 +- tailwind.config.js | 2 + yarn.lock | 19 + 15 files changed, 1666 insertions(+), 748 deletions(-) diff --git a/package.json b/package.json index c063c1224..f1fdf7307 100644 --- a/package.json +++ b/package.json @@ -39,6 +39,7 @@ "@types/react-icons": "^3.0.0", "@types/react-modal": "^3.12.0", "@types/react-router-dom": "^5.1.7", + "@types/react-slider": "^1.3.1", "@types/react-toastify": "^4.1.0", "autoprefixer": "^10.2.5", "babel-jest": "~26.6.2", @@ -118,6 +119,7 @@ "lodash": "^4.17.21", "mathjs": "^9.3.0", "moment": "^2.29.1", + "multi-range-slider-react": "^2.0.3", "near-api-js": "0.44.2", "near-seed-phrase": "^0.2.0", "postcss": "^8.4.20", @@ -139,6 +141,7 @@ "react-modal": "^3.13.1", "react-router-dom": "^5.2.0", "react-scripts": "^5.0.1", + "react-slider": "^2.0.5", "react-spinners": "^0.11.0", "react-toastify": "^9.1.1", "react-tooltip": "^4.2.17", @@ -168,4 +171,4 @@ "staticPath": "public", "watcherGlob": "**" } -} \ No newline at end of file +} diff --git a/src/components/icon/Info.tsx b/src/components/icon/Info.tsx index 9f5d5f7ba..cb38ade9e 100644 --- a/src/components/icon/Info.tsx +++ b/src/components/icon/Info.tsx @@ -42,6 +42,25 @@ export function Slider({ ); } +export function SliderCurColor() { + return ( + + + + ); +} + export const PopUpContainer = () => { return ( diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index e8e538145..18a11ac22 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -2130,6 +2130,9 @@ function UserLiquidityLineStyleGroup1({ onClick={(e) => { e.stopPropagation(); // setShowAddBox(true); + + history.push('/addLiquidityV2'); + }} color="#fff" minWidth="5rem" diff --git a/src/global.css b/src/global.css index 5023195ef..fb1ee8781 100644 --- a/src/global.css +++ b/src/global.css @@ -1295,3 +1295,45 @@ input[type='range']::-webkit-slider-runnable-track { .hideScroll::-webkit-scrollbar { display: none; } + +.multi-slider { + height: 10px; + border-radius: 6px; + background: #121e27; +} + +.multi-slider .thumb { + width: 21px; + height: 21px; + cursor: pointer; + background: white; + border-radius: 100%; + border: 3px solid #1d2932; + outline: none; + background: #00d6af; +} + +.thumb-0 { + top: -5px; + transform: translateX(-50%); +} + +.thumb-1 { + top: -5px; + transform: translateX(50%); +} + +.invert .thumb-0 { + top: -5px; + transform: translateX(50%); +} + +.invert .thumb-1 { + top: -5px; + transform: translateX(-50%); +} + +.track-1 { + background-color: #00d6af; + height: 10px; +} diff --git a/src/pages/Orderly/components/Common/Icons.tsx b/src/pages/Orderly/components/Common/Icons.tsx index 409ff8fa4..974339a9d 100644 --- a/src/pages/Orderly/components/Common/Icons.tsx +++ b/src/pages/Orderly/components/Common/Icons.tsx @@ -1774,3 +1774,260 @@ export const PolygonRight = () => { ); }; + +export function SpotShape() { + return ( + + + + + + + + + + + ); +} + +export function CurveShape() { + return ( + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + ); +} + +export function BidAskShape() { + return ( + + + + + + + + + + + + + + + + ); +} diff --git a/src/pages/Orderly/components/UserBoard/index.tsx b/src/pages/Orderly/components/UserBoard/index.tsx index 0c130d04b..3e38d4040 100644 --- a/src/pages/Orderly/components/UserBoard/index.tsx +++ b/src/pages/Orderly/components/UserBoard/index.tsx @@ -2105,16 +2105,12 @@ export function AssetManagerModal( if (decimalPlaceLimit === undefined) return; function limitDecimalPlaces(e: any) { - // 获取用户输入的数字 const value = e.target.value; - // 判断是否超过了8位小数 - if ( value.includes('.') && value.split('.')[1].length > decimalPlaceLimit ) { - // 截取前8位小数 e.target.value = value.slice( 0, value.indexOf('.') + decimalPlaceLimit + 1 diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index e1a09d3f7..602865d30 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1,4 +1,5 @@ import React, { useState, useContext, useEffect, useRef } from 'react'; + import { ReturnIcon, AddIcon, @@ -34,7 +35,12 @@ import { ftGetTokenMetadata, } from '../../services/ft-contract'; import { getTokenPriceList } from '../../services/indexer'; -import { getBoostTokenPrices, Seed, FarmBoost } from '../../services/farm'; +import { + getBoostTokenPrices, + Seed, + FarmBoost, + classificationOfCoins, +} from '../../services/farm'; import { useTokenBalances, useDepositableBalance } from '../../state/token'; import Loading from '~components/layout/Loading'; import { @@ -43,6 +49,7 @@ import { create_pool, PoolInfo, get_pool_marketdepth, + regularizedPoint, } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; import { @@ -87,10 +94,20 @@ import { OutLinkIcon } from '../../components/icon/Common'; import { REF_FI_POOL_ACTIVE_TAB } from '../pools/LiquidityPage'; import getConfig from '../../services/config'; import QuestionMark from '../../components/farm/QuestionMark'; -const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); - +import ReactSlider from 'react-slider'; import Big from 'big.js'; import { SelectTokenDCL } from '../../components/forms/SelectToken'; +import { SliderCurColor } from '~components/icon/Info'; +import { values } from 'lodash'; +import { PipValues } from '../../../public/charting_library/charting_library'; +import { + CurveShape, + SpotShape, + BidAskShape, +} from '../Orderly/components/Common/Icons'; +import { SLOT_NUMBER } from '~services/near'; + +export type LiquidityShape = 'Spot' | 'Curve' | 'BidAsk'; export default function AddYourLiquidityPageV3() { const [tokenX, setTokenX] = useState(null); @@ -115,17 +132,19 @@ export default function AddYourLiquidityPageV3() { const [feeBoxStatus, setFeeBoxStatus] = useState(true); const [buttonSort, setButtonSort] = useState(false); const [selectHover, setSelectHover] = useState(false); - const [viewPoolHover, setViewPoolHover] = useState(false); const [topPairs, setTopPairs] = useState([]); const [seed_list, set_seed_list] = useState(); const [related_seeds, set_related_seeds] = useState([]); + const [hoverFeeBox, setHoverFeeBox] = useState(false); + + const [liquidityShape, setLiquidityShape] = useState('Spot'); + // callBack handle useAddAndRemoveUrlHandle(); const history = useHistory(); const triTokenIds = useTriTokenIdsOnRef(); const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); const triTokens = useTriTokens(); - const balances = useTokenBalances(); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; const nearBalance = useDepositableBalance('NEAR'); @@ -231,7 +250,6 @@ export default function AddYourLiquidityPageV3() { if (!refTokens || !triTokens || !triTokenIds) return ; const allTokens = getAllTokens(refTokens, triTokens); - const nearSwapTokens = allTokens.filter((token) => token.onRef); async function get_seeds() { const seeds = await get_all_seeds(); set_seed_list(seeds); @@ -660,37 +678,34 @@ export default function AddYourLiquidityPageV3() { const mobileDevice = isMobile(); return ( <> -
-
- -
- - - -
{ + history.goBack(); }} - >
-
+ >
-
+ +
+ + + +
+ +
+ {/*
-
-
- {/* left area */} -
-
- { - if (tokenY && tokenY.id == token.id) return; - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - if (tokenX && tokenX.id == token.id) return; - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - className="pt-0 absolute top-8 outline-none left-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - - -
- } - /> -
{ - setViewPoolHover(true); - }} - onMouseLeave={() => { - setViewPoolHover(false); - }} - onClick={goPoolsPage} - className={`flex items-center justify-center bg-viewPoolBgColor rounded-md px-3.5 py-1 mb-3 cursor-pointer ${ - viewPoolHover ? 'text-white' : 'text-primaryText' - }`} - > - - - - -
+
*/} +
+ {/* left area */} + + {/* right area */} + {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + +
+
+
+
-
-
- - {tokenX ? ( -
- - - {toRealSymbol(tokenX.symbol)} - -
- ) : ( - <> - {} - - - )} - -
- } - onSelect={(token) => { - if (tokenY && tokenY.id == token.id) return; - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); + + { + if (tokenY && tokenY.id == token.id) return; + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + if (tokenX && tokenX.id == token.id) return; + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } }} - balances={balances} - /> -
+ onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ + + + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + +
{ - if (!mobileDevice) { - setButtonHover(true); - } - }} + className="w-7 h-7 rounded-lg relative bg-v3SwapGray z-50 bg-opacity-10 hover:bg-opacity-30 text-primaryText hover:text-white frcc " onMouseLeave={() => { - if (!mobileDevice) { - setButtonHover(false); - } - }} - onTouchStart={() => { - if (mobileDevice) { - setButtonHover(true); - } + setHoverFeeBox(false); }} - onTouchEnd={() => { - if (mobileDevice) { - setButtonHover(false); - } + onMouseEnter={() => { + setHoverFeeBox(true); }} - onClick={switchButtonSort} - className="flex flex-col items-center justify-center border-2 border-switchIconBorderColor w-6 h-6 rounded-lg mx-2 cursor-pointer box-content bg-switchIconBgColor" - > - - -
-
- - {tokenY ? ( -
- - - {toRealSymbol(tokenY.symbol)} - -
- ) : ( - <> - - - )} - -
- } - onSelect={(token: TokenMetadata) => { - if (tokenX && tokenX.id == token.id) return; - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - balances={balances} - /> -
-
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = currentPools && !currentPools[fee]; - return ( +
+ +
+ {hoverFeeBox && ( +
{ - switchSelectedFee(fee); + className="rounded-xl right-0 top-3 px-4 py-3 xs:px-2 md:px-2" + style={{ + border: '1.2px solid rgba(145, 162, 174, 0.2)', + width: '418px', + background: + 'linear-gradient(rgb(34, 47, 55) 0%, rgb(25, 34, 41) 100%)', }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} > - + +
+
- {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > 0 ? ( - {displayTvl(currentPools[fee].tvl)} - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} + {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
- ); - })} +
+ )}
-
- - - - - - - -
- {/* right area */} - {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - + +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1) setLiquidityShape('Curve'); + else setLiquidityShape('BidAsk'); + }} + > + + + + {index === 0 && ( + + )} + {index === 1 && ( + + )} + + {index === 2 && ( + + )} + +
+ ); + } + )} +
+ +
+ +
+ {currentSelectedPool && currentSelectedPool.pool_id && ( + - ) : null} + > + )} + +
+ + +
@@ -1070,6 +1062,37 @@ export default function AddYourLiquidityPageV3() { ); } + +function Slider({ + min, + max, + step, + values, + setValues, + invert, +}: { + min: number; + max: number; + step: number; + values: any; + setValues: any; + invert: boolean; +}) { + return ( + + ); +} + function CreatePoolComponent({ currentSelectedPool, tokenX, @@ -1153,8 +1176,7 @@ function CreatePoolComponent({ const mobileDevice = isMobile(); return (
(0); let [rightPoint, setRightPoint] = useState(0); + + let [targetPoint, setTargetPoint] = useState(0); + const [leftInputStatus, setLeftInputStatus] = useState(false); const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + const [currentPoint, setCurrentPoint] = useState(); + const [chartLoading, setChartLoading] = useState(false); const [noDataForChart, setNoDataForChart] = useState(false); const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = useState(false); + + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + + const [radius, setRadius] = useState(5); + + const [binNumber, setBinNumber] = useState(10); + const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< number | string >(); @@ -1317,9 +1356,17 @@ function AddLiquidityComponent({ const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< Record >({}); + + console.log('quickOptionsMapPoint: ', quickOptionsMapPoint); + const { globalState } = useContext(WalletContext); const [timer, setTimer] = useState(null); const [depthData, setDepthData] = useState(null); + + const [pointChangeDirection, setPointChangeDirection] = useState< + 'right' | 'left' + >(); + const [displayedSeedIndex, setDisplayedSeedIndex] = useState(0); const isSignedIn = globalState.isSignedIn; const intl = useIntl(); @@ -1330,19 +1377,24 @@ function AddLiquidityComponent({ const token_y_decimals = tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; // init + + // quick options useEffect(() => { - const { current_point, point_delta } = currentSelectedPool; + const { current_point, point_delta, fee } = currentSelectedPool; // 5, 10 20 50 + + const BIN_SIZE = point_delta * SLOT_NUMBER; + const optionsMapPoints_temp = {}; quickOptions.forEach((p: number) => { const { left_p, right_p } = getPointByCondition(p); optionsMapPoints_temp[p] = { left_p, right_p }; - if (p == 10) { - leftPoint = left_p; - rightPoint = right_p; - setLeftPoint(left_p); - setRightPoint(right_p); - } + // if (p == 10) { + // leftPoint = left_p; + // rightPoint = right_p; + // setLeftPoint(left_p); + // setRightPoint(right_p); + // } }); // full const l_p_temp = _.max([current_point - 400000, -800000]); @@ -1352,9 +1404,24 @@ function AddLiquidityComponent({ if (r_p - l_p >= POINTRIGHTRANGE) { l_p = l_p + point_delta; } + + r_p = Math.floor((r_p - l_p) / BIN_SIZE) * BIN_SIZE + l_p; + optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; + + let { left_p, right_p } = optionsMapPoints_temp['10']; + + left_p = Math.floor((left_p - l_p) / BIN_SIZE) * BIN_SIZE + l_p; + + right_p = left_p + BIN_SIZE * binNumber; + + setLeftPoint(left_p); + setRightPoint(right_p); + // set setCurrentPoint(current_point); + + setTargetPoint(current_point); setQuickOptionsMapPoint(optionsMapPoints_temp); setCurrentCheckedQuickOption(10); // show chart data @@ -1366,6 +1433,7 @@ function AddLiquidityComponent({ }, 1000); setTimer(timer_latest); }, [currentSelectedPool]); + console.log('currentSelectedPool: ', currentSelectedPool); useEffect(() => { pointChange({ leftPoint, rightPoint, currentPoint }); const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { @@ -1475,6 +1543,7 @@ function AddLiquidityComponent({ return '$-'; } } + function getCurrentPrice_real_decimal() { if (currentSelectedPool && currentSelectedPool.pool_id) { const { current_point, token_x, token_y } = currentSelectedPool; @@ -1535,6 +1604,25 @@ function AddLiquidityComponent({ setRightPoint(appropriate_r_point); } } + if (targetCustomPrice) { + if (!tokenSort) { + targetCustomPrice = new BigNumber(1) + .dividedBy(targetCustomPrice) + .toFixed(); + } + const c_point = getPointByPrice( + point_delta, + targetCustomPrice, + decimalRate + ); + setTargetCustomPrice(''); + setTargetPoint(c_point); + // if (c_point - leftPoint >= POINTRIGHTRANGE ) { + // const appropriate_l_point = c_point - POINTRIGHTRANGE + point_delta; + // setLeftPoint(appropriate_l_point); + // } + } + if (rightCustomPrice) { if (!tokenSort) { rightCustomPrice = new BigNumber(1) @@ -1554,6 +1642,7 @@ function AddLiquidityComponent({ } } } + function getButtonStatus() { const condition1 = currentSelectedPool?.pool_id; let condition2; @@ -1641,6 +1730,26 @@ function AddLiquidityComponent({ return ''; } } + + function getTargetPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(targetPoint, decimalRate); + if (tokenX.id == token_y) { + price = new BigNumber(1).dividedBy(price).toFixed(); + } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function addLiquidity() { setAddLiquidityButtonLoading(true); const { pool_id } = currentSelectedPool; @@ -1658,6 +1767,7 @@ function AddLiquidityComponent({ token_y: tokenSort ? tokenY : tokenX, }); } + function quickChangePoint(item: string | number) { if (currentCheckedQuickOption == item) return; const { point_delta, current_point } = currentSelectedPool; @@ -1721,34 +1831,700 @@ function AddLiquidityComponent({ } } } - function getButtonText() { - let txt: any = ( - + + function get_related_seeds() { + const temp_seeds = seeds.map((seed: Seed) => { + const [contractId, temp_mft_id] = seed.seed_id.split('@'); + const [fixRange, pool_id, left_point, right_point] = + temp_mft_id.split('&'); + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price_left = getPriceByPoint(+left_point, decimalRate); + const price_right = getPriceByPoint(+right_point, decimalRate); + return { + seed, + price_left, + price_right, + }; + }); + const tokenSort = tokenX.id == token_x ? true : false; + const targetSeed = temp_seeds[displayedSeedIndex]; + const { price_left, price_right } = targetSeed; + let price_left_final = price_left; + let price_right_final = price_right; + + if (!tokenSort) { + price_left_final = new BigNumber(1).dividedBy(price_right).toFixed(); + price_right_final = new BigNumber(1).dividedBy(price_left).toFixed(); + } + const display_price_left = displayNumberToAppropriateDecimals( + price_left_final.toString() ); - if (invalidRange) { - txt = ( - - ); - } else if ( - (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || - (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || - (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) - ) { - txt = ( - - ); + const display_price_right = displayNumberToAppropriateDecimals( + price_right_final.toString() + ); + return ( +
+ + 1 {tokenX.symbol} ={' '} + +
+ { + setDisplayedSeedIndex(displayedSeedIndex - 1); + }} + className={`transform rotate-90 mr-1.5 text-primaryText cursor-pointer ${ + seeds.length > 1 && displayedSeedIndex > 0 ? '' : 'hidden' + }`} + > + { + if (!tokenSort) { + leftCustomPrice = price_right_final; + rightCustomPrice = price_left_final; + } else { + leftCustomPrice = price_left_final; + rightCustomPrice = price_right_final; + } + setLeftCustomPrice(leftCustomPrice); + setRightCustomPrice(rightCustomPrice); + handlePriceToAppropriatePoint(); + }} + > + {display_price_left} ~ {display_price_right} + + {tokenY.symbol} + { + setDisplayedSeedIndex(displayedSeedIndex + 1); + }} + className={`transform -rotate-90 ml-1.5 text-primaryText cursor-pointer ${ + seeds.length > 1 && displayedSeedIndex < seeds.length - 1 + ? '' + : 'hidden' + }`} + > +
+
+ ); + } + function rewardRangeTip() { + const tip = intl.formatMessage({ id: 'reward_range_tip' }); + let result: string = `
${tip}
`; + return result; + } + const tokenSort = tokenX.id == currentSelectedPool.token_x; + + function getPair() { + if (tokenSort) { + return `(${tokenX.symbol}/${tokenY.symbol})`; + } else { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } + } + + return ( +
+
+
+
+ + + + {getPair()} + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + }} + > + + +
+
+ +
+
+
+ {/* target price */} + +
+ {priceRangeMode === 'by_range' && + quickOptionsMapPoint['full']?.['left_p'] && + quickOptionsMapPoint['full']?.['right_p'] && ( + { + setLeftPoint(values[0]); + setRightPoint(values[1]); + + if (values[0] === leftPoint) + setPointChangeDirection('left'); + else setPointChangeDirection('right'); + }} + > + )} +
+ +
+ + + + {/* {tokenSort ? ( */} + +
+ + {/* radius */} +
+ + + + { + setRadius(value); + }} + /> +
+ + {/* min price */} + +
+ + + + {tokenSort ? ( + + ) : ( + { + addOneSlot('r'); + }} + addOneSlot={() => { + reduceOneSlot('r'); + }} + handlePriceToAppropriatePoint={ + handlePriceToAppropriatePoint + } + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + > + )} +
+ + {/* max price */} +
+ + + + {tokenSort ? ( + { + reduceOneSlot('r'); + }} + addOneSlot={() => { + addOneSlot('r'); + }} + handlePriceToAppropriatePoint={ + handlePriceToAppropriatePoint + } + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + > + ) : ( + { + addOneSlot('l'); + }} + addOneSlot={() => { + reduceOneSlot('l'); + }} + handlePriceToAppropriatePoint={ + handlePriceToAppropriatePoint + } + customPrice={leftCustomPrice} + getPrice={getLeftPrice} + setCustomPrice={setLeftCustomPrice} + inputStatus={leftInputStatus} + setInputStatus={setLeftInputStatus} + > + )} +
+ + {/* bin number */} +
+ + + + { + setBinNumber(value); + }} + /> +
+
+ + {seeds.length ? ( +
+
+ +
+ + +
+
+ {get_related_seeds()} +
+ ) : null} +
+
+
+ + {/*
+ +
+ +
+
+
+ +
+ +
+
*/} + {/* {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} */} +
+ ); +} + +function AddLiquidityButton({ + currentSelectedPool, + tokenX, + tokenY, + tokenPriceList, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + pointChange, + onlyAddXToken, + onlyAddYToken, + invalidRange, + seeds, +}: { + currentSelectedPool: PoolInfo; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + tokenPriceList: Record; + tokenXAmount: string; + tokenYAmount: string; + tokenXBalanceFromNear: string; + tokenYBalanceFromNear: string; + pointChange: any; + onlyAddXToken: boolean; + onlyAddYToken: boolean; + invalidRange: boolean; + seeds: Seed[]; +}) { + let [leftPoint, setLeftPoint] = useState(0); + let [rightPoint, setRightPoint] = useState(0); + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [currentPoint, setCurrentPoint] = useState(); + const [chartLoading, setChartLoading] = useState(false); + const [noDataForChart, setNoDataForChart] = useState(false); + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< + number | string + >(); + const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); + const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< + Record + >({}); + const { globalState } = useContext(WalletContext); + const [timer, setTimer] = useState(null); + const [depthData, setDepthData] = useState(null); + const isSignedIn = globalState.isSignedIn; + const intl = useIntl(); + const chartDom = useRef(null); + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + // init + useEffect(() => { + const { current_point, point_delta } = currentSelectedPool; + // 5, 10 20 50 + const optionsMapPoints_temp = {}; + quickOptions.forEach((p: number) => { + const { left_p, right_p } = getPointByCondition(p); + optionsMapPoints_temp[p] = { left_p, right_p }; + if (p == 10) { + leftPoint = left_p; + rightPoint = right_p; + setLeftPoint(left_p); + setRightPoint(right_p); + } + }); + // full + const l_p_temp = _.max([current_point - 400000, -800000]); + const r_p_temp = _.min([current_point + 400000, 800000]); + let l_p = Math.floor(l_p_temp / point_delta) * point_delta; + let r_p = Math.floor(r_p_temp / point_delta) * point_delta; + if (r_p - l_p >= POINTRIGHTRANGE) { + l_p = l_p + point_delta; + } + optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; + // set + setCurrentPoint(current_point); + setQuickOptionsMapPoint(optionsMapPoints_temp); + setCurrentCheckedQuickOption(10); + // show chart data + setChartLoading(true); + setNoDataForChart(false); + clearTimeout(timer); + const timer_latest = setTimeout(() => { + getChartData(); + }, 1000); + setTimer(timer_latest); + }, [currentSelectedPool]); + useEffect(() => { + pointChange({ leftPoint, rightPoint, currentPoint }); + const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { + const { left_p, right_p } = quickOptionsMapPoint[key]; + if (left_p == leftPoint && right_p == rightPoint) { + return key; + } + }); + if (targetKey) { + setCurrentCheckedQuickOption(targetKey); + } else { + setCurrentCheckedQuickOption(undefined); + } + if (depthData) { + drawChartData({ + depthData, + left_point: leftPoint, + right_point: rightPoint, + token_x_decimals, + token_y_decimals, + chartDom, + sort: tokenX.id == token_x, + space_x: 5, + }); + } + }, [leftPoint, rightPoint]); + async function getChartData() { + const depthData = await get_pool_marketdepth(currentSelectedPool.pool_id); + const length = drawChartData({ + depthData, + left_point: leftPoint, + right_point: rightPoint, + token_x_decimals, + token_y_decimals, + chartDom, + sort: tokenX.id == token_x, + space_x: 5, + }); + if (length == 0) { + setNoDataForChart(true); + } else { + setDepthData(depthData); + } + setChartLoading(false); + } + function getPointByCondition(p: number) { + const { point_delta, token_x } = currentSelectedPool; + const c_price = getCurrentPrice_real_decimal(); + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const tokenSort = tokenX.id == token_x ? true : false; + const reduce_p = 1 - p / 100; + const add_p = 1 + p / 100; + if (tokenSort) { + // -10% example + const l_price = new BigNumber(c_price).multipliedBy(reduce_p).toFixed(); + const point_l = getPointByPrice( + point_delta, + l_price.toString(), + decimalRate + ); + // +10% example + const r_price = new BigNumber(c_price).multipliedBy(add_p).toFixed(); + const point_r = getPointByPrice( + point_delta, + r_price.toString(), + decimalRate + ); + return { left_p: point_l, right_p: point_r }; + } else { + const c_price2 = new BigNumber(1).dividedBy(c_price).toFixed(); + // +10% example + const priceAdd = new BigNumber(c_price2).multipliedBy(add_p); + const l_price_2 = new BigNumber(1).dividedBy(priceAdd).toFixed(); + const point_l_2 = getPointByPrice( + point_delta, + l_price_2.toString(), + decimalRate + ); + // -10% example + const priceDivide = new BigNumber(c_price2).multipliedBy(reduce_p); + const r_price_2 = new BigNumber(1).dividedBy(priceDivide).toFixed(); + const point_r_2 = getPointByPrice( + point_delta, + r_price_2.toString(), + decimalRate + ); + return { left_p: point_l_2, right_p: point_r_2 }; + } + } + + function getCurrentPrice_real_decimal() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { current_point, token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(current_point, decimalRate); + return price; + } + return 0; + } + + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + if (tokenSort) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + } else if (onlyAddYToken) { + if (tokenSort) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + return !(condition1 && condition2); + } + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + + function addLiquidity() { + setAddLiquidityButtonLoading(true); + const { pool_id } = currentSelectedPool; + add_liquidity({ + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: tokenSort + ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + amount_y: tokenSort + ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + token_x: tokenSort ? tokenX : tokenY, + token_y: tokenSort ? tokenY : tokenX, + }); + } + + function getButtonText() { + let txt: any = ( + + ); + if (invalidRange) { + txt = ( + + ); + } else if ( + (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || + (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || + (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) + ) { + txt = ( + + ); } else if ( !onlyAddXToken && !onlyAddYToken && @@ -1787,338 +2563,13 @@ function AddLiquidityComponent({ } return txt; } - function getPriceTip() { - const tip = intl.formatMessage({ id: 'price_on_slot_tip' }); - let result: string = `
${tip}
`; - return result; - } - function get_related_seeds() { - const temp_seeds = seeds.map((seed: Seed) => { - const [contractId, temp_mft_id] = seed.seed_id.split('@'); - const [fixRange, pool_id, left_point, right_point] = - temp_mft_id.split('&'); - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price_left = getPriceByPoint(+left_point, decimalRate); - const price_right = getPriceByPoint(+right_point, decimalRate); - return { - seed, - price_left, - price_right, - }; - }); - const tokenSort = tokenX.id == token_x ? true : false; - const targetSeed = temp_seeds[displayedSeedIndex]; - const { price_left, price_right } = targetSeed; - let price_left_final = price_left; - let price_right_final = price_right; - if (!tokenSort) { - price_left_final = new BigNumber(1).dividedBy(price_right).toFixed(); - price_right_final = new BigNumber(1).dividedBy(price_left).toFixed(); - } - const display_price_left = displayNumberToAppropriateDecimals( - price_left_final.toString() - ); - const display_price_right = displayNumberToAppropriateDecimals( - price_right_final.toString() - ); - return ( -
- - 1 {tokenX.symbol} ={' '} - -
- { - setDisplayedSeedIndex(displayedSeedIndex - 1); - }} - className={`transform rotate-90 mr-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex > 0 ? '' : 'hidden' - }`} - > - { - if (!tokenSort) { - leftCustomPrice = price_right_final; - rightCustomPrice = price_left_final; - } else { - leftCustomPrice = price_left_final; - rightCustomPrice = price_right_final; - } - setLeftCustomPrice(leftCustomPrice); - setRightCustomPrice(rightCustomPrice); - handlePriceToAppropriatePoint(); - }} - > - {display_price_left} ~ {display_price_right} - - {tokenY.symbol} - { - setDisplayedSeedIndex(displayedSeedIndex + 1); - }} - className={`transform -rotate-90 ml-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex < seeds.length - 1 - ? '' - : 'hidden' - }`} - > -
-
- ); - } - function rewardRangeTip() { - const tip = intl.formatMessage({ id: 'reward_range_tip' }); - let result: string = `
${tip}
`; - return result; - } const tokenSort = tokenX.id == currentSelectedPool.token_x; const isAddLiquidityDisabled = getButtonStatus(); - const mobileDevice = isMobile(); return (
-
- -
-
-
- - - -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue()}) - - - - {getCurrentPrice()} {toRealSymbol(tokenY?.symbol)} - -
-
- {/* range chart area */} -
- - - - - - - - {chartLoading ? ( - - ) : null} - {noDataForChart ? : null} -
- {/* input range area */} -
-
-
- - - - {tokenSort ? ( - { - reduceOneSlot('l'); - }} - addOneSlot={() => { - addOneSlot('l'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - > - ) : ( - { - addOneSlot('r'); - }} - addOneSlot={() => { - reduceOneSlot('r'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - )} -
-
- - - - {tokenSort ? ( - { - reduceOneSlot('r'); - }} - addOneSlot={() => { - addOneSlot('r'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - ) : ( - { - addOneSlot('l'); - }} - addOneSlot={() => { - reduceOneSlot('l'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - > - )} -
-
-
-
- - -
- {quickOptions.map((item: number, index) => { - return ( -
{ - quickChangePoint(item); - }} - key={index} - className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ - currentCheckedQuickOption == item - ? 'bg-v3PurpleColor border-v3PurpleColor text-white' - : 'border-v3GreyColor text-v3LightGreyColor' - }`} - > - ± {item}% -
- ); - })} -
{ - quickChangePoint('full'); - }} - className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ - currentCheckedQuickOption == 'full' - ? 'bg-v3PurpleColor border-v3PurpleColor text-white' - : 'border-v3GreyColor text-v3LightGreyColor' - }`} - > - -
-
- {seeds.length ? ( -
-
- -
- - -
-
- {get_related_seeds()} -
- ) : null} -
-
-
- -
- -
-
-
- -
- -
-
{isSignedIn ? (
-
+ {/*
{quickOptions.map((item: number, index) => { return ( @@ -2234,7 +2685,7 @@ function NoDataComponent(props: any) { >
-
+
*/}
-
+ {/*
{ reduceOneSlot('r'); }} > -
+
*/} { handlePriceToAppropriatePoint(); setInputStatus(false); @@ -2297,17 +2748,65 @@ function PointInputComponent({ setCustomPrice(inputPrice); }} /> -
{ addOneSlot('r'); }} > -
+
*/} +
+ ); +} + +function IntegerInputComponent({ value, onChange }: any) { + const removeLeadingZeros = (s: string) => { + const oldLen = s.length; + s = s.replace(/^0+/, ''); + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + + onChange(val); + }; + + return ( +
+ {/*
{ + reduceOneSlot('r'); + }} + > + +
*/} + { + handleChange(target.value); + }} + /> + {/*
{ + addOneSlot('r'); + }} + > + +
*/}
); } + function OneSide({ show }: { show: boolean }) { return (
=> { const paramString = genUrlParams(props); - return await fetch(config.indexerUrl + `/get-top-bin-fee?${paramString}`, { + return await fetch(config.indexerUrl + `/get-dcl-points?${paramString}`, { method: 'GET', headers: { 'Content-type': 'application/json; charset=UTF-8' }, - }).then((res) => res.json()); + }) + .then((res) => res.json()) + .then((res) => { + return res.top_bin_fee_data; + }); }; export const getDCLAccountFee = async (props: { diff --git a/src/services/near.ts b/src/services/near.ts index d05ba3041..bc7a8ea97 100644 --- a/src/services/near.ts +++ b/src/services/near.ts @@ -200,6 +200,8 @@ export const REF_AIRDRAOP_CONTRACT_ID = config.REF_AIRDROP_CONTRACT_ID; export const REF_TOKEN_ID = config.REF_TOKEN_ID; const XREF_TOKEN_ID = getConfig().XREF_TOKEN_ID; +export const SLOT_NUMBER = 100; + export const LP_STORAGE_AMOUNT = '0.01'; export const ONE_YOCTO_NEAR = '0.000000000000000000000001'; diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 84328f8ba..6c6065ad3 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1,4 +1,7 @@ import { TokenMetadata, ftGetStorageBalance } from './ft-contract'; + +import { utils } from 'near-api-js'; + import { refSwapV3ViewFunction, REF_UNI_V3_SWAP_CONTRACT_ID, @@ -9,6 +12,7 @@ import { toNonDivisibleNumber, toReadableNumber, scientificNotationToString, + ONLY_ZEROS, } from '../utils/numbers'; import { getCurrentWallet } from '../utils/wallets-integration'; import _ from 'lodash'; @@ -312,6 +316,19 @@ export const v3Swap = async ({ ) ); } + + const neededStorage = await get_user_storage_detail({ size: 1 }); + if (!ONLY_ZEROS.test(neededStorage)) { + transactions.unshift({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), + ], + }); + } } if (SwapByOutput) { @@ -713,6 +730,58 @@ export const list_pools = async () => { (p: any) => !getConfig().DCL_POOL_BLACK_LIST.includes(p?.pool_id) ); }; + +export interface UserStorageDetail { + max_slots: number; + cur_order_slots: number; + cur_liquidity_slots: number; + locked_near: string; + storage_for_asset: string; + slot_price: string; + sponser_id: string; +} + +export const get_user_storage_detail = async ({ size }: { size: number }) => { + const user_id = window.selectorAccountId; + + let deposit_fee = new Big(0); + + if (!user_id) { + alert('sign in first'); + return; + } + + const detail: UserStorageDetail = await refSwapV3ViewFunction({ + methodName: 'get_user_storage_detail', + args: { + user_id, + }, + }); + + const { + max_slots, + cur_order_slots, + cur_liquidity_slots, + locked_near, + storage_for_asset, + slot_price, + } = detail; + + if (size + cur_liquidity_slots + cur_order_slots > max_slots) { + deposit_fee = deposit_fee.plus( + new Big(slot_price).mul( + size + cur_liquidity_slots + cur_order_slots - max_slots + ) + ); + + if (user_id !== detail.sponser_id) { + deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); + } + } + + return utils.format.formatNearAmount(deposit_fee.toFixed(0)); +}; + export const add_liquidity = async ({ pool_id, left_point, @@ -834,12 +903,15 @@ export const add_liquidity = async ({ ], }); } - const neededStorage = await checkTokenNeedsStorageDeposit_v3(); - if (neededStorage) { + const neededStorage = await get_user_storage_detail({ size: 1 }); + if (!ONLY_ZEROS.test(neededStorage)) { transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ - storageDepositAction({ amount: neededStorage, registrationOnly: true }), + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), ], }); } diff --git a/src/state/pool.ts b/src/state/pool.ts index 290f6d1e8..4053b35f6 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -1472,7 +1472,7 @@ export const useDCLTopBinFee = ({ getDCLTopBinFee({ pool_id, - number, + slot_number: number, }).then((res) => { if (!res || ONLY_ZEROS.test(res.total_liquidity)) return; const apr = diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index 94a6db918..26b0b6e6e 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -81,7 +81,7 @@ export const useAllPoolsV2 = () => { const topBinFee = await getDCLTopBinFee({ pool_id: p.pool_id, - number: 100, + slot_number: 100, }); if (!topBinFee || ONLY_ZEROS.test(topBinFee.total_liquidity)) { diff --git a/tailwind.config.js b/tailwind.config.js index 05ad8189e..eb87d7c24 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -87,6 +87,8 @@ module.exports = { darkGradientBg: 'linear-gradient(180deg, #1D2932 0%, #001320 100%)', orderGradient: 'linear-gradient(180deg, #455765 0%, #223746 100%)', orderGraidentHover: 'linear-gradient(180deg, #62798A 0%, #334B5E 100%)', + dclAddLiquidityFeeGradient: + 'linear-gradient(180deg, #26343E 0%, #1D2932 100%)', grayBoderGradient: 'linear-gradient(180deg, rgba(126, 138, 147, 0.2) 0%, rgba(3, 5, 5, 0.2) 100%)', grayBoderGradientReverse: diff --git a/yarn.lock b/yarn.lock index a5b9eb260..ca20e0e6e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3571,6 +3571,13 @@ "@types/history" "^4.7.11" "@types/react" "*" +"@types/react-slider@^1.3.1": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@types/react-slider/-/react-slider-1.3.1.tgz#a3816989eb4fc172e7df316930f242fec90690fc" + integrity sha512-4X2yK7RyCIy643YCFL+bc6XNmcnBtt8n88uuyihvcn5G7Lut23eNQU3q3KmwF7MWIfKfsW5NxCjw0SeDZRtgaA== + dependencies: + "@types/react" "*" + "@types/react-toastify@^4.1.0": version "4.1.0" resolved "https://registry.yarnpkg.com/@types/react-toastify/-/react-toastify-4.1.0.tgz#604e712855dd677916d5c66af595d3b590f5d95d" @@ -11443,6 +11450,11 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== +multi-range-slider-react@^2.0.3: + version "2.0.3" + resolved "https://registry.yarnpkg.com/multi-range-slider-react/-/multi-range-slider-react-2.0.3.tgz#f28233a23bb6711c854c5bf776493cb50b90c66f" + integrity sha512-mMjrVO9rP9uRMHq61MHNmiet5NxCMblrZwX9zEcqNBCguGXdSNlrhQfeluCy9ICpq7NOT9cKn+RhANrB5TBLkQ== + multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" @@ -13991,6 +14003,13 @@ react-scripts@^5.0.1: optionalDependencies: fsevents "^2.3.2" +react-slider@^2.0.5: + version "2.0.5" + resolved "https://registry.yarnpkg.com/react-slider/-/react-slider-2.0.5.tgz#23188e69e8103022370e9dff78cb851d71aef7e1" + integrity sha512-MU5gaK1yYCKnbDDN3CMiVcgkKZwMvdqK2xUEW7fFU37NAzRgS1FZbF9N7vP08E3XXNVhiuZnwVzUa3PYQAZIMg== + dependencies: + prop-types "^15.8.1" + react-smooth@^2.0.1: version "2.0.2" resolved "https://registry.yarnpkg.com/react-smooth/-/react-smooth-2.0.2.tgz#0ef24213628cb13bf4305194a050e1db4302a3a1" From 1fec0373c7fa7059235768400b38ec28d585bd14 Mon Sep 17 00:00:00 2001 From: Max Date: Fri, 9 Jun 2023 20:14:26 +0800 Subject: [PATCH 013/204] update package.json --- package.json | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/package.json b/package.json index f1fdf7307..151311e12 100644 --- a/package.json +++ b/package.json @@ -119,7 +119,6 @@ "lodash": "^4.17.21", "mathjs": "^9.3.0", "moment": "^2.29.1", - "multi-range-slider-react": "^2.0.3", "near-api-js": "0.44.2", "near-seed-phrase": "^0.2.0", "postcss": "^8.4.20", @@ -171,4 +170,4 @@ "staticPath": "public", "watcherGlob": "**" } -} +} \ No newline at end of file From 4f206bd56cb2ec29d4e4ff2998c8ce51a65a9969 Mon Sep 17 00:00:00 2001 From: Max Date: Sat, 10 Jun 2023 16:49:06 +0800 Subject: [PATCH 014/204] add batch_add_liquidity api --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 366 +++++++++++++++++- src/services/swapV3.ts | 380 +++++++++++++------ 2 files changed, 619 insertions(+), 127 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 2738b24a4..ebd01a453 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -50,6 +50,8 @@ import { PoolInfo, get_pool_marketdepth, regularizedPoint, + AddLiquidityInfo, + batch_add_liquidity, } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; import { @@ -82,7 +84,7 @@ import { toInternationalCurrencySystem, } from '~utils/numbers'; import { WalletContext } from '../../utils/wallets-integration'; -import _ from 'lodash'; +import _, { set } from 'lodash'; import BigNumber from 'bignumber.js'; import { toRealSymbol } from '../../utils/token'; import ReactTooltip from 'react-tooltip'; @@ -116,6 +118,10 @@ export default function AddYourLiquidityPageV3() { const [tokenXAmount, setTokenXAmount] = useState(''); const [tokenYAmount, setTokenYAmount] = useState(''); const [listPool, setListPool] = useState([]); + + const [binNumber, setBinNumber] = useState(10); + const [curPointInBinBoundry, setCurPointInBinBoundry] = useState(false); + const [buttonHover, setButtonHover] = useState(false); const [tokenPriceList, setTokenPriceList] = useState>({}); const [currentPools, setCurrentPools] = @@ -152,6 +158,11 @@ export default function AddYourLiquidityPageV3() { const intl = useIntl(); const intl_select = intl.formatMessage({ id: 'select_s' }); const OPEN_CREATE_POOL_ENTRY = false; + + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + useEffect(() => { getBoostTokenPrices().then(setTokenPriceList); get_list_pools(); @@ -214,6 +225,7 @@ export default function AddYourLiquidityPageV3() { getTopPairs(); } }, [listPool, tokenPriceList]); + async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -766,6 +778,7 @@ export default function AddYourLiquidityPageV3() { currentSelectedPool={currentSelectedPool} tokenX={tokenX} tokenY={tokenY} + setCurrentSelectedPool={setCurrentSelectedPool} tokenXAmount={tokenXAmount} tokenYAmount={tokenYAmount} tokenXBalanceFromNear={tokenXBalanceFromNear} @@ -776,6 +789,12 @@ export default function AddYourLiquidityPageV3() { invalidRange={invalidRange} pointChange={pointChange} seeds={related_seeds} + priceRangeMode={priceRangeMode} + setPriceRangeMode={setPriceRangeMode} + binNumber={binNumber} + setBinNumber={setBinNumber} + curPointInBinBoundry={curPointInBinBoundry} + setCurPointInBinBoundry={setCurPointInBinBoundry} > ) : null} @@ -1062,7 +1081,15 @@ export default function AddYourLiquidityPageV3() { invalidRange={invalidRange} pointChange={pointChange} seeds={related_seeds} - > + priceRangeMode={priceRangeMode} + setPriceRangeMode={setPriceRangeMode} + binNumber={binNumber} + setBinNumber={setBinNumber} + curPointInBinBoundry={curPointInBinBoundry} + setCurPointInBinBoundry={setCurPointInBinBoundry} + getTokenYAmountByCondition={getTokenYAmountByCondition} + getTokenXAmountByCondition={getTokenXAmountByCondition} + /> )}
@@ -1313,6 +1340,9 @@ function CreatePoolComponent({
); } + +export type PriceRangeModeType = 'by_range' | 'by_radius'; + function AddLiquidityComponent({ currentSelectedPool, tokenX, @@ -1327,6 +1357,13 @@ function AddLiquidityComponent({ onlyAddYToken, invalidRange, seeds, + priceRangeMode, + setPriceRangeMode, + binNumber, + setBinNumber, + curPointInBinBoundry, + setCurPointInBinBoundry, + setCurrentSelectedPool, }: { currentSelectedPool: PoolInfo; tokenX: TokenMetadata; @@ -1341,6 +1378,13 @@ function AddLiquidityComponent({ onlyAddYToken: boolean; invalidRange: boolean; seeds: Seed[]; + priceRangeMode: PriceRangeModeType; + setPriceRangeMode: (mode: PriceRangeModeType) => void; + binNumber: number; + setBinNumber: (binNumber: number) => void; + curPointInBinBoundry: boolean; + setCurPointInBinBoundry: (curPointInBinBoundry: boolean) => void; + setCurrentSelectedPool: (pool: PoolInfo) => void; }) { let [leftCustomPrice, setLeftCustomPrice] = useState(''); let [rightCustomPrice, setRightCustomPrice] = useState(''); @@ -1362,13 +1406,39 @@ function AddLiquidityComponent({ const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = useState(false); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const [radius, setRadius] = useState(5); - const [binNumber, setBinNumber] = useState(10); + useEffect(() => { + if (priceRangeMode === 'by_radius') { + setBinNumber(radius * 2); + } + }, [radius, priceRangeMode]); + + useEffect(() => { + if (!currentSelectedPool || !targetPoint) return; + if (priceRangeMode === 'by_range') { + setCurrentSelectedPool({ + ...currentSelectedPool, + current_point: currentPoint, + }); + } else { + setCurrentSelectedPool({ + ...currentSelectedPool, + current_point: targetPoint, + }); + } + }, [priceRangeMode, targetCustomPrice]); + console.log('currentSelectedPool: ', currentSelectedPool); + + useEffect(() => { + const { point_delta } = currentSelectedPool; + + if (tokenSort) { + setRightPoint(leftPoint + point_delta * SLOT_NUMBER * binNumber); + } else { + setLeftPoint(rightPoint - point_delta * SLOT_NUMBER * binNumber); + } + }, [binNumber]); const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< number | string @@ -1384,10 +1454,6 @@ function AddLiquidityComponent({ const [timer, setTimer] = useState(null); const [depthData, setDepthData] = useState(null); - const [pointChangeDirection, setPointChangeDirection] = useState< - 'right' | 'left' - >(); - const [displayedSeedIndex, setDisplayedSeedIndex] = useState(0); const isSignedIn = globalState.isSignedIn; const intl = useIntl(); @@ -1436,6 +1502,8 @@ function AddLiquidityComponent({ right_p = left_p + BIN_SIZE * binNumber; + setCurPointInBinBoundry((current_point - l_p) / BIN_SIZE === 0); + setLeftPoint(left_p); setRightPoint(right_p); @@ -2019,9 +2087,10 @@ function AddLiquidityComponent({ setLeftPoint(values[0]); setRightPoint(values[1]); - if (values[0] === leftPoint) - setPointChangeDirection('left'); - else setPointChangeDirection('right'); + setBinNumber( + (values[1] - values[0]) / + (currentSelectedPool.point_delta * SLOT_NUMBER) + ); }} > )} @@ -2083,6 +2152,7 @@ function AddLiquidityComponent({ handlePriceToAppropriatePoint={ handlePriceToAppropriatePoint } + disbaled={priceRangeMode === 'by_radius'} customPrice={leftCustomPrice} getPrice={getLeftPrice} setCustomPrice={setLeftCustomPrice} @@ -2097,6 +2167,7 @@ function AddLiquidityComponent({ addOneSlot={() => { reduceOneSlot('r'); }} + disbaled={priceRangeMode === 'by_radius'} handlePriceToAppropriatePoint={ handlePriceToAppropriatePoint } @@ -2122,6 +2193,7 @@ function AddLiquidityComponent({ reduceOneSlot={() => { reduceOneSlot('r'); }} + disbaled={priceRangeMode === 'by_radius'} addOneSlot={() => { addOneSlot('r'); }} @@ -2145,6 +2217,7 @@ function AddLiquidityComponent({ handlePriceToAppropriatePoint={ handlePriceToAppropriatePoint } + disbaled={priceRangeMode === 'by_radius'} customPrice={leftCustomPrice} getPrice={getLeftPrice} setCustomPrice={setLeftCustomPrice} @@ -2164,6 +2237,7 @@ function AddLiquidityComponent({ { setBinNumber(value); }} @@ -2200,6 +2274,15 @@ function AddLiquidityComponent({ ) : null}
+ +
+ *Only NEAR is needed in the price range you choose. +
{/*
void; + binNumber: number; + setBinNumber: (binNumber: number) => void; + curPointInBinBoundry: boolean; + setCurPointInBinBoundry: (curPointInBinBoundry: boolean) => void; + getTokenYAmountByCondition: any; + getTokenXAmountByCondition: any; }) { let [leftPoint, setLeftPoint] = useState(0); let [rightPoint, setRightPoint] = useState(0); @@ -2500,6 +2599,232 @@ function AddLiquidityButton({ : String(Number(balance) - 0.5); } + function addLiquidityCurve() { + const { pool_id, point_delta } = currentSelectedPool; + + const binSize = point_delta * SLOT_NUMBER; + + let liquidityInfo: AddLiquidityInfo[]; + + if (onlyAddYToken) { + // left side + const Y = tokenYAmount; + + const D = new Big(Y) + .mul(2) + .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) + .div(binSize); + + liquidityInfo = new Array(binNumber).map((item, index) => { + const height = new Big(index + 1).mul(D).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y, + }; + }); + } + + if (onlyAddXToken) { + const X = tokenSort ? tokenYAmount : tokenXAmount; + + const D = new Big(X) + .mul(2) + .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) + .div(binSize); + + liquidityInfo = new Array(binNumber).map((item, index) => { + const height = new Big(binNumber - index).mul(D).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }); + } + + if (!onlyAddXToken || !onlyAddYToken) { + if (curPointInBinBoundry) { + const leftBinSize = Math.floor(binNumber / 2); + const rightBinSize = binNumber - leftBinSize; + + const X_left = tokenYAmount; + + const Y_right = tokenXAmount; + + const D_left = new Big(X_left) + .mul(2) + .div(new Big(leftBinSize).mul(new Big(leftBinSize).plus(1))) + .div(binSize); + + const D_right = new Big(Y_right) + .mul(2) + .div(new Big(rightBinSize).mul(new Big(rightBinSize).plus(1))) + .div(binSize); + + liquidityInfo = new Array(leftBinSize).map((item, index) => { + const height = new Big(index + 1).mul(D_left).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y, + }; + }); + + liquidityInfo = liquidityInfo.concat( + new Array(rightBinSize).map((item, index) => { + const height = new Big(rightBinSize - index) + .mul(D_right) + .toFixed(0); + const left_p = leftPoint + binSize * (leftBinSize + index); + const right_p = leftPoint + binSize * (leftBinSize + index + 1); + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }) + ); + } else { + const leftBinSize = Math.floor(binNumber / 2) + 1; + const rightBinSize = binNumber - leftBinSize - 1; + + const D_left = new Big(tokenYAmount).div( + new Big( + new Big(((leftBinSize - 1) * leftBinSize) / 2).mul(binSize) + ).plus(new Big(leftBinSize).mul(currentPoint - leftPoint)) + ); + + if (leftBinSize - 1 > 0) { + const addLpList = new Array(leftBinSize - 1).map((item, index) => { + const height = new Big(index + 1).mul(D_left).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y: amount_y, + }; + }); + liquidityInfo = (liquidityInfo || []).concat(addLpList); + } + + // middle bin + + const tokenYMiddleAmount = new Big(currentPoint) + .minus(leftPoint + Number(leftBinSize - 1) * binSize) + .mul(D_left) + .mul(leftBinSize) + .toFixed(0); + + const tokenXMiddleAmount = getTokenXAmountByCondition({ + amount: tokenYMiddleAmount, + leftPoint: leftPoint + Number(leftBinSize - 1) * binSize, + rightPoint: leftPoint + leftBinSize * binSize, + currentPoint, + }); + + liquidityInfo = (liquidityInfo || []).concat([ + { + pool_id, + left_point: leftPoint + Number(leftBinSize - 1) * binSize, + right_point: leftPoint + leftBinSize * binSize, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXMiddleAmount), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYMiddleAmount), + }, + ]); + + // right side + + if (rightBinSize > 1) { + const newtokenXAmount = new Big(tokenXAmount) + .minus(tokenXMiddleAmount) + .toFixed(0); + + const D_right = new Big(newtokenXAmount) + .mul(2) + .div(new Big(((rightBinSize - 1) * rightBinSize) / 2).mul(binSize)); + + const addLpList = new Array(rightBinSize).map((item, index) => { + const height = new Big(rightBinSize - index) + .mul(D_right) + .toFixed(0); + const left_p = leftPoint + leftBinSize + index * binSize; + const right_p = left_p + binSize; + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }); + liquidityInfo = (liquidityInfo || []).concat(addLpList); + } + + // + } + } + + return batch_add_liquidity({ + liquidityInfo, + token_x: tokenX, + token_y: tokenY, + }); + } + function addLiquidity() { setAddLiquidityButtonLoading(true); const { pool_id } = currentSelectedPool; @@ -2742,6 +3067,7 @@ function PointInputComponent({ setCustomPrice, inputStatus, setInputStatus, + disbaled, }: any) { return (
@@ -2757,11 +3083,14 @@ function PointInputComponent({ type="number" placeholder="0.0" step="any" - className="text-base font-gothamBold mx-2 text-left" + className={`text-base font-gothamBold mx-2 text-left ${ + disbaled ? 'text-primaryText' : 'text-white' + }`} onBlur={() => { handlePriceToAppropriatePoint(); setInputStatus(false); }} + disabled={disbaled} value={inputStatus ? customPrice : getPrice()} onChange={({ target }) => { setInputStatus(true); @@ -2781,7 +3110,7 @@ function PointInputComponent({ ); } -function IntegerInputComponent({ value, onChange }: any) { +function IntegerInputComponent({ value, onChange, disabled }: any) { const removeLeadingZeros = (s: string) => { const oldLen = s.length; s = s.replace(/^0+/, ''); @@ -2810,7 +3139,10 @@ function IntegerInputComponent({ value, onChange }: any) {
*/} { handleChange(target.value); diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 6c6065ad3..60c6af94a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -317,144 +317,146 @@ export const v3Swap = async ({ ); } - const neededStorage = await get_user_storage_detail({ size: 1 }); - if (!ONLY_ZEROS.test(neededStorage)) { - transactions.unshift({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + if (SwapByOutput) { + const pool_ids = Swap.pool_ids; + const output_token = tokenB.id; + const output_amount = toNonDivisibleNumber(tokenB.decimals, amountB); + const msg = JSON.stringify({ + SwapByOutput: { + pool_ids, + output_token, + output_amount, + }, + }); + const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { + throw new Error(`${tokenB.id} doesn't exist.`); + }); + + if (tokenRegistered === null) { + transactions.push({ + receiverId: tokenB.id, + functionCalls: [registerAccountOnToken()], + }); + } + + transactions.push({ + receiverId: tokenA.id, functionCalls: [ - storageDepositAction({ - amount: neededStorage, - registrationOnly: true, - }), + { + methodName: 'ft_transfer_call', + args: { + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: toNonDivisibleNumber(tokenA.decimals, amountA), + msg, + }, + gas: '180000000000000', + amount: ONE_YOCTO_NEAR, + }, ], }); } - } - if (SwapByOutput) { - const pool_ids = Swap.pool_ids; - const output_token = tokenB.id; - const output_amount = toNonDivisibleNumber(tokenB.decimals, amountB); - const msg = JSON.stringify({ - SwapByOutput: { - pool_ids, - output_token, - output_amount, - }, - }); - const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { - throw new Error(`${tokenB.id} doesn't exist.`); - }); + if (LimitOrderWithSwap) { + const pool_id = LimitOrderWithSwap.pool_id; - if (tokenRegistered === null) { - transactions.push({ - receiverId: tokenB.id, - functionCalls: [registerAccountOnToken()], + const fee = Number(pool_id.split(V3_POOL_SPLITER)[2]); + + const buy_token = tokenB.id; + const point = priceToPoint({ + amountA, + amountB, + tokenA, + tokenB, + fee, }); - } - transactions.push({ - receiverId: tokenA.id, - functionCalls: [ - { - methodName: 'ft_transfer_call', - args: { - receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, - amount: toNonDivisibleNumber(tokenA.decimals, amountA), - msg, - }, - gas: '180000000000000', - amount: ONE_YOCTO_NEAR, - }, - ], - }); - } + const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { + throw new Error(`${tokenB.id} doesn't exist.`); + }); - if (LimitOrderWithSwap) { - const pool_id = LimitOrderWithSwap.pool_id; + if (tokenRegistered === null) { + transactions.push({ + receiverId: tokenB.id, + functionCalls: [registerAccountOnToken()], + }); + } - const fee = Number(pool_id.split(V3_POOL_SPLITER)[2]); + const DCLRegistered = await swapV3GetStorageBalance(tokenB.id).catch( + () => { + throw new Error(`${tokenB.id} doesn't exist.`); + } + ); - const buy_token = tokenB.id; - const point = priceToPoint({ - amountA, - amountB, - tokenA, - tokenB, - fee, - }); + if (DCLRegistered === null) { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'storage_deposit', + args: { + registration_only: true, + account_id: getCurrentWallet()?.wallet?.getAccountId(), + }, + gas: '30000000000000', + amount: '0.5', + }, + ], + }); + } - const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { - throw new Error(`${tokenB.id} doesn't exist.`); - }); + const new_point = + pool_id.split(V3_POOL_SPLITER)[0] === tokenA.id ? point : -point; - if (tokenRegistered === null) { - transactions.push({ - receiverId: tokenB.id, - functionCalls: [registerAccountOnToken()], + const msg = JSON.stringify({ + LimitOrderWithSwap: { + client_id: '', + pool_id, + buy_token, + point: new_point, + }, }); - } - const DCLRegistered = await swapV3GetStorageBalance(tokenB.id).catch(() => { - throw new Error(`${tokenB.id} doesn't exist.`); - }); - - if (DCLRegistered === null) { transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + receiverId: tokenA.id, functionCalls: [ { - methodName: 'storage_deposit', + methodName: 'ft_transfer_call', args: { - registration_only: true, - account_id: getCurrentWallet()?.wallet?.getAccountId(), + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: toNonDivisibleNumber(tokenA.decimals, amountA), + msg, }, - gas: '30000000000000', - amount: '0.5', + gas: '250000000000000', + amount: ONE_YOCTO_NEAR, }, ], }); } - const new_point = - pool_id.split(V3_POOL_SPLITER)[0] === tokenA.id ? point : -point; - - const msg = JSON.stringify({ - LimitOrderWithSwap: { - client_id: '', - pool_id, - buy_token, - point: new_point, - }, - }); - - transactions.push({ - receiverId: tokenA.id, - functionCalls: [ - { - methodName: 'ft_transfer_call', - args: { - receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, - amount: toNonDivisibleNumber(tokenA.decimals, amountA), - msg, - }, - gas: '250000000000000', - amount: ONE_YOCTO_NEAR, - }, - ], - }); - } + if (tokenA.id === WRAP_NEAR_CONTRACT_ID && tokenA.symbol == 'NEAR') { + transactions.unshift(nearDepositTransaction(amountA)); + } - if (tokenA.id === WRAP_NEAR_CONTRACT_ID && tokenA.symbol == 'NEAR') { - transactions.unshift(nearDepositTransaction(amountA)); - } + if (tokenA.id === WRAP_NEAR_CONTRACT_ID) { + const registered = await ftGetStorageBalance(WRAP_NEAR_CONTRACT_ID); + if (registered === null) { + transactions.unshift({ + receiverId: WRAP_NEAR_CONTRACT_ID, + functionCalls: [registerAccountOnToken()], + }); + } + } - if (tokenA.id === WRAP_NEAR_CONTRACT_ID) { - const registered = await ftGetStorageBalance(WRAP_NEAR_CONTRACT_ID); - if (registered === null) { + const neededStorage = await get_user_storage_detail({ size: 1 }); + if (!ONLY_ZEROS.test(neededStorage)) { transactions.unshift({ - receiverId: WRAP_NEAR_CONTRACT_ID, - functionCalls: [registerAccountOnToken()], + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), + ], }); } } @@ -917,6 +919,164 @@ export const add_liquidity = async ({ } return executeMultipleTransactions(transactions); }; + +export interface AddLiquidityInfo { + pool_id: string; + left_point: number; + right_point: number; + amount_x: string; + amount_y: string; +} + +export const batch_add_liquidity = async ({ + liquidityInfo, + token_x, + token_y, +}: { + liquidityInfo: AddLiquidityInfo[]; +} & { + token_x: TokenMetadata; + token_y: TokenMetadata; +}) => { + const transactions: Transaction[] = [ + { + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_add_liquidity', + args: { + add_liquidity_infos: liquidityInfo.map((info) => ({ + ...info, + min_amount_x: '0', + min_amount_y: '0', + })), + }, + gas: '150000000000000', + }, + ], + }, + ]; + + let amount_x; + let amount_y; + + amount_x = liquidityInfo.reduce( + (acc, info) => acc.plus(info.amount_x), + new Big(0) + ); + + amount_x = amount_x.toFixed(0); + + amount_y = liquidityInfo.reduce( + (acc, info) => acc.plus(info.amount_y), + new Big(0) + ); + + + amount_y = amount_y.toFixed(0); + + + if (+amount_x > 0) { + transactions.unshift({ + receiverId: token_x.id, + functionCalls: [ + { + methodName: 'ft_transfer_call', + args: { + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: amount_x, + msg: '"Deposit"', + }, + amount: ONE_YOCTO_NEAR, + gas: '150000000000000', + }, + ], + }); + } + if (+amount_y > 0) { + transactions.unshift({ + receiverId: token_y.id, + functionCalls: [ + { + methodName: 'ft_transfer_call', + args: { + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: amount_y, + msg: '"Deposit"', + }, + amount: ONE_YOCTO_NEAR, + gas: '150000000000000', + }, + ], + }); + } + if (+amount_x > 0 && token_x.id == WRAP_NEAR_CONTRACT_ID) { + transactions.unshift({ + receiverId: WRAP_NEAR_CONTRACT_ID, + functionCalls: [ + { + methodName: 'near_deposit', + args: {}, + gas: '50000000000000', + amount: toReadableNumber(token_x.decimals, amount_x), + }, + ], + }); + } + if (+amount_y > 0 && token_y.id == WRAP_NEAR_CONTRACT_ID) { + transactions.unshift({ + receiverId: WRAP_NEAR_CONTRACT_ID, + functionCalls: [ + { + methodName: 'near_deposit', + args: {}, + gas: '50000000000000', + amount: toReadableNumber(token_y.decimals, amount_y), + }, + ], + }); + } + const ftBalance_x = await ftGetStorageBalance(token_x.id); + if (!ftBalance_x) { + transactions.unshift({ + receiverId: token_x.id, + functionCalls: [ + storageDepositAction({ + registrationOnly: true, + amount: STORAGE_TO_REGISTER_WITH_MFT, + }), + ], + }); + } + const ftBalance_y = await ftGetStorageBalance(token_y.id); + if (!ftBalance_y) { + transactions.unshift({ + receiverId: token_y.id, + functionCalls: [ + storageDepositAction({ + registrationOnly: true, + amount: STORAGE_TO_REGISTER_WITH_MFT, + }), + ], + }); + } + const neededStorage = await get_user_storage_detail({ + size: liquidityInfo.length, + }); + if (!ONLY_ZEROS.test(neededStorage)) { + transactions.unshift({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), + ], + }); + } + return executeMultipleTransactions(transactions); +}; + export const append_liquidity = async ({ lpt_id, mft_id, From 79e8626cd2b63dc000b64eb7267bc203d980e76d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 10 Jun 2023 23:23:52 +0800 Subject: [PATCH 015/204] add user chart --- .../{DclPoolChart.tsx => DclChart.tsx} | 325 +++++++++++++----- src/components/d3Chart/interfaces.tsx | 1 + src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 10 +- src/services/indexer.ts | 19 +- 4 files changed, 267 insertions(+), 88 deletions(-) rename src/components/d3Chart/{DclPoolChart.tsx => DclChart.tsx} (81%) diff --git a/src/components/d3Chart/DclPoolChart.tsx b/src/components/d3Chart/DclChart.tsx similarity index 81% rename from src/components/d3Chart/DclPoolChart.tsx rename to src/components/d3Chart/DclChart.tsx index 78b2ceaa7..992b1ae06 100644 --- a/src/components/d3Chart/DclPoolChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -9,7 +9,7 @@ import { POINTLEFTRANGE, POINTRIGHTRANGE, } from '../../services/commonV3'; -import { getDclPoolPoints } from '../../services/indexer'; +import { getDclPoolPoints, getDclUserPoints } from '../../services/indexer'; import { sortBy } from 'lodash'; import { IChartData, @@ -25,13 +25,15 @@ import { } from './config'; import Big from 'big.js'; import * as d3 from 'd3'; -export default function DclPoolChart({ +import { useWalletSelector } from '../../context/WalletSelectorContext'; +export default function DclChart({ pool_id, leftPoint, rightPoint, setLeftPoint, setRightPoint, config, + chartType, }: { pool_id: string; leftPoint?: number; @@ -39,6 +41,7 @@ export default function DclPoolChart({ setLeftPoint?: Function; setRightPoint?: Function; config?: IPoolChartConfig; + chartType?: 'POOL' | 'USER'; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -68,7 +71,11 @@ export default function DclPoolChart({ ); const svgPaddingX = +(appearanceConfig.svgPaddingX || 5); const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + const whole_bars_background_padding = +( + appearanceConfig.whole_bars_background_padding || 20 + ); /** constant end */ + const { accountId } = useWalletSelector(); useEffect(() => { if (pool_id) { get_pool_detail(pool_id); @@ -78,9 +85,9 @@ export default function DclPoolChart({ if (pool) { get_chart_data(); } - }, [pool]); + }, [pool, accountId]); useEffect(() => { - if (price_range && chartDataList) { + if (price_range && chartDataList?.length) { drawChart(); } }, [price_range, chartDataList]); @@ -170,21 +177,70 @@ export default function DclPoolChart({ setPool(p); } async function get_chart_data() { + const { range } = getConfig(); + const list = await get_data_from_back_end(); + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + setChartDataList(list); + } + async function get_data_from_back_end() { const { point_delta, token_x_metadata, token_y_metadata, pool_id } = pool; - const { bin: bin_final, rangeGear, range } = getConfig(); + const { bin: bin_final, rangeGear } = getConfig(); const decimalRate_point = Math.pow(10, token_y_metadata.decimals) / Math.pow(10, token_x_metadata.decimals); const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); - const result = await getDclPoolPoints(pool_id, bin_final, point_l, point_r); - const list = result.point_data || []; - const [price_l_default, price_r_default] = - get_price_range_by_percent(range); - set_price_range([+price_l_default, +price_r_default]); - setZoom(range); - setChartDataList(list); + let list = []; + if (chartType == 'USER' && accountId) { + list = await getDclUserPoints(pool_id, bin_final, accountId); + } else { + const result = await getDclPoolPoints( + pool_id, + bin_final, + point_l, + point_r + ); + list = result.point_data || []; + } + return list; + } + function getChartDataListInRange() { + const point_l = get_point_by_price(price_range[0].toString()); + const point_r = get_point_by_price( + price_range[price_range.length - 1].toString() + ); + let start_index; + for (let i = 0; i < chartDataList.length - 1; i++) { + if (+chartDataList[i].point == point_l) { + start_index = i; + break; + } + if ( + +chartDataList[i].point > point_l && + +chartDataList[Math.max(i - 1, 0)].point < point_l + ) { + start_index = Math.max(i - 1, 0); + break; + } + } + let end_index = chartDataList.findIndex((d: IChartData) => { + return +d.point >= point_r; + }); + if (!isValid(start_index)) { + start_index = 0; + } + if (end_index == -1) { + end_index = chartDataList.length - 1; + } + const chartDataListInRange = chartDataList.slice( + start_index, + end_index + 1 + ); + return chartDataListInRange; } function get_price_range_by_percent(percent: number): [string, string] { const p_l_r = percent / 100; @@ -204,7 +260,9 @@ export default function DclPoolChart({ // down bars draw_down_bars({ data, scale, scaleBar }); // up bars - draw_up_bars({ data, scale, scaleBar }); + if (chartType !== 'USER') { + draw_up_bars({ data, scale, scaleBar }); + } // 创建横坐标轴 if (appearanceConfig.axisHidden) { d3.select(`${randomId} .axis`).remove(); @@ -215,8 +273,10 @@ export default function DclPoolChart({ if (appearanceConfig.hoverBoxHidden) { d3.select(`${randomId} .bars_background`).remove(); d3.select(`${randomId} .overBox`).remove(); + d3.select(`${randomId} .whole_bars_background`).remove(); + d3.select(`${randomId} .wholeOverBox`).remove(); } else { - draw_background_bars({ data, scale }); + draw_background_bars({ data, scale, scaleBar }); } // current line if (appearanceConfig.currentBarHidden) { @@ -239,30 +299,32 @@ export default function DclPoolChart({ const decimalRate_price = Math.pow(10, token_x_metadata.decimals) / Math.pow(10, token_y_metadata.decimals); - const data: IChartData[] = chartDataList.map((o: IChartData) => { - const { point } = o; - const price_l = getPriceByPoint(+point, decimalRate_price); - const point_r = +point + point_delta * bin_final; - const point_r_close = +point + point_delta * bin_final + 1; - const price_r = getPriceByPoint(point_r, decimalRate_price); - const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); + const data: IChartData[] = getChartDataListInRange().map( + (o: IChartData) => { + const { point } = o; + const price_l = getPriceByPoint(+point, decimalRate_price); + const point_r = +point + point_delta * bin_final; + const point_r_close = +point + point_delta * bin_final + 1; + const price_r = getPriceByPoint(point_r, decimalRate_price); + const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); - return { - ...o, - liquidity: Big(o.liquidity || 0).toFixed(), - order_liquidity: Big(o.order_liquidity || 0).toFixed(), - token_x: Big(o.token_x || 0).toFixed(), - token_y: Big(o.token_y || 0).toFixed(), - order_x: Big(o.order_x || 0).toFixed(), - order_y: Big(o.order_y || 0).toFixed(), - total_liquidity: Big(o.total_liquidity || 0).toFixed(), - fee: Big(o.fee || 0).toFixed(), - price: price_l.toString(), - price_r: price_r.toString(), - point_r: point_r.toString(), - price_r_close: price_r_close.toString(), - }; - }); + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + price: price_l.toString(), + price_r: price_r.toString(), + point_r: point_r.toString(), + price_r_close: price_r_close.toString(), + }; + } + ); return data; } function hoverBox(e: any, d: IChartData) { @@ -272,17 +334,8 @@ export default function DclPoolChart({ e.offsetX + disFromHoverBoxToPointer }px, ${e.offsetY / 2}px)` ); - const { - point, - token_x, - token_y, - order_x, - order_y, - liquidity, - order_liquidity, - fee, - total_liquidity, - } = d; + const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = + d; const { current_point } = pool; const { colors } = getConfig(); @@ -323,6 +376,22 @@ export default function DclPoolChart({ }px, ${e.offsetY / 2}px)` ); } + function hoverUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function LeaveUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } function remove_control() { d3.select(`${randomId} .control`).remove(); d3.select(`${randomId} .overlap`).remove(); @@ -416,6 +485,27 @@ export default function DclPoolChart({ function draw_background_bars({ data, scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + if (chartType == 'USER') { + draw_background_bars_user({ + scale, + scaleBar, + }); + } else { + draw_background_bars_pool({ + data, + scale, + }); + } + } + function draw_background_bars_pool({ + data, + scale, }: { data: IChartData[]; scale: Function; @@ -451,6 +541,49 @@ export default function DclPoolChart({ .attr('rx', 2) .attr('fill', 'transparent'); } + function draw_background_bars_user({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = getChartDataListRange_x_y(); + d3.select(`${randomId} .whole_bars_background`) + .on('mousemove', function (e) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverUserBox(e); + }) + .on('mouseleave', function (e) { + d3.select(this).attr('fill', 'transparent'); + LeaveUserBox(e); + }) + .transition() + .attr('width', function () { + return ( + scale(sortP[sortP.length - 1]) - + scale(sortP[0]) + + whole_bars_background_padding * 2 + ); + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + return scale(Big(sortP[0]).toNumber()) - whole_bars_background_padding; + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'transparent'); + } function draw_current_bar({ scale }: { scale: Function }) { d3.select(`${randomId} .currentLine`).attr( 'style', @@ -548,41 +681,6 @@ export default function DclPoolChart({ }); d3.select(`${randomId} .drag-right`).call(dragRight); } - function draw_init_overlap_area({ scale }: { scale: any }) { - const price = get_current_price(); - let price_l; - if (leftPoint) { - price_l = get_price_by_point(leftPoint); - } else { - const price_l_temp = Big(1 - defaultPercent / 100) - .mul(price) - .toFixed(); - const newLeftPoint = get_nearby_bin_left_point( - get_point_by_price(price_l_temp) - ); - price_l = get_point_by_price(newLeftPoint.toString()); - } - const x = scale(price_l); - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = rightX - x; - console.log( - 'price_range transform, x, rightX W', - price_range, - d3.select(`${randomId} .drag-right`).attr('transform'), - x, - rightX, - W - ); - d3.select(`${randomId} .overlap rect`) - .attr('transform', `translate(${x}, 0)`) - .attr('width', W); - } function get_current_price() { return get_price_by_point(pool.current_point); } @@ -640,6 +738,13 @@ export default function DclPoolChart({ .range([0, svgWidth - svgPaddingX * 2]); } function scaleAxisY() { + if (chartType == 'USER') { + return scaleAxisY_User(); + } else { + return scaleAxisY_Pool(); + } + } + function scaleAxisY_Pool() { const L: number[] = []; chartDataList.forEach((o: IChartData) => { const { liquidity, order_liquidity } = o; @@ -655,6 +760,32 @@ export default function DclPoolChart({ .domain([+sortL[0], +sortL[sortL.length - 1]]) .range([0, wholeBarHeight]); } + function scaleAxisY_User() { + const { sortY: sortL } = getChartDataListRange_x_y(); + return d3 + .scaleLinear() + .domain([+sortL[0], +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true); + } + function getChartDataListRange_x_y() { + const Y: number[] = []; + const X: number[] = []; + const chartDataListInrange = getChartDataListInRange(); + chartDataListInrange.forEach((o: IChartData) => { + const { liquidity, point } = o; + Y.push(+liquidity); + X.push(+point); + }); + const sortY = sortBy(Y); + const sortX = sortBy(X); + const max_x = sortX[X.length - 1] + getConfig().bin * pool.point_delta; + sortX.push(max_x); + const sortX_map_P = sortX.map((x) => { + return get_price_by_point(x); + }); + return { sortP: sortX_map_P, sortY }; + } function diffPrices(newPrice: string, peferencePrice?: string) { let movePercent; const price = peferencePrice || get_current_price(); @@ -761,6 +892,7 @@ export default function DclPoolChart({ + {/* 横坐标轴 */} {/* 拖拽线 left */} @@ -972,6 +1104,33 @@ export default function DclPoolChart({ ) : null}
+ {/* hover到区域上的悬浮框 todo test数据*/} +
+
+ Your Liquidity + ~$294.25 +
+
+ Price Range + + 1.5636 - 2.7102 + +
+
+ Position + + 20 NEAR + 36.2418 USDC + +
+
+ 24h APR + 34.5% +
+
+ Total Earned Fee + $8.283 +
+
{/* current 价格 */}
(null); @@ -693,11 +693,13 @@ export default function AddYourLiquidityPageV3() { <>
{/* 缩略图 */} - {/* */} + {/* */} {/* 详情页图 */} - {/* */} + {/* */} {/* 添加页图 */} - {/* */} + + {/* 用户流动性图表*/} + {/* */}
diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 20fc90ec3..e92b9974b 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -818,6 +818,23 @@ export const getDclPoolPoints = async ( return data; }) .catch(() => { - return {} + return {}; + }); +}; +export const getDclUserPoints = async ( + pool_id: string, + bin: number, + account_id: string +) => { + return await fetch( + config.indexerUrl + + `/get-dcl-points-by-account?pool_id=${pool_id}&slot_number=${bin}&account_id=${account_id}` + ) + .then(async (res) => { + const data = await res.json(); + return data; + }) + .catch(() => { + return []; }); }; From c0a946f3dae488e78f9a1e67208f30eb7acfc57b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 10 Jun 2023 23:41:07 +0800 Subject: [PATCH 016/204] fix conflict --- src/components/pool/YourLiquidityV2.tsx | 1 - src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 2 +- src/services/swapV3.ts | 2 -- 3 files changed, 1 insertion(+), 4 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 18a11ac22..566c8de7e 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -2132,7 +2132,6 @@ function UserLiquidityLineStyleGroup1({ // setShowAddBox(true); history.push('/addLiquidityV2'); - }} color="#fff" minWidth="5rem" diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 0c0c2f133..ceb17b683 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -697,7 +697,7 @@ export default function AddYourLiquidityPageV3() { {/* 详情页图 */} {/* */} {/* 添加页图 */} - + {/* */} {/* 用户流动性图表*/} {/* */}
diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 60c6af94a..faad7a3cf 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -972,10 +972,8 @@ export const batch_add_liquidity = async ({ new Big(0) ); - amount_y = amount_y.toFixed(0); - if (+amount_x > 0) { transactions.unshift({ receiverId: token_x.id, From ef4a6ad5162a33aad25dba72da132106e10de560 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 11 Jun 2023 16:13:29 +0800 Subject: [PATCH 017/204] add remove chart --- src/components/d3Chart/DclChart.tsx | 71 +++++++++++++++++++- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 ++ 2 files changed, 72 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 992b1ae06..477c7ffb8 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -34,6 +34,7 @@ export default function DclChart({ setRightPoint, config, chartType, + removeParams, }: { pool_id: string; leftPoint?: number; @@ -42,6 +43,12 @@ export default function DclChart({ setRightPoint?: Function; config?: IPoolChartConfig; chartType?: 'POOL' | 'USER'; + removeParams?: { + fromLeft?: boolean; + fromRight?: boolean; + point?: number; + all?: boolean; + }; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -278,6 +285,13 @@ export default function DclChart({ } else { draw_background_bars({ data, scale, scaleBar }); } + // remove select area + if (chartType == 'USER' && removeParams) { + draw_background_bars_for_select_area({ scale, scaleBar }); + } else { + d3.select('.remove_bars_background').remove(); + } + // current line if (appearanceConfig.currentBarHidden) { d3.select(`${randomId} .currentLine`).remove(); @@ -572,7 +586,7 @@ export default function DclChart({ ); }) .attr('x', function () { - return scale(Big(sortP[0]).toNumber()) - whole_bars_background_padding; + return scale(sortP[0]) - whole_bars_background_padding; }) .attr('y', function () { return ( @@ -584,6 +598,50 @@ export default function DclChart({ .attr('rx', 4) .attr('fill', 'transparent'); } + function draw_background_bars_for_select_area({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + // todo + const { sortP, sortY } = getChartDataListRange_x_y(); + const { fromLeft, fromRight, all, point } = removeParams; + d3.select(`${randomId} .remove_bars_background`) + .attr('width', function () { + if (all) { + return scale(sortP[sortP.length - 1]) - scale(sortP[0]); + } else if (fromLeft) { + return scale(get_price_by_point(point)) - scale(sortP[0]); + } else if (fromRight) { + return ( + scale(sortP[sortP.length - 1]) - scale(get_price_by_point(point)) + ); + } + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + if (fromRight) { + return scale(get_price_by_point(point)); + } else { + return scale(sortP[0]); + } + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'rgba(255,255,255,0.1)'); + } function draw_current_bar({ scale }: { scale: Function }) { d3.select(`${randomId} .currentLine`).attr( 'style', @@ -779,8 +837,9 @@ export default function DclChart({ }); const sortY = sortBy(Y); const sortX = sortBy(X); - const max_x = sortX[X.length - 1] + getConfig().bin * pool.point_delta; - sortX.push(max_x); + console.log('sortX', sortX); + // const max_x = sortX[X.length - 1] + getConfig().bin * pool.point_delta; + // sortX.push(max_x); const sortX_map_P = sortX.map((x) => { return get_price_by_point(x); }); @@ -893,6 +952,12 @@ export default function DclChart({ + {/* 横坐标轴 */} {/* 拖拽线 left */} diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index ceb17b683..26dc52bd6 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -700,6 +700,10 @@ export default function AddYourLiquidityPageV3() { {/* */} {/* 用户流动性图表*/} {/* */} + {/* 删除流动性图表 部分删除 */} + {/* */} + {/* 删除流动性图表 全部删除 */} + {/* */}
From 067f2644708385e719f00e4e4814a2ccfcff6925 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 11 Jun 2023 16:21:48 +0800 Subject: [PATCH 018/204] update e.g. --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 26dc52bd6..7ae1c19c8 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -700,8 +700,10 @@ export default function AddYourLiquidityPageV3() { {/* */} {/* 用户流动性图表*/} {/* */} - {/* 删除流动性图表 部分删除 */} + {/* 删除流动性图表 从右侧部分删除 */} {/* */} + {/* 删除流动性图表 从左侧部分删除 */} + {/* */} {/* 删除流动性图表 全部删除 */} {/* */}
From 7d6a100898f58df9bf56b89bcdbb18c2c55987fb Mon Sep 17 00:00:00 2001 From: Max Date: Mon, 12 Jun 2023 04:11:32 +0800 Subject: [PATCH 019/204] update remove liquidity style --- src/components/forms/SlippageSelector.tsx | 6 +- src/components/pool/RemovePoolV3.tsx | 619 +++++++++++++++--- src/components/pool/YourLiquidityV2.tsx | 19 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 342 +++++++++- src/pages/poolsV3/PoolDetailV3.tsx | 647 ++++++++++--------- src/pages/poolsV3/YourLiquidityDetailV3.tsx | 4 + src/services/swapV3.ts | 5 +- 7 files changed, 1212 insertions(+), 430 deletions(-) diff --git a/src/components/forms/SlippageSelector.tsx b/src/components/forms/SlippageSelector.tsx index b8a21c417..a7a130d00 100644 --- a/src/components/forms/SlippageSelector.tsx +++ b/src/components/forms/SlippageSelector.tsx @@ -473,9 +473,11 @@ export function PoolSlippageSelector({ export function PoolSlippageSelectorV3({ slippageTolerance, onChange, + textColor, }: { slippageTolerance: number; onChange: (slippage: number) => void; + textColor?: string; }) { const validSlippages = [0.1, 0.5, 1.0]; const intl = useIntl(); @@ -485,7 +487,9 @@ export function PoolSlippageSelectorV3({ <>
-
-
-
+ +
+
+ +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('left'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('right'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('all'); + }} + > + +
+
+
+ +
+ {/*
{removePercentList.map((p) => { return (
{
); })} -
-
- { - changeRemoveAmount(e.target.value); - }} - disabled={isLegacy ? true : false} - value={removePercentAmount} - type="range" - className={`w-full ${ - isLegacy ? 'pause cursor-not-allowed' : 'cursor-pointer' - }`} - style={{ backgroundSize: '100% 100%' }} - min="0" - max="100" - step="any" - /> -
*/} + { + changeRemoveAmount(e.target.value); + }} + disabled={removeType === 'all' || isLegacy ? true : false} + value={removePercentAmount} + type="range" + className={`w-full ${ + isLegacy ? 'pause cursor-not-allowed' : 'cursor-pointer' + }`} + style={{ backgroundSize: '100% 100%' }} + min="0" + max="100" + step={step} + /> + {/*
{ {toPrecision(removePercentAmount.toString(), 0)}% -
+
*/} +
+ +
+ {/* min price */} + +
+ + + + + { + const value = e.target.value; + setChangeType('min'); + setMinPrice(value); + }} + inputMode="decimal" + onBlur={() => { + handlePriceToAppropriatePoint(); + }} + disabled={removeType !== 'right'} + > +
+ +
+ + + + { + const value = e.target.value; + setChangeType('max'); + setMaxPrice(value); + }} + min={0} + value={maxPrice} + inputMode="decimal" + onBlur={() => { + handlePriceToAppropriatePoint(); + }} + > +
+ +
+ + + + +
+
-
- +
+ -
-
-
- - {MINDATA.displayX || '-'} - -
+ +
+
- - {tokenMetadata_x_y && tokenMetadata_x_y[0].symbol} + + + {MINDATA.displayX || '-'}
-
-
- - {MINDATA.displayY || '-'} - -
+
- - {tokenMetadata_x_y && tokenMetadata_x_y[1].symbol} + + + {MINDATA.displayY || '-'}
+ {isSignedIn ? ( { isRemoveLiquidityDisabled ? 'cursor-not-allowed' : '' }`} className={`mt-8 w-full h-14 text-center text-lg text-white focus:outline-none font-semibold`} - backgroundImage="linear-gradient(270deg, #7F43FF 0%, #00C6A2 97.06%)" + backgroundImage="linear-gradient(180deg, #C0B1A3 0%, #92877D 100%)" > { if ( @@ -380,6 +382,8 @@ export function YourLiquidityV2(props: any) { l.liquidityDetail)} + tokenPriceList={tokenPriceList} /> ); } @@ -1667,8 +1671,12 @@ export function findRangeIntersection(arr: number[][]) { function UserLiquidityLineStyleGroup1({ groupYourLiquidityList, + liquidities_list, + tokenPriceList, }: { groupYourLiquidityList: any[]; + liquidities_list: UserLiquidityInfo[]; + tokenPriceList: any; }) { // const { // goYourLiquidityDetailPage, @@ -1695,6 +1703,8 @@ function UserLiquidityLineStyleGroup1({ const publicData = groupYourLiquidityList[0]; + const [showRemoveBox, setShowRemoveBox] = useState(false); + const { tokenMetadata_x_y, fee, @@ -2147,7 +2157,7 @@ function UserLiquidityLineStyleGroup1({ { e.stopPropagation(); - // setShowRemoveBox(true); + setShowRemoveBox(true); }} rounded="rounded-lg" disabled={is_in_farming} @@ -2415,16 +2425,17 @@ function UserLiquidityLineStyleGroup1({ ) : null}
*/} - {/* {showRemoveBox ? ( + {showRemoveBox ? ( { setShowRemoveBox(false); }} + listLiquidities={liquidities_list} tokenMetadata_x_y={tokenMetadata_x_y} poolDetail={poolDetail} tokenPriceList={tokenPriceList} - userLiquidity={liquidityDetail} + userLiquidity={list_liquidities[0]} style={{ overlay: { backdropFilter: 'blur(15px)', @@ -2437,7 +2448,7 @@ function UserLiquidityLineStyleGroup1({ }} > ) : null} - { setShowAddBox(false); diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index ceb17b683..26609e9c7 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -82,6 +82,7 @@ import { scientificNotationToString, getAllocationsLeastOne, toInternationalCurrencySystem, + ONLY_ZEROS, } from '~utils/numbers'; import { WalletContext } from '../../utils/wallets-integration'; import _, { set } from 'lodash'; @@ -122,7 +123,6 @@ export default function AddYourLiquidityPageV3() { const [binNumber, setBinNumber] = useState(10); const [curPointInBinBoundry, setCurPointInBinBoundry] = useState(false); - const [buttonHover, setButtonHover] = useState(false); const [tokenPriceList, setTokenPriceList] = useState>({}); const [currentPools, setCurrentPools] = useState>(null); @@ -226,6 +226,17 @@ export default function AddYourLiquidityPageV3() { } }, [listPool, tokenPriceList]); + // trigger + useEffect(() => { + if ( + liquidityShape === 'Spot' && + !ONLY_ZEROS.test(tokenXAmount) && + currentSelectedPool + ) { + changeTokenXAmount(tokenXAmount); + } + }, [liquidityShape]); + async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -446,7 +457,10 @@ export default function AddYourLiquidityPageV3() { rightPoint: rightPoint, currentPoint: currentPoint, }); - setTokenYAmount(amount_result); + + if (liquidityShape === 'Spot') { + setTokenYAmount(amount_result); + } } } else { if (!onlyAddYToken) { @@ -456,7 +470,10 @@ export default function AddYourLiquidityPageV3() { rightPoint, currentPoint, }); - setTokenYAmount(amount_result); + + if (liquidityShape === 'Spot') { + setTokenYAmount(amount_result); + } } } } @@ -472,7 +489,9 @@ export default function AddYourLiquidityPageV3() { rightPoint, currentPoint, }); - setTokenXAmount(amount_result); + if (liquidityShape === 'Spot') { + setTokenXAmount(amount_result); + } } } else { if (!onlyAddXToken) { @@ -482,7 +501,9 @@ export default function AddYourLiquidityPageV3() { rightPoint: rightPoint, currentPoint: currentPoint, }); - setTokenXAmount(amount_result); + if (liquidityShape === 'Spot') { + setTokenXAmount(amount_result); + } } } } @@ -617,6 +638,8 @@ export default function AddYourLiquidityPageV3() { } return; } + + if (liquidityShape !== 'Spot') return; if (sort) { if (tokenXAmount) { const amount_result = getTokenYAmountByCondition({ @@ -1091,6 +1114,8 @@ export default function AddYourLiquidityPageV3() { setCurPointInBinBoundry={setCurPointInBinBoundry} getTokenYAmountByCondition={getTokenYAmountByCondition} getTokenXAmountByCondition={getTokenXAmountByCondition} + liquidityShape={liquidityShape} + setLiquidityShape={setLiquidityShape} /> )} @@ -1410,12 +1435,6 @@ function AddLiquidityComponent({ const [radius, setRadius] = useState(5); - useEffect(() => { - if (priceRangeMode === 'by_radius') { - setBinNumber(radius * 2); - } - }, [radius, priceRangeMode]); - useEffect(() => { if (!currentSelectedPool || !targetPoint) return; if (priceRangeMode === 'by_range') { @@ -1429,7 +1448,8 @@ function AddLiquidityComponent({ current_point: targetPoint, }); } - }, [priceRangeMode, targetCustomPrice]); + setCurrentPoint(targetPoint); + }, [priceRangeMode, targetPoint]); console.log('currentSelectedPool: ', currentSelectedPool); useEffect(() => { @@ -1450,8 +1470,6 @@ function AddLiquidityComponent({ Record >({}); - console.log('quickOptionsMapPoint: ', quickOptionsMapPoint); - const { globalState } = useContext(WalletContext); const [timer, setTimer] = useState(null); const [depthData, setDepthData] = useState(null); @@ -1465,6 +1483,33 @@ function AddLiquidityComponent({ tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; const token_y_decimals = tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + useEffect(() => { + if (priceRangeMode === 'by_radius') { + setBinNumber(radius * 2); + } + + if (!quickOptionsMapPoint['full']?.['left_p']) return; + + // get curpoint in bin boundry + + const left_b = quickOptionsMapPoint['full']['left_p']; + + const { current_point, point_delta } = currentSelectedPool; + + const point_diff = + Math.floor((current_point - left_b) / (point_delta * SLOT_NUMBER)) * + (point_delta * SLOT_NUMBER); + + const temp_cur_point = left_b + point_diff; + + const new_left_point = temp_cur_point - point_delta * SLOT_NUMBER * radius; + + const new_right_point = temp_cur_point + point_delta * SLOT_NUMBER * radius; + + setLeftPoint(new_left_point); + setRightPoint(new_right_point); + }, [radius, priceRangeMode, quickOptionsMapPoint['full']?.['left_p']]); + // init // quick options @@ -2360,6 +2405,8 @@ function AddLiquidityButton({ setCurPointInBinBoundry, getTokenYAmountByCondition, getTokenXAmountByCondition, + liquidityShape, + setLiquidityShape, }: { currentSelectedPool: PoolInfo; tokenX: TokenMetadata; @@ -2382,6 +2429,8 @@ function AddLiquidityButton({ setCurPointInBinBoundry: (curPointInBinBoundry: boolean) => void; getTokenYAmountByCondition: any; getTokenXAmountByCondition: any; + liquidityShape: LiquidityShape; + setLiquidityShape: (liquidityShape: LiquidityShape) => void; }) { let [leftPoint, setLeftPoint] = useState(0); let [rightPoint, setRightPoint] = useState(0); @@ -2827,6 +2876,229 @@ function AddLiquidityButton({ }); } + function addLiquidityBidAsk() { + const { pool_id, point_delta } = currentSelectedPool; + + const binSize = point_delta * SLOT_NUMBER; + + let liquidityInfo: AddLiquidityInfo[]; + + if (onlyAddYToken) { + // left side + const Y = tokenYAmount; + + const D = new Big(Y) + .mul(2) + .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) + .div(binSize); + + liquidityInfo = new Array(binNumber).map((item, index) => { + const height = new Big(binNumber - index).mul(D).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y, + }; + }); + } + + if (onlyAddXToken) { + const X = tokenSort ? tokenYAmount : tokenXAmount; + + const D = new Big(X) + .mul(2) + .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) + .div(binSize); + + liquidityInfo = new Array(binNumber).map((item, index) => { + const height = new Big(index + 1).mul(D).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }); + } + + if (!onlyAddXToken || !onlyAddYToken) { + if (curPointInBinBoundry) { + const leftBinSize = Math.floor(binNumber / 2); + const rightBinSize = binNumber - leftBinSize; + + const X_left = tokenYAmount; + + const Y_right = tokenXAmount; + + const D_left = new Big(X_left) + .mul(2) + .div(new Big(leftBinSize).mul(new Big(leftBinSize).plus(1))) + .div(binSize); + + const D_right = new Big(Y_right) + .mul(2) + .div(new Big(rightBinSize).mul(new Big(rightBinSize).plus(1))) + .div(binSize); + + liquidityInfo = new Array(leftBinSize).map((item, index) => { + const height = new Big(leftBinSize - index).mul(D_left).toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y, + }; + }); + + liquidityInfo = liquidityInfo.concat( + new Array(rightBinSize).map((item, index) => { + const height = new Big(index + 1).mul(D_right).toFixed(0); + const left_p = leftPoint + binSize * (leftBinSize + index); + const right_p = leftPoint + binSize * (leftBinSize + index + 1); + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }) + ); + } else { + const leftBinSize = Math.floor(binNumber / 2) + 1; + const rightBinSize = binNumber - leftBinSize - 1; + + const D_left = new Big(tokenYAmount).div( + new Big( + new Big(((leftBinSize - 1) * leftBinSize) / 2).mul(binSize) + ).plus(new Big(leftBinSize).mul(currentPoint - leftPoint)) + ); + + if (leftBinSize - 1 > 0) { + const addLpList = new Array(leftBinSize - 1).map((item, index) => { + const height = new Big(leftBinSize - 1 - index) + .mul(D_left) + .toFixed(0); + const left_p = leftPoint + binSize * index; + const right_p = leftPoint + binSize * (index + 1); + + const amount_y = toNonDivisibleNumber( + tokenY.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x: '0', + amount_y: amount_y, + }; + }); + liquidityInfo = (liquidityInfo || []).concat(addLpList); + } + + // middle bin + + const tokenYMiddleAmount = new Big(currentPoint) + .minus(leftPoint + Number(leftBinSize - 1) * binSize) + .mul(D_left) + .toFixed(0); + + const tokenXMiddleAmount = getTokenXAmountByCondition({ + amount: tokenYMiddleAmount, + leftPoint: leftPoint + Number(leftBinSize - 1) * binSize, + rightPoint: leftPoint + leftBinSize * binSize, + currentPoint, + }); + + liquidityInfo = (liquidityInfo || []).concat([ + { + pool_id, + left_point: leftPoint + Number(leftBinSize - 1) * binSize, + right_point: leftPoint + leftBinSize * binSize, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXMiddleAmount), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYMiddleAmount), + }, + ]); + + // right side + + if (rightBinSize > 1) { + const newtokenXAmount = new Big(tokenXAmount) + .minus(tokenXMiddleAmount) + .toFixed(0); + + const D_right = new Big(newtokenXAmount) + .mul(2) + .div(new Big(((rightBinSize - 1) * rightBinSize) / 2).mul(binSize)); + + const addLpList = new Array(rightBinSize).map((item, index) => { + const height = new Big(index + 1).mul(D_right).toFixed(0); + const left_p = leftPoint + leftBinSize + index * binSize; + const right_p = left_p + binSize; + + const amount_x = toNonDivisibleNumber( + tokenX.decimals, + new Big(height).mul(binSize).toFixed() + ); + + return { + pool_id, + left_point: left_p, + right_point: right_p, + amount_x, + amount_y: '0', + }; + }); + liquidityInfo = (liquidityInfo || []).concat(addLpList); + } + + // + } + } + + return batch_add_liquidity({ + liquidityInfo, + token_x: tokenX, + token_y: tokenY, + }); + } + function addLiquidity() { setAddLiquidityButtonLoading(true); const { pool_id } = currentSelectedPool; @@ -2914,6 +3186,14 @@ function AddLiquidityButton({ const tokenSort = tokenX.id == currentSelectedPool.token_x; const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquidity + : liquidityShape === 'Curve' + ? addLiquidityCurve + : () => {}; + return (
{ const oldLen = s.length; s = s.replace(/^0+/, ''); + if (s.length === 0 && oldLen > 0) { s = '0'; } + + if (max && Number(s) > max) { + return max; + } + return s; }; @@ -3130,15 +3422,7 @@ function IntegerInputComponent({ value, onChange, disabled }: any) { }; return ( -
- {/*
{ - reduceOneSlot('r'); - }} - > - -
*/} +
- {/*
{ - addOneSlot('r'); - }} - > - -
*/}
); } diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 7b16c1324..205af971f 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -888,6 +888,8 @@ function YourLiquidityBox(props: { const { accountId } = useWalletSelector(); + const history = useHistory(); + const accountAPR = useDCLAccountAPR({ pool_id: poolDetail.pool_id, account_id: accountId, @@ -973,6 +975,11 @@ function YourLiquidityBox(props: { const groupedData = getGroupLiquidities(); + function goAddliquidityV2() { + const url_pool_id = get_pool_name(poolDetail.pool_id); + history.push(`/addLiquidityV2#${url_pool_id}`); + } + return (
@@ -1139,7 +1146,9 @@ function YourLiquidityBox(props: { { e.stopPropagation(); - addLiquidity(); + // addLiquidity(); + + goAddliquidityV2(); }} color="#fff" borderRadius={'8px'} @@ -1522,6 +1531,7 @@ function SelectLiquidityBox(props: any) { user_liquidities, matched_seeds, } = props; + const [hoverHashId, setHoverHashId] = useState(''); const [showRemoveBox, setShowRemoveBox] = useState(false); const [showAddBox, setShowAddBox] = useState(false); @@ -1639,312 +1649,335 @@ function SelectLiquidityBox(props: any) { (liquidity: UserLiquidityInfo) => +(liquidity.part_farm_ratio || 0) == 0 ); return ( - - -
-
- Your Positions - - {user_liquidities_detail.length} - -
-
- -
-
- {/* for Mobile */} - {isMobile ? ( -
- {user_liquidities_detail.map( - (liquidityDetail: UserLiquidityDetail, index: number) => { - return ( -
- - #{liquidityDetail.hashId} - -
- Liquidity - - {displayLiqudityTvl(liquidityDetail)} - -
-
- Range - - {displayRange(liquidityDetail)} - -
-
- - - - - {displayLiqudityFee(liquidityDetail)} - -
-
- - - - - {displayFarmStatus(user_liquidities[index])} - -
-
- {is_in_farming(user_liquidities[index]) ? ( - { - e.stopPropagation(); - go_farm(user_liquidities[index]); - }} - rounded="rounded-lg" - px="px-0" - py="py-1" - style={{ minWidth: '5rem' }} - className={`px-2 text-sm text-greenColor border-opacity-50 h-9 focus:outline-none`} - > -
- - -
-
- ) : ( - <> - {operation == 'add' ? ( - { - e.stopPropagation(); - hoverLine(liquidityDetail.hashId); - setShowAddBox(true); - }} - color="#fff" - borderRadius={'8px'} - className={`px-2 h-9 text-center text-sm text-white focus:outline-none`} - > - - - ) : ( - { - e.stopPropagation(); - hoverLine(liquidityDetail.hashId); - setShowRemoveBox(true); - }} - color="#fff" - className={`flex w-24 h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover`} - > - - - )} - - )} -
-
- ); - } - )} -
- ) : ( - // for Pc -
-
- NFT ID - - - - - - - - - - {has_no_related_seed ? null : ( - - - - )} - -
-
- {user_liquidities_detail.map( - (liquidityDetail: UserLiquidityDetail, index: number) => { - return ( -
{ - hoverLine(liquidityDetail.hashId); - }} - // onMouseLeave={() => setHoverHashId('')} - className={`grid grid-cols-${ - has_no_related_seed ? 10 : 12 - } gap-x-3 text-white text-base h-14 justify-center items-center px-6 ${ - hoverHashId == liquidityDetail.hashId - ? 'bg-chartBg bg-opacity-20' - : '' - }`} - > - - #{liquidityDetail.hashId} - - - {displayLiqudityTvl(liquidityDetail)} - - - {displayRange(liquidityDetail)} - - - {displayLiqudityFee(liquidityDetail)} - - {has_no_related_seed ? null : ( - - {displayFarmStatus(user_liquidities[index])} - - )} -
- {is_in_farming(user_liquidities[index]) ? ( - { - e.stopPropagation(); - go_farm(user_liquidities[index]); - }} - rounded="rounded-lg" - px="px-0" - py="py-1" - style={{ minWidth: '5rem' }} - className={`w-full px-2 text-sm text-greenColor h-9 border-opacity-50 ${ - hoverHashId == liquidityDetail.hashId - ? '' - : 'hidden' - }`} - > -
- - -
-
- ) : ( - <> - {operation == 'add' ? ( - { - e.stopPropagation(); - setShowAddBox(true); - }} - color="#fff" - borderRadius={'8px'} - className={`px-2 h-9 text-center text-sm text-white focus:outline-none ${ - hoverHashId == liquidityDetail.hashId - ? '' - : 'hidden' - }`} - > - - - ) : ( - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - color="#fff" - className={`flex h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ - hoverHashId == liquidityDetail.hashId - ? '' - : 'hidden' - }`} - > - - - )} - - )} -
-
- ); - } - )} -
-
- )} - - {operation == 'add' ? ( -
-
{ - e.stopPropagation(); - goAddLiqudityPage(); - }} - color="#fff" - className={`flex items-center justify-center w-full h-10 mx-6 border border-dashed border-dclBorderColor rounded-lg text-sm text-primaryText cursor-pointer hover:bg-dclButtonBgColor hover:text-white focus:outline-none mt-7 xsm:mt-4`} - > - + -
-
- ) : null} - {operation == 'add' && showAddBox ? ( - { - setShowAddBox(false); - }} - tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={getCurrentLiqudity(hoverHashId)} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > - ) : null} - {operation == 'remove' && showRemoveBox ? ( - { - setShowRemoveBox(false); - }} - tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={getCurrentLiqudity(hoverHashId)} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > - ) : null} -
-
+ // + // + //
+ //
+ // Your Positions + // + // {user_liquidities_detail.length} + // + //
+ //
+ // + //
+ //
+ // {/* for Mobile */} + // {isMobile ? ( + //
+ // {user_liquidities_detail.map( + // (liquidityDetail: UserLiquidityDetail, index: number) => { + // return ( + //
+ // + // #{liquidityDetail.hashId} + // + //
+ // Liquidity + // + // {displayLiqudityTvl(liquidityDetail)} + // + //
+ //
+ // Range + // + // {displayRange(liquidityDetail)} + // + //
+ //
+ // + // + // + // + // {displayLiqudityFee(liquidityDetail)} + // + //
+ //
+ // + // + // + // + // {displayFarmStatus(user_liquidities[index])} + // + //
+ //
+ // {is_in_farming(user_liquidities[index]) ? ( + // { + // e.stopPropagation(); + // go_farm(user_liquidities[index]); + // }} + // rounded="rounded-lg" + // px="px-0" + // py="py-1" + // style={{ minWidth: '5rem' }} + // className={`px-2 text-sm text-greenColor border-opacity-50 h-9 focus:outline-none`} + // > + //
+ // + // + //
+ //
+ // ) : ( + // <> + // {operation == 'add' ? ( + // { + // e.stopPropagation(); + // hoverLine(liquidityDetail.hashId); + // setShowAddBox(true); + // }} + // color="#fff" + // borderRadius={'8px'} + // className={`px-2 h-9 text-center text-sm text-white focus:outline-none`} + // > + // + // + // ) : ( + // { + // e.stopPropagation(); + // hoverLine(liquidityDetail.hashId); + // setShowRemoveBox(true); + // }} + // color="#fff" + // className={`flex w-24 h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover`} + // > + // + // + // )} + // + // )} + //
+ //
+ // ); + // } + // )} + //
+ // ) : ( + // // for Pc + //
+ //
+ // NFT ID + // + // + // + // + // + // + // + // + // + // {has_no_related_seed ? null : ( + // + // + // + // )} + // + //
+ //
+ // {user_liquidities_detail.map( + // (liquidityDetail: UserLiquidityDetail, index: number) => { + // return ( + //
{ + // hoverLine(liquidityDetail.hashId); + // }} + // // onMouseLeave={() => setHoverHashId('')} + // className={`grid grid-cols-${ + // has_no_related_seed ? 10 : 12 + // } gap-x-3 text-white text-base h-14 justify-center items-center px-6 ${ + // hoverHashId == liquidityDetail.hashId + // ? 'bg-chartBg bg-opacity-20' + // : '' + // }`} + // > + // + // #{liquidityDetail.hashId} + // + // + // {displayLiqudityTvl(liquidityDetail)} + // + // + // {displayRange(liquidityDetail)} + // + // + // {displayLiqudityFee(liquidityDetail)} + // + // {has_no_related_seed ? null : ( + // + // {displayFarmStatus(user_liquidities[index])} + // + // )} + //
+ // {is_in_farming(user_liquidities[index]) ? ( + // { + // e.stopPropagation(); + // go_farm(user_liquidities[index]); + // }} + // rounded="rounded-lg" + // px="px-0" + // py="py-1" + // style={{ minWidth: '5rem' }} + // className={`w-full px-2 text-sm text-greenColor h-9 border-opacity-50 ${ + // hoverHashId == liquidityDetail.hashId + // ? '' + // : 'hidden' + // }`} + // > + //
+ // + // + //
+ //
+ // ) : ( + // <> + // {operation == 'add' ? ( + // { + // e.stopPropagation(); + // setShowAddBox(true); + // }} + // color="#fff" + // borderRadius={'8px'} + // className={`px-2 h-9 text-center text-sm text-white focus:outline-none ${ + // hoverHashId == liquidityDetail.hashId + // ? '' + // : 'hidden' + // }`} + // > + // + // + // ) : ( + // { + // e.stopPropagation(); + // setShowRemoveBox(true); + // }} + // color="#fff" + // className={`flex h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ + // hoverHashId == liquidityDetail.hashId + // ? '' + // : 'hidden' + // }`} + // > + // + // + // )} + // + // )} + //
+ //
+ // ); + // } + // )} + //
+ //
+ // )} + + // {operation == 'add' ? ( + //
+ //
{ + // e.stopPropagation(); + // goAddLiqudityPage(); + // }} + // color="#fff" + // className={`flex items-center justify-center w-full h-10 mx-6 border border-dashed border-dclBorderColor rounded-lg text-sm text-primaryText cursor-pointer hover:bg-dclButtonBgColor hover:text-white focus:outline-none mt-7 xsm:mt-4`} + // > + // + + //
+ //
+ // ) : null} + // {operation == 'add' && showAddBox ? ( + // { + // setShowAddBox(false); + // }} + // tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} + // poolDetail={poolDetail} + // tokenPriceList={tokenPriceList} + // userLiquidity={getCurrentLiqudity(hoverHashId)} + // style={{ + // overlay: { + // backdropFilter: 'blur(15px)', + // WebkitBackdropFilter: 'blur(15px)', + // }, + // content: { + // outline: 'none', + // transform: 'translate(-50%, -50%)', + // }, + // }} + // > + // ) : null} + // {operation == 'remove' && showRemoveBox ? ( + // { + // setShowRemoveBox(false); + // }} + // listLiquidities={user_liquidities} + // tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} + // poolDetail={poolDetail} + // tokenPriceList={tokenPriceList} + // userLiquidity={getCurrentLiqudity(hoverHashId)} + // style={{ + // overlay: { + // backdropFilter: 'blur(15px)', + // WebkitBackdropFilter: 'blur(15px)', + // }, + // content: { + // outline: 'none', + // transform: 'translate(-50%, -50%)', + // }, + // }} + // /> + // ) : null} + //
+ //
+ + operation == 'remove' && isOpen ? ( + + ) : null ); } diff --git a/src/pages/poolsV3/YourLiquidityDetailV3.tsx b/src/pages/poolsV3/YourLiquidityDetailV3.tsx index 81c101d73..1820d16a8 100644 --- a/src/pages/poolsV3/YourLiquidityDetailV3.tsx +++ b/src/pages/poolsV3/YourLiquidityDetailV3.tsx @@ -87,6 +87,9 @@ export default function YourLiquidityDetail(props: any) { const [listLiquidities, setListLiquidities] = useState( [] ); + + console.log('listLiquidities: ', listLiquidities); + const [listLiquiditiesDone, setListLiquiditiesDone] = useState(false); const [is_in_farming, set_is_in_farming] = useState(true); @@ -950,6 +953,7 @@ export default function YourLiquidityDetail(props: any) { onRequestClose={() => { setShowRemoveBox(false); }} + listLiquidities={listLiquidities} tokenMetadata_x_y={tokenMetadata_x_y} poolDetail={poolDetail} tokenPriceList={tokenPriceList} diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index faad7a3cf..88ee2b11b 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -780,6 +780,9 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); } } + if(deposit_fee.eq(0)){ + return '' + } return utils.format.formatNearAmount(deposit_fee.toFixed(0)); }; @@ -806,7 +809,7 @@ export const add_liquidity = async ({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ { - methodName: 'add_liquidity', + methodName: 'batch_add_liquidity', args: { pool_id, left_point, From 580724f4dd3438b995d1f97182e64f88d287df31 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 12 Jun 2023 10:48:50 +0800 Subject: [PATCH 020/204] just for test --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index fe8582c6e..bd3ca7c98 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -720,9 +720,9 @@ export default function AddYourLiquidityPageV3() { {/* 详情页图 */} {/* */} {/* 添加页图 */} - {/* */} + {/* 用户流动性图表*/} - {/* */} + {/* 删除流动性图表 从右侧部分删除 */} {/* */} {/* 删除流动性图表 从左侧部分删除 */} From 3b9f6acfdd603573c8c4b1c73cc53bc9b72cf09b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 12 Jun 2023 15:36:45 +0800 Subject: [PATCH 021/204] update --- src/components/d3Chart/DclChart.tsx | 15 +- src/components/farm/FarmsDclDetailCopy.tsx | 2562 ++++++++++++++++++++ 2 files changed, 2572 insertions(+), 5 deletions(-) create mode 100644 src/components/farm/FarmsDclDetailCopy.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 477c7ffb8..f82f034b5 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -62,6 +62,7 @@ export default function DclChart({ const appearanceConfig: IPoolChartConfig = config || {}; const dragBarWidth = 28; const percentBoxWidth = 44; + const min_bar_height = 2; const svgWidth = +(appearanceConfig.svgWidth || 520); const svgHeight = +(appearanceConfig.svgHeight || 250); const axisHeight = appearanceConfig.axisHidden @@ -445,13 +446,13 @@ export default function DclChart({ ); }) .attr('height', function (d) { - return scaleBar(+d.liquidity); + return get_final_bar_height(scaleBar(+d.liquidity)); }) .attr('x', function (d) { return scale(Big(d.price).toNumber()); }) .attr('y', function (d) { - return wholeBarHeight - scaleBar(+d.liquidity); + return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); }) .attr('rx', 2) .attr('fill', function (d) { @@ -480,14 +481,14 @@ export default function DclChart({ ); }) .attr('height', function (d) { - return scaleBar(+d.order_liquidity); + return get_final_bar_height(scaleBar(+d.order_liquidity)); }) .attr('x', function (d) { return scale(Big(d.price).toNumber()); }) .attr('y', function (d) { return ( - wholeBarHeight - scaleBar(+d.liquidity) - scaleBar(+d.order_liquidity) + wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)) - get_final_bar_height(scaleBar(+d.order_liquidity)) ); }) .attr('rx', 2) @@ -496,6 +497,10 @@ export default function DclChart({ }) .attr('opacity', '0.7'); } + function get_final_bar_height(h:number) { + if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) return min_bar_height; + return h + } function draw_background_bars({ data, scale, @@ -809,7 +814,7 @@ export default function DclChart({ L.push( +liquidity, +order_liquidity, - Big(liquidity).plus(order_liquidity).toNumber() + Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() ); }); const sortL = sortBy(L); diff --git a/src/components/farm/FarmsDclDetailCopy.tsx b/src/components/farm/FarmsDclDetailCopy.tsx new file mode 100644 index 000000000..7dda4ca30 --- /dev/null +++ b/src/components/farm/FarmsDclDetailCopy.tsx @@ -0,0 +1,2562 @@ +import React, { + useEffect, + useRef, + useState, + useContext, + useMemo, + createContext, +} from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { isMobile } from '~utils/device'; +import { + ArrowLeftIcon, + UpArrowIcon, + BoostRightArrowIcon, + BoostOptIcon, + DclFarmIcon, + NFTIdIcon, + LinkArrowIcon, + NewTag, + CalcIcon, +} from '~components/icon/FarmBoost'; +import { RefreshIcon } from '~components/icon/swapV3'; +import { AddButtonIcon } from '~components/icon/V3'; +import { useHistory, useLocation } from 'react-router-dom'; +import getConfig from '../../services/config'; +import { LinkIcon, ArrowDownHollow } from '~components/icon'; +import { + FarmBoost, + Seed, + claimRewardBySeed_boost, + BoostConfig, + stake_boost_nft, + unStake_boost_nft, +} from '~services/farm'; +import { WalletContext } from '../../utils/wallets-integration'; +import { + toPrecision, + toReadableNumber, + toNonDivisibleNumber, + toInternationalCurrencySystem, + formatWithCommas, + calculateFairShare, +} from '../../utils/numbers'; +import BigNumber from 'bignumber.js'; +import { + GradientButton, + ButtonTextWrapper, + OprationButton, + ConnectToNearBtn, +} from '~components/button/Button'; +import { getMftTokenId, toRealSymbol } from '~utils/token'; +import ReactTooltip from 'react-tooltip'; +import QuestionMark from '~components/farm/QuestionMark'; +import { LOVE_TOKEN_DECIMAL } from '../../state/referendum'; +import { + getPriceByPoint, + UserLiquidityInfo, + get_total_value_by_liquidity_amount_dcl, + mint_liquidity, + get_valid_range, + allocation_rule_liquidities, + TOKEN_LIST_FOR_RATE, + get_matched_seeds_for_dcl_pool, + displayNumberToAppropriateDecimals, + get_intersection_radio, + get_intersection_icon_by_radio, + getEffectiveFarmList, + sort_tokens_by_base, + get_pool_name, + openUrl, +} from '~services/commonV3'; +import { list_liquidities, dcl_mft_balance_of } from '../../services/swapV3'; +import { AddNewPoolV3 } from '~components/pool/AddNewPoolV3'; +import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; +import CalcModelDcl from '../../components/farm/CalcModelDcl'; +import moment from 'moment'; +const ONLY_ZEROS = /^0*\.?0*$/; +const { REF_VE_CONTRACT_ID, REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); +const FarmContext = createContext(null); +export default function FarmsDclDetail(props: { + detailData: Seed; + emptyDetailData: Function; + tokenPriceList: any; + loveSeed: Seed; + boostConfig: BoostConfig; + user_data: Record; + user_data_loading: Boolean; + dayVolumeMap: Record; + all_seeds: Seed[]; +}) { + const { + detailData, + emptyDetailData, + tokenPriceList, + loveSeed, + boostConfig, + user_data, + user_data_loading, + dayVolumeMap, + all_seeds, + } = props; + const [listLiquidities, setListLiquidities] = useState( + [] + ); + const [listLiquidities_inFarimg, set_listLiquidities_inFarimg] = useState< + UserLiquidityInfo[] + >([]); + const [listLiquidities_unFarimg, set_listLiquidities_unFarimg] = useState< + UserLiquidityInfo[] + >([]); + const [listLiquidities_unavailable, set_listLiquidities_unavailable] = + useState([]); + const [listLiquiditiesLoading, setListLiquiditiesLoading] = useState(true); + const [rangeSort, setRangeSort] = useState(true); + const [addLiquidityModalVisible, setAddLiquidityModalVisible] = + useState(false); + const [mft_balance_in_dcl_account, set_mft_balance_in_dcl_account] = + useState('0'); + const [claimLoading, setClaimLoading] = useState(false); + const [showDetail, setShowDetail] = useState(false); + const [betterSeed, setBetterSeed] = useState(); + const [isNewSeed, setIsNewSeed] = useState(false); + const [seedDclCalcVisible, setSeedDclCalcVisible] = useState(false); + const s = localStorage.getItem('unavailable_positions'); + const [show_unavailable_positions, set_show_unavailable_positions] = useState( + s == '1' ? true : false + ); + const { + user_seeds_map = {}, + user_unclaimed_map = {}, + user_unclaimed_token_meta_map = {}, + } = user_data; + const history = useHistory(); + const intl = useIntl(); + const tokens = sortTokens(detailData.pool.tokens_meta_data); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const rate_need_to_reverse_display = useMemo(() => { + const { tokens_meta_data } = detailData.pool; + if (tokens_meta_data) { + const [tokenX] = tokens_meta_data; + if (TOKEN_LIST_FOR_RATE.indexOf(tokenX.symbol) > -1) return true; + return false; + } + }, [detailData]); + const isEnded = useMemo(() => { + if (detailData?.farmList) { + const farms = detailData.farmList; + return farms[0].status == 'Ended'; + } + return false; + }, [detailData]); + useEffect(() => { + get_farms_data(); + }, [all_seeds]); + useEffect(() => { + if (isSignedIn) { + get_list_liquidities(); + get_mft_balance_of(); + } + }, [isSignedIn, user_data_loading, all_seeds]); + useEffect(() => { + if (rate_need_to_reverse_display) { + setRangeSort(false); + } + }, [rate_need_to_reverse_display]); + const unclaimedRewardsData = useMemo(() => { + return getTotalUnclaimedRewards(); + }, [user_unclaimed_map[detailData.seed_id]]); + function getTotalUnclaimedRewards() { + let totalPrice = 0; + let resultTip = ''; + const tempFarms = {}; + + detailData.farmList.forEach((farm: FarmBoost) => { + tempFarms[farm.terms.reward_token] = true; + }); + const unclaimed = user_unclaimed_map[detailData.seed_id] || {}; + const unClaimedTokenIds = Object.keys(unclaimed); + const tokenList: any[] = []; + unClaimedTokenIds?.forEach((tokenId: string) => { + const token: TokenMetadata = user_unclaimed_token_meta_map[tokenId]; + // total price + const { id, decimals, icon } = token; + const amount = toReadableNumber(decimals, unclaimed[id] || '0'); + const tokenPrice = tokenPriceList[id]?.price; + if (tokenPrice && tokenPrice != 'N/A') { + totalPrice += +amount * tokenPrice; + } + // rewards number + let displayNum = ''; + if (new BigNumber('0').isEqualTo(amount)) { + displayNum = '-'; + } else if (new BigNumber('0.001').isGreaterThan(amount)) { + displayNum = '<0.001'; + } else { + displayNum = new BigNumber(amount).toFixed(3, 1); + } + // before boost number + let beforeNum = ''; + // if (radio) { // Reserved comment + // const v = new BigNumber(amount).dividedBy(radio); + // if (new BigNumber('0').isEqualTo(v)) { + // beforeNum = '-'; + // } else if (new BigNumber('0.001').isGreaterThan(v)) { + // beforeNum = '<0.001'; + // } else { + // beforeNum = new BigNumber(v).toFixed(3, 1); + // } + // } + const tempTokenData = { + token, + amount: displayNum, + preAmount: beforeNum, + }; + tokenList.push(tempTokenData); + const txt = intl.formatMessage({ id: 'ended_search' }); + const itemHtml = `
+ +
+ ${formatWithCommas(displayNum)} + ${ + !isEnded && !tempFarms[id] + ? `${txt}` + : '' + } +
+
`; + resultTip += itemHtml; + }); + if (totalPrice == 0) { + return { + worth: , + showClaimButton: false, + tip: resultTip, + list: tokenList, + }; + } else if (new BigNumber('0.01').isGreaterThan(totalPrice)) { + return { + worth: '<$0.01', + showClaimButton: true, + tip: resultTip, + list: tokenList, + }; + } else { + return { + worth: `$${toInternationalCurrencySystem(totalPrice.toString(), 2)}`, + showClaimButton: true, + tip: resultTip, + list: tokenList, + }; + } + } + async function get_farms_data() { + const matched_seeds = get_matched_seeds_for_dcl_pool({ + seeds: all_seeds, + pool_id: detailData.pool.pool_id, + sort: 'new', + }); + const targetSeed = matched_seeds[0]; + if (matched_seeds.length > 1 && targetSeed.seed_id != detailData.seed_id) { + setBetterSeed(targetSeed); + } + if (matched_seeds.length > 1 && targetSeed.seed_id == detailData.seed_id) { + setIsNewSeed(true); + } + } + async function get_mft_balance_of() { + const { seed_decimal, seed_id } = detailData; + const [contractId, temp_pool_id] = seed_id.split('@'); + const mft_id = `:${temp_pool_id}`; + const balance = await dcl_mft_balance_of(mft_id); + set_mft_balance_in_dcl_account(balance); + } + async function get_list_liquidities() { + const list: UserLiquidityInfo[] = await list_liquidities(); + if (list.length > 0 && !user_data_loading && all_seeds.length > 0) { + let temp_unavailable_final: UserLiquidityInfo[] = []; + let temp_free_final: UserLiquidityInfo[] = []; + let temp_farming_final: UserLiquidityInfo[] = []; + const { free_amount = '0', locked_amount = '0' } = + user_seeds_map[detailData.seed_id] || {}; + const user_seed_amount = new BigNumber(free_amount) + .plus(locked_amount) + .toFixed(); + const [temp_farming, temp_free, temp_unavailable] = + allocation_rule_liquidities({ + list, + user_seed_amount, + seed: detailData, + }); + temp_unavailable_final = temp_unavailable; + temp_free_final = temp_free; + temp_farming_final = temp_farming; + const liquidities_minted_in_another_seed = temp_unavailable.filter( + (liquidity: UserLiquidityInfo) => { + const { mft_id } = liquidity; + const { seed_id } = detailData; + if (mft_id) { + const [contractId, temp_pool_id] = seed_id.split('@'); + const [fixRange_s, pool_id_s, left_point_s, right_point_s] = + temp_pool_id.split('&'); + const [fixRange_l, pool_id_l, left_point_l, right_point_l] = + mft_id.split('&'); + return ( + left_point_s != left_point_l || right_point_s != right_point_l + ); + } + } + ); + if (liquidities_minted_in_another_seed.length > 0) { + const liquidity_another = liquidities_minted_in_another_seed[0]; + const { mft_id } = liquidity_another; + const list_new = JSON.parse(JSON.stringify(list)); + const seed_id_another = + REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); + const { free_amount = '0', locked_amount = '0' } = + user_seeds_map[seed_id_another] || {}; + const user_seed_amount_another = new BigNumber(free_amount) + .plus(locked_amount) + .toFixed(); + const seed_another = all_seeds.find((seed: Seed) => { + return seed.seed_id == seed_id_another; + }); + if (seed_another) { + const [ + temp_farming_another, + temp_free_another, + temp_unavailable_another, + ] = allocation_rule_liquidities({ + list: list_new, + user_seed_amount: user_seed_amount_another, + seed: seed_another, + }); + const temp_farming_another_map = {}; + temp_farming_another.forEach((liquidity: UserLiquidityInfo) => { + temp_farming_another_map[liquidity.lpt_id] = liquidity; + }); + const { min_deposit } = detailData; + liquidities_minted_in_another_seed.forEach( + (liquidity: UserLiquidityInfo) => { + const liquidity_another: UserLiquidityInfo = + temp_farming_another_map[liquidity.lpt_id]; + const v_liquidity = mint_liquidity(liquidity, detailData.seed_id); + if (+liquidity_another?.part_farm_ratio > 0) { + liquidity.status_in_other_seed = 'staked'; + } + if (new BigNumber(v_liquidity).isLessThan(min_deposit)) { + liquidity.less_than_min_deposit = true; + } + } + ); + } + const temp_unavailable_new: UserLiquidityInfo[] = []; + const frees_extra = temp_unavailable.filter( + (liquidity: UserLiquidityInfo) => { + const [left_point, right_point] = get_valid_range( + liquidity, + detailData.seed_id + ); + const inRange = right_point > left_point; + const { amount, mft_id } = liquidity; + const amount_is_little = new BigNumber(amount).isLessThan(1000000); + if ( + !( + liquidity.status_in_other_seed == 'staked' || + liquidity.less_than_min_deposit || + !inRange || + (!mft_id && amount_is_little) + ) + ) + return true; + temp_unavailable_new.push(liquidity); + } + ); + temp_free_final = temp_free.concat(frees_extra); + temp_unavailable_final = temp_unavailable_new; + } + const matched_liquidities = temp_farming_final + .concat(temp_free_final) + .concat(temp_unavailable_final); + set_listLiquidities_inFarimg(temp_farming_final); + set_listLiquidities_unFarimg(temp_free_final); + set_listLiquidities_unavailable(temp_unavailable_final); + setListLiquidities(matched_liquidities); + } + if (!user_data_loading) { + setListLiquiditiesLoading(false); + } + } + function sortTokens(tokens: TokenMetadata[]) { + tokens.sort((a: TokenMetadata, b: TokenMetadata) => { + if (a.symbol === 'NEAR') return 1; + if (b.symbol === 'NEAR') return -1; + return 0; + }); + return tokens; + } + const goBacktoFarms = () => { + history.replace('/v2farms'); + emptyDetailData(); + }; + const displaySymbols = () => { + const tokens_sort = sort_tokens_by_base(detailData.pool.tokens_meta_data); + let result = ''; + tokens_sort.forEach((token: TokenMetadata, index: number) => { + const symbol = toRealSymbol(token.symbol); + if (index == detailData.pool.tokens_meta_data.length - 1) { + result += symbol; + } else { + result += symbol + '-'; + } + }); + return result; + }; + const displayImgs = () => { + const tokenList: any[] = []; + const tokens_sort = sort_tokens_by_base(tokens || []); + tokens_sort.forEach((token: TokenMetadata) => { + tokenList.push( + + ); + }); + return tokenList; + }; + const goPoolPage = () => { + const poolId = detailData.pool.pool_id; + const params_str = get_pool_name(poolId); + openUrl(`/poolV2/${params_str}`); + }; + function getBoostMutil() { + if (REF_VE_CONTRACT_ID && !boostConfig) return ''; + const { affected_seeds = {} } = boostConfig || {}; + const { seed_id } = detailData; + const love_user_seed = user_seeds_map[REF_VE_CONTRACT_ID]; + const base = affected_seeds[seed_id]; + if (base && loveSeed) { + const { free_amount = 0, locked_amount = 0 } = love_user_seed || {}; + const totalStakeLoveAmount = toReadableNumber( + LOVE_TOKEN_DECIMAL, + new BigNumber(free_amount).plus(locked_amount).toFixed() + ); + if (+totalStakeLoveAmount > 0) { + let result; + if (+totalStakeLoveAmount < 1) { + result = 1; + } else { + result = new BigNumber(1) + .plus(Math.log(+totalStakeLoveAmount) / Math.log(base)) + .toFixed(2); + } + return result; + } + return ''; + } + return ''; + } + function getBoostDom() { + if (REF_VE_CONTRACT_ID && !boostConfig) return ''; + const { affected_seeds = {} } = boostConfig || {}; + const { seed_id } = detailData; + const base = affected_seeds[seed_id]; + if (base && loveSeed) { + const tip = intl.formatMessage({ id: 'boostFarmTip' }); + const result: string = `
${tip}
`; + return ( +
+
+
+ + +
+ +
+
+ ); + } + } + function getTotalApr() { + const farms = detailData.farmList; + let apr = 0; + const allPendingFarms = isPending(); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { + apr = +new BigNumber(apr).plus(item.apr).toFixed(); + } + }); + return apr * 100; + } + function isPending() { + let pending: boolean = true; + const farms = detailData.farmList; + for (let i = 0; i < farms.length; i++) { + if (farms[i].status != 'Created' && farms[i].status != 'Pending') { + pending = false; + break; + } + } + return pending; + } + function getAprTip() { + // isYour?: Boolean todo 暂时去掉 + const tempList = detailData.farmList; + const lastList: any[] = []; + const pending_farms: FarmBoost[] = []; + const no_pending_farms: FarmBoost[] = []; + let totalApr; + const baseApr = getTotalApr(); + const txt = intl.formatMessage({ id: 'reward_apr' }); + tempList.forEach((farm: FarmBoost) => { + if (farm.status == 'Created') { + pending_farms.push(farm); + } else { + no_pending_farms.push(farm); + } + }); + if (pending_farms.length > 0) { + pending_farms.forEach((farm: FarmBoost) => { + lastList.push({ + rewardToken: farm.token_meta_data, + apr: new BigNumber(farm.apr || 0) + .multipliedBy(100) + .toFixed() + .toString(), + startTime: farm.terms.start_at, + pending: true, + }); + }); + } + if (no_pending_farms.length > 0) { + const mergedFarms = mergeCommonRewardsFarms( + JSON.parse(JSON.stringify(no_pending_farms)) + ); + mergedFarms.forEach((farm: FarmBoost) => { + lastList.push({ + rewardToken: farm.token_meta_data, + apr: new BigNumber(farm.apr || 0) + .multipliedBy(100) + .toFixed() + .toString(), + }); + }); + } + totalApr = baseApr; + // show last display string + let result: string = ''; + result = ` +
+ ${txt} + ${ + toPrecision(totalApr.toString(), 2) + '%' + } +
+ `; + lastList.forEach((item: any) => { + const { rewardToken, apr: baseApr, pending, startTime } = item; + const token = rewardToken; + let itemHtml = ''; + let apr = baseApr; + if (pending) { + const startDate = moment.unix(startTime).format('YYYY-MM-DD'); + const txt = intl.formatMessage({ id: 'start' }); + itemHtml = `
+ +
+ + + +
+
`; + } else { + itemHtml = `
+ + +
`; + } + result += itemHtml; + }); + return result; + } + function mergeCommonRewardsFarms(farms: FarmBoost[]) { + const tempMap = {}; + farms.forEach((farm: FarmBoost) => { + const { reward_token, daily_reward } = farm.terms; + let preMergedfarms: FarmBoost = tempMap[reward_token]; + if (preMergedfarms) { + preMergedfarms.apr = new BigNumber(preMergedfarms.apr || 0) + .plus(farm.apr) + .toFixed() + .toString(); + preMergedfarms.terms.daily_reward = new BigNumber( + preMergedfarms.terms.daily_reward + ) + .plus(daily_reward) + .toFixed(); + } else { + tempMap[reward_token] = farm; + } + }); + return Object.values(tempMap); + } + function getRange() { + const [contractId, temp_pool_id] = detailData.seed_id.split('@'); + const [fixRange, dcl_pool_id, left_point, right_point] = + temp_pool_id.split('&'); + const [token_x_metadata, token_y_metadata] = + detailData.pool.tokens_meta_data; + const decimalRate = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + let left_price = getPriceByPoint(+left_point, decimalRate); + let right_price = getPriceByPoint(+right_point, decimalRate); + if (!rangeSort) { + const temp = left_price; + left_price = new BigNumber(1).dividedBy(right_price).toFixed(); + right_price = new BigNumber(1).dividedBy(temp).toFixed(); + } + const display_left_price = left_price; + const display_right_price = right_price; + return ( +
+
+ { + setRangeSort(!rangeSort); + }} + > + + 1 {rangeSort ? token_x_metadata.symbol : token_y_metadata.symbol}= + +
+
+ + {displayNumberToAppropriateDecimals(display_left_price)} ~{' '} + {displayNumberToAppropriateDecimals(display_right_price)} + + + {rangeSort ? token_y_metadata.symbol : token_x_metadata.symbol} + +
+
+ ); + } + function valueOfRewardsTip() { + const tip = intl.formatMessage({ id: 'farmRewardsCopy' }); + let result: string = `
${tip}
`; + return result; + } + function getRewardsPerWeekTip() { + const tempList: FarmBoost[] = detailData.farmList; + const lastList: any[] = []; + const pending_farms: FarmBoost[] = []; + const no_pending_farms: FarmBoost[] = []; + tempList.forEach((farm: FarmBoost) => { + if (farm.status == 'Created') { + pending_farms.push(farm); + } else { + no_pending_farms.push(farm); + } + }); + if (pending_farms.length > 0) { + pending_farms.forEach((farm: FarmBoost) => { + const { decimals } = farm.token_meta_data; + const weekAmount = toReadableNumber( + decimals, + new BigNumber(farm.terms.daily_reward).multipliedBy(7).toFixed() + ); + lastList.push({ + commonRewardToken: farm.token_meta_data, + commonRewardTotalRewardsPerWeek: weekAmount, + startTime: farm.terms.start_at, + pending: true, + }); + }); + } + if (no_pending_farms.length > 0) { + const mergedFarms = mergeCommonRewardsFarms( + JSON.parse(JSON.stringify(no_pending_farms)) + ); + mergedFarms.forEach((farm: FarmBoost) => { + const { decimals } = farm.token_meta_data; + const weekAmount = toReadableNumber( + decimals, + new BigNumber(farm.terms.daily_reward).multipliedBy(7).toFixed() + ); + lastList.push({ + commonRewardToken: farm.token_meta_data, + commonRewardTotalRewardsPerWeek: weekAmount, + }); + }); + } + // show last display string + const rewards_week_txt = intl.formatMessage({ id: 'rewards_week' }); + let result: string = `
${rewards_week_txt}
`; + let itemHtml: string = ''; + lastList.forEach((item: any) => { + const { + commonRewardToken, + commonRewardTotalRewardsPerWeek, + pending, + startTime, + } = item; + const token = commonRewardToken; + const txt = intl.formatMessage({ id: 'start' }); + function display_number(value: string | number) { + if (!value) return value; + const [whole, decimals] = value.toString().split('.'); + const whole_format = formatWithCommas(whole); + if (+whole < 1 && decimals) { + return whole_format + '.' + decimals; + } else { + return whole_format; + } + } + if (pending) { + itemHtml = `
+
+ +
+ + +
`; + } else { + itemHtml = `
+ + +
`; + } + + result += itemHtml; + }); + return result; + } + function get_total_staked() { + if (+detailData.seedTvl == 0) { + return '-'; + } else { + return `$${toInternationalCurrencySystem(detailData.seedTvl, 2)}`; + } + } + function get_total_apr() { + let apr = getTotalApr(); + if (+apr == 0) { + return '-'; + } else { + return toPrecision(apr.toString(), 2) + '%'; + } + } + function totalTvlPerWeekDisplay() { + const farms = detailData.farmList; + const rewardTokenIconMap = {}; + let totalPrice = 0; + const effectiveFarms = getEffectiveFarmList(farms); + farms.forEach((farm: FarmBoost) => { + const { id, icon } = farm.token_meta_data; + rewardTokenIconMap[id] = icon; + }); + effectiveFarms.forEach((farm: FarmBoost) => { + const { id, decimals } = farm.token_meta_data; + const { daily_reward } = farm.terms; + const tokenPrice = tokenPriceList[id]?.price; + if (tokenPrice && tokenPrice != 'N/A') { + const tokenAmount = toReadableNumber(decimals, daily_reward); + totalPrice += +new BigNumber(tokenAmount) + .multipliedBy(tokenPrice) + .toFixed(); + } + }); + totalPrice = +new BigNumber(totalPrice).multipliedBy(7).toFixed(); + const totalPriceDisplay = + totalPrice == 0 + ? '-' + : '$' + toInternationalCurrencySystem(totalPrice.toString(), 2); + return ( + <> +
+
+ {Object.values(rewardTokenIconMap).map( + (icon: string, index: number) => { + return ( + + ); + } + )} +
+ +
+
+ {totalPriceDisplay} + +
+ + ); + } + function claimReward() { + if (claimLoading) return; + setClaimLoading(true); + claimRewardBySeed_boost(detailData.seed_id) + // .then(() => { + // window.location.reload(); + // }) + .catch((error) => { + setClaimLoading(false); + // setError(error); + }); + } + function rewardRangeTip() { + const tip = intl.formatMessage({ id: 'reward_range_tip' }); + let result: string = `
${tip}
`; + return result; + } + function switchDetailButton() { + setShowDetail(!showDetail); + } + function getBetterSeedSymbols() { + let result = ''; + const tokens = sort_tokens_by_base(detailData.pool.tokens_meta_data); + tokens.forEach((token: TokenMetadata, index: number) => { + const symbol = toRealSymbol(token.symbol); + if (index == detailData.pool.tokens_meta_data.length - 1) { + result += symbol; + } else { + result += symbol + '-'; + } + }); + return result; + } + function goBetterSeed() { + const [contractId, temp_pool_id] = betterSeed.seed_id.split('@'); + const [fixRange, pool_id, left_point, right_point] = + temp_pool_id.split('&'); + const mft_id = `${get_pool_name(pool_id)}[${left_point}-${right_point}]`; + openUrl(`/v2farms/${mft_id}-r`); + } + function getFee() { + const [tokenx, tokeny, fee] = detailData?.pool?.pool_id?.split('|') || ''; + return +fee / 10000 + '%'; + } + const radio = getBoostMutil(); + return ( +
+
+
+ + +
+
+ + +
+
+
+
+ {displayImgs()} +
+
+ + {displaySymbols()} + +
+ + + + + {getFee()} + +
+
+ + +
+ {isEnded ? ( + + + + ) : null} + {radio ? ( +
+ {`x${toPrecision(radio.toString(), 2)}`} +
+ ) : ( + getBoostDom() + )} + {isNewSeed ? ( + + ) : null} +
+
+ + + + + {getFee()} + +
+
+
+ +
+ + +
+
+ {/* new Farm is coming Banner */} + {betterSeed ? ( +
+
+ + {isEnded ? ( + + ) : ( + + )} + + + {getBetterSeedSymbols()} + + + ! + +
+ +
+ ) : null} + + {/* baseData for PC*/} +
+
+
+
+ + { + e.stopPropagation(); + setSeedDclCalcVisible(true); + }} + className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" + /> +
+
+ {get_total_apr()} + +
+
+
+
+ + + +
+ + +
+
+
+ { + setRangeSort(!rangeSort); + }} + > + {getRange()} +
+
+
+
+
+ +
+ + +
+
+
+ {totalTvlPerWeekDisplay()} +
+
+
+ {/* baseData for Mobile*/} +
+
+
+
+ + { + e.stopPropagation(); + setSeedDclCalcVisible(true); + }} + className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" + /> +
+ {get_total_apr()} +
+
+
+
+
+ + + +
+ + +
+
+ {getRange()} +
+
+
+
+ +
+ + +
+
+
+ {totalTvlPerWeekDisplay()} +
+
+
+ {/* login area */} + + {/* unClaimed Rewards for PC */} +
+
+ +
+ + +
+
+
+
+ + {unclaimedRewardsData.worth} + + +
+ { + if (!unclaimedRewardsData.showClaimButton) return; + e.stopPropagation(); + claimReward(); + }} + > + } + /> + +
+
+ {/* unClaimed Rewards for Mobile */} +
+
+
+ +
+ + +
+
+ +
+ + {unclaimedRewardsData.worth} + + +
+
+ {unclaimedRewardsData.showClaimButton ? ( +
+
+ + +
+ { + e.stopPropagation(); + claimReward(); + }} + > + } + /> + +
+ ) : null} + +
+ {unclaimedRewardsData.list.map( + ( + { + token, + amount, + preAmount, + }: { token: TokenMetadata; amount: string; preAmount: string }, + index: number + ) => { + return ( +
+
+ + + {toRealSymbol(token.symbol)} + +
+
+ {preAmount ? ( + <> + {preAmount} + + + + {amount} + + ) : ( + {amount} + )} +
+
+ ); + } + )} +
+
+ {/* Your Positions */} +
+
+
{ + setAddLiquidityModalVisible(true); + }} + className={`flex items-center text-sm text-primaryText p-1.5 cursor-pointer border border-v3HoverDarkBgColor hover:text-white hover:bg-dclButtonBgColor border-dashed rounded-lg ${ + listLiquidities.length > 0 ? '' : 'hidden' + }`} + > + + +
+
+ {listLiquidities.length == 0 && !listLiquiditiesLoading && !isEnded ? ( +
+
+
+ + + + { + setAddLiquidityModalVisible(true); + }} + color="#fff" + borderRadius="8px" + className={`flex-shrink-0 px-4 h-10 text-center text-sm text-white ml-2 xsm:w-full xsm:ml-0 xsm:mt-2.5`} + > + + +
+
+ ) : null} + + {listLiquidities_inFarimg.length > 0 ? ( + <> +
+ +
+ {listLiquidities_inFarimg.map((liquidity: UserLiquidityInfo) => { + return ( + + ); + })} + + ) : null} + + {listLiquidities_unFarimg.length > 0 ? ( + <> +
+ +
+ {listLiquidities_unFarimg.map((liquidity: UserLiquidityInfo) => { + return ( + + ); + })}{' '} + + ) : null} + + {listLiquidities_unavailable.length > 0 ? ( + <> +
{ + const current_status = !show_unavailable_positions; + localStorage.setItem( + 'unavailable_positions', + current_status ? '1' : '0' + ); + set_show_unavailable_positions(current_status); + }} + > + + {show_unavailable_positions ? ( + + ) : ( + + )} + + +
+
+ {listLiquidities_unavailable.map( + (liquidity: UserLiquidityInfo) => { + return ( + + ); + } + )} +
+ + ) : null} +
+
+ {/* add liquidity modal */} + { + setAddLiquidityModalVisible(false); + }} + seed={detailData} + tokenPriceList={tokenPriceList} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > + {/* caculator */} + {seedDclCalcVisible ? ( + { + e.stopPropagation(); + setSeedDclCalcVisible(false); + }} + seed={detailData} + tokenPriceList={tokenPriceList} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + /> + ) : null} +
+ ); +} + +function LiquidityLine(props: { + liquidity: UserLiquidityInfo; + status: StatusType; +}) { + const { liquidity, status } = props; + const { + detailData, + tokenPriceList, + tokens, + mft_balance_in_dcl_account, + rate_need_to_reverse_display, + all_seeds, + isPending, + isEnded, + rangeSort, + } = useContext(FarmContext); + const [nft_stake_loading, set_nft_stake_loading] = useState(false); + const [nft_unStake_loading, set_nft_unStake_loading] = useState(false); + const [hover, setHover] = useState(false); + const [dclCalcVisible, setDclCalcVisible] = useState(false); + const liquidity_status_string: StatusType | string = useMemo(() => { + if (!(liquidity && detailData)) return ''; + const { unfarm_part_amount } = liquidity; + let farmStatus: StatusType = status; + if (status == 'farming' && +unfarm_part_amount > 0) { + farmStatus = 'partialfarming'; + } + return farmStatus; + }, [liquidity, detailData]); + const [ + liquidity_status_display, + liquidity_operation_display, + liquidity_partialfarming_stakebuttonstatus_display, + ]: any = useMemo(() => { + if (!(liquidity && detailData && liquidity_status_string)) + return [null, [], '']; + const part_farm_ratio = liquidity.part_farm_ratio; + let status; + let operation: string[] = []; + let stakeButtonStatus = ''; + if (liquidity_status_string == 'farming') { + status = ( + + + + ); + operation = ['unstake']; + } else if (liquidity_status_string == 'unavailable') { + status = ( + + + + ); + } else if (liquidity_status_string == 'unfarming') { + status = ( + + + + ); + operation = ['stake']; + } else { + const part_farm_ratio_big = new BigNumber(part_farm_ratio); + let percent = '-%'; + if (part_farm_ratio_big.isLessThan(1)) { + percent = '<1%'; + } else { + percent = `${part_farm_ratio_big.toFixed(0, 1)}%`; + } + status = ( + + {percent} Partial Farming + + ); + operation = ['stake', 'unstake']; + if ( + new BigNumber(liquidity.v_liquidity).isLessThan(detailData.min_deposit) + ) { + stakeButtonStatus = 'disable'; + } else { + stakeButtonStatus = 'enable'; + } + } + return [status, operation, stakeButtonStatus]; + }, [liquidity, detailData, liquidity_status_string]); + useEffect(() => { + get_each_token_apr_for_nft(); + }, [detailData, tokenPriceList, liquidity]); + const intl = useIntl(); + + function get_liquidity_value(liquidity: UserLiquidityInfo) { + const { left_point, right_point, amount } = liquidity; + const poolDetail = detailData.pool; + const { token_x, token_y } = poolDetail; + const v = get_total_value_by_liquidity_amount_dcl({ + left_point, + right_point, + poolDetail, + amount, + price_x_y: { + [token_x]: tokenPriceList[token_x]?.price || '0', + [token_y]: tokenPriceList[token_y]?.price || '0', + }, + metadata_x_y: { + [token_x]: tokens[0], + [token_y]: tokens[1], + }, + }); + return v; + } + function get_liquidity_value_display(liquidity: UserLiquidityInfo) { + const v = get_liquidity_value(liquidity); + const v_big = new BigNumber(v); + if (v_big.isLessThan(0.01)) { + return `<$0.01`; + } else { + return `$${formatWithCommas(toPrecision(v.toString(), 2))}`; + } + } + function get_your_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + const [token_x_metadata, token_y_metadata] = + detailData.pool.tokens_meta_data; + const [fixRange, dcl_pool_id, seed_left_point, seed_right_point] = + detailData.seed_id.split('@')[1].split('&'); + let icon; + const radio = get_intersection_radio({ + left_point_liquidity: left_point, + right_point_liquidity: right_point, + left_point_seed: seed_left_point, + right_point_seed: seed_right_point, + }); + const p = new BigNumber(radio); + const Icon = get_intersection_icon_by_radio(radio); + icon = ; + const decimalRate = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + let left_price = getPriceByPoint(+left_point, decimalRate); + let right_price = getPriceByPoint(+right_point, decimalRate); + if (!rangeSort) { + const temp = left_price; + left_price = new BigNumber(1).dividedBy(right_price).toFixed(); + right_price = new BigNumber(1).dividedBy(temp).toFixed(); + } + const display_left_price = left_price; + const display_right_price = right_price; + function rangeTip() { + // const tip = intl.formatMessage({ id: 'farmRewardsCopy' }); + const tip = + 'The intersection of your price range and the farm reward range.'; + let result: string = `
${tip}
`; + return result; + } + function displayP(p: BigNumber) { + if (p.isEqualTo(0)) { + return '0%'; + } else if (p.isLessThan(1)) { + return '1%'; + } else { + return p?.toFixed(0, 1) + '%'; + } + } + return ( +
+ + {displayNumberToAppropriateDecimals(display_left_price)} ~{' '} + {displayNumberToAppropriateDecimals(display_right_price)} + +
+ + {icon} + + + +
+
+ ); + } + function get_each_token_apr_for_nft() { + const { farmList, total_seed_amount, total_seed_power } = detailData; + // principal + const total_principal = get_liquidity_value(liquidity); + // lp percent + let percent: BigNumber = new BigNumber(0); + const mint_amount = mint_liquidity(liquidity, detailData.seed_id); + const part_farm_ratio = liquidity.part_farm_ratio; + if (+part_farm_ratio == 100) { + // full farming + if (+total_seed_power > 0) { + percent = new BigNumber(mint_amount).dividedBy(total_seed_power); + } + } else if (+part_farm_ratio > 0 && +part_farm_ratio < 100) { + // partial Farming + if (+total_seed_power > 0) { + const partial_amount = new BigNumber(mint_amount) + .multipliedBy(part_farm_ratio) + .dividedBy(100); + percent = partial_amount.dividedBy(total_seed_power); + } + } else { + // unFarming, unavailable + const temp_total = new BigNumber(total_seed_power || 0).plus(mint_amount); + if (temp_total.isGreaterThan(0)) { + percent = new BigNumber(mint_amount).dividedBy(temp_total); + } + } + // get apr per token + const farmList_parse = JSON.parse(JSON.stringify(farmList || [])); + farmList_parse.forEach((farm: FarmBoost) => { + const { token_meta_data } = farm; + const { daily_reward, reward_token } = farm.terms; + const quantity = toReadableNumber(token_meta_data.decimals, daily_reward); + const reward_token_price = Number( + tokenPriceList[reward_token]?.price || 0 + ); + const cur_token_rewards = new BigNumber(quantity) + .multipliedBy(reward_token_price) + .multipliedBy(365); + const user_can_get_token_rewards = new BigNumber( + cur_token_rewards + ).multipliedBy(percent); + if (+total_principal > 0) { + farm.yourNFTApr = new BigNumber(user_can_get_token_rewards) + .dividedBy(total_principal) + .toFixed(); + } else { + farm.yourNFTApr = '0'; + } + }); + liquidity.farmList = farmList_parse; + } + function get_your_total_apr() { + const farms = liquidity.farmList || []; + let apr = new BigNumber(0); + const allPendingFarms = isPending(); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || !pendingFarm) { + apr = new BigNumber(apr).plus(item.yourNFTApr || 0); + } + }); + return apr.multipliedBy(100).toFixed(); + } + function display_your_apr() { + const total_apr = new BigNumber(get_your_total_apr()); + if (total_apr.isEqualTo('0')) { + return '0%'; + } else if (total_apr.isLessThan(0.01)) { + return `<0.01%`; + } else { + return `${toPrecision(total_apr.toFixed(), 2)}%`; + } + } + function get_your_apr_copy(liquidity: UserLiquidityInfo) { + const { farmList, total_seed_amount, total_seed_power } = detailData; + // principal + const total_principal = get_liquidity_value(liquidity); + // seed total rewards + let total_rewards = '0'; + farmList.forEach((farm: FarmBoost) => { + const { token_meta_data } = farm; + const { daily_reward, reward_token } = farm.terms; + const quantity = toReadableNumber(token_meta_data.decimals, daily_reward); + const reward_token_price = Number( + tokenPriceList[reward_token]?.price || 0 + ); + const cur_token_rewards = new BigNumber(quantity) + .multipliedBy(reward_token_price) + .multipliedBy(365); + total_rewards = cur_token_rewards.plus(total_rewards).toFixed(); + }); + // lp percent + let percent: BigNumber = new BigNumber(0); + const mint_amount = mint_liquidity(liquidity, detailData.seed_id); + const part_farm_ratio = liquidity.part_farm_ratio; + if (+part_farm_ratio == 100) { + // full farming + if (+total_seed_power > 0) { + percent = new BigNumber(mint_amount).dividedBy(total_seed_power); + } + } else if (+part_farm_ratio > 0 && +part_farm_ratio < 100) { + // partial Farming + if (+total_seed_power > 0) { + const partial_amount = new BigNumber(mint_amount) + .multipliedBy(part_farm_ratio) + .dividedBy(100); + percent = partial_amount.dividedBy(total_seed_power); + } + } else { + // unFarming, unavailable + const temp_total = new BigNumber(total_seed_power || 0).plus(mint_amount); + if (temp_total.isGreaterThan(0)) { + percent = new BigNumber(mint_amount).dividedBy(temp_total); + } + } + // profit + let profit; + if (percent) { + profit = percent.multipliedBy(total_rewards); + } + // your apr + if (profit) { + if (+total_principal == 0) { + return 0; + } + const your_apr = profit.dividedBy(total_principal).multipliedBy(100); + if (your_apr.isEqualTo('0')) { + return '0%'; + } else if (your_apr.isLessThan(0.01)) { + return `<0.01%`; + } else { + return `${toPrecision(your_apr.toFixed(), 2)}%`; + } + } else { + return '-'; + } + } + function getYourLiquidityAprTip() { + const { farmList } = liquidity; + if (!farmList) return ''; + const tempList = farmList; + const lastList: any[] = []; + const pending_farms: FarmBoost[] = []; + const no_pending_farms: FarmBoost[] = []; + const totalApr = get_your_total_apr(); + const txt = intl.formatMessage({ id: 'reward_apr' }); + tempList.forEach((farm: FarmBoost) => { + if (farm.status == 'Created') { + pending_farms.push(farm); + } else { + no_pending_farms.push(farm); + } + }); + if (pending_farms.length > 0) { + pending_farms.forEach((farm: FarmBoost) => { + lastList.push({ + rewardToken: farm.token_meta_data, + apr: new BigNumber(farm.yourNFTApr || 0) + .multipliedBy(100) + .toFixed() + .toString(), + startTime: farm.terms.start_at, + pending: true, + }); + }); + } + if (no_pending_farms.length > 0) { + const mergedFarms = mergeCommonRewardsFarms( + JSON.parse(JSON.stringify(no_pending_farms)) + ); + mergedFarms.forEach((farm: FarmBoost) => { + lastList.push({ + rewardToken: farm.token_meta_data, + apr: new BigNumber(farm.yourNFTApr || 0) + .multipliedBy(100) + .toFixed() + .toString(), + }); + }); + } + // show last display string + let result: string = ''; + result = ` +
+ ${txt} + ${ + toPrecision(totalApr.toString(), 2) + '%' + } +
+ `; + lastList.forEach((item: any) => { + const { rewardToken, apr, pending, startTime } = item; + const token = rewardToken; + let itemHtml = ''; + if (pending) { + const startDate = moment.unix(startTime).format('YYYY-MM-DD'); + const txt = intl.formatMessage({ id: 'start' }); + itemHtml = `
+ +
+ + + +
+
`; + } else { + itemHtml = `
+ + +
`; + } + result += itemHtml; + }); + return result; + } + function mergeCommonRewardsFarms(farms: FarmBoost[]) { + const tempMap = {}; + farms.forEach((farm: FarmBoost) => { + const { reward_token, daily_reward } = farm.terms; + let preMergedfarms: FarmBoost = tempMap[reward_token]; + if (preMergedfarms) { + preMergedfarms.yourNFTApr = new BigNumber( + preMergedfarms.yourNFTApr || 0 + ) + .plus(farm.yourNFTApr) + .toFixed() + .toString(); + preMergedfarms.terms.daily_reward = new BigNumber( + preMergedfarms.terms.daily_reward + ) + .plus(daily_reward) + .toFixed(); + } else { + tempMap[reward_token] = farm; + } + }); + return Object.values(tempMap); + } + function stakeNFT(liquidity: UserLiquidityInfo) { + set_nft_stake_loading(true); + let finalAmount; + const { seed_id, min_deposit } = detailData; + const { mft_id, part_farm_ratio, unfarm_part_amount } = liquidity; + const v_liquidity = mint_liquidity(liquidity, seed_id); + let needUnstake = false; + let withdraw_amount = '0'; + if (!mft_id || new BigNumber(part_farm_ratio || 0).isEqualTo(0)) { + finalAmount = v_liquidity; + } else { + if (new BigNumber(unfarm_part_amount).isLessThan(min_deposit)) { + needUnstake = true; + withdraw_amount = new BigNumber(v_liquidity) + .minus(unfarm_part_amount) + .toFixed(); + } else { + finalAmount = unfarm_part_amount; + } + } + stake_boost_nft({ + liquidity, + seed_id, + stakeAmount: finalAmount, + withdraw_amount, + needUnstake, + }); + } + function unStakeNFT(liquidity: UserLiquidityInfo) { + set_nft_unStake_loading(true); + const amount = liquidity.v_liquidity; + const mft_balance_big = new BigNumber(mft_balance_in_dcl_account); + const amount_big = new BigNumber(amount); + let finalAmount = '0'; + if (mft_balance_big.isEqualTo('0')) { + finalAmount = amount; + } else if (mft_balance_big.isGreaterThanOrEqualTo(amount)) { + finalAmount = '0'; + } else { + finalAmount = amount_big.minus(mft_balance_in_dcl_account).toFixed(); + } + unStake_boost_nft({ + lpt_id: liquidity.lpt_id, + seed_id: detailData.seed_id, + withdraw_amount: finalAmount, + }); + } + function goYourLiquidityDetail(liquidity: UserLiquidityInfo) { + liquidity.lpt_id.split('#')[0]; + const pool_id = liquidity.lpt_id.split('#')[0]; + const lpt_index = liquidity.lpt_id.split('#')[1]; + openUrl(`/yoursLiquidityDetailV2/${get_pool_name(pool_id)}@${lpt_index}`); + } + function unavailableTip() { + const tip = unavailableText(); + const result: string = `
${tip}
`; + return result; + } + function unavailableText() { + let tip = ''; + const { seed_id, min_deposit } = detailData; + const [left_point, right_point] = get_valid_range(liquidity, seed_id); + const inrange = +right_point > +left_point; + if (!inrange) { + tip = intl.formatMessage({ id: 'your_price_range_tip' }); + } else if (liquidity.status_in_other_seed == 'staked') { + tip = intl.formatMessage({ id: 'position_has_staked_tip' }); + } else { + const v_liquidity = mint_liquidity(liquidity, seed_id); + const rate = new BigNumber(min_deposit).dividedBy(v_liquidity); + let rate_display = rate.toFixed(1); + if (rate.isGreaterThan(rate_display)) { + rate_display = new BigNumber(rate_display).plus(0.1).toFixed(); + } + // your liquidity + tip = + intl.formatMessage({ id: 'minimum_tip' }) + + ' ' + + `${rate_display}x` + + ' ' + + intl.formatMessage({ id: 'your_liquidity_3' }); + } + return tip; + } + function unavailableDiv() { + let tip; + const { seed_id, min_deposit } = detailData; + const [left_point, right_point] = get_valid_range(liquidity, seed_id); + const inrange = +right_point > +left_point; + if (!inrange) { + tip = intl.formatMessage({ id: 'your_price_range_tip' }); + } else if (liquidity.status_in_other_seed == 'staked') { + const { mft_id } = liquidity; + const link: string = get_target_seed_url_link({ + all_seeds, + mft_id, + }); + tip = ( + <> + {' '} +
{ + openUrl(link); + }} + > + + + + +
+ + ); + } else { + const v_liquidity = mint_liquidity(liquidity, seed_id); + const rate = new BigNumber(min_deposit).dividedBy(v_liquidity); + let rate_display = rate.toFixed(1); + if (rate.isGreaterThan(rate_display)) { + rate_display = new BigNumber(rate_display).plus(0.1).toFixed(); + } + tip = + intl.formatMessage({ id: 'minimum_tip' }) + + ' ' + + `${rate_display}x` + + ' ' + + intl.formatMessage({ id: 'your_liquidity_3' }); + } + return tip; + } + function get_target_seed_url_link({ + all_seeds, + mft_id, + }: { + all_seeds: Seed[]; + mft_id: string; + }) { + const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); + const temps = all_seeds.filter((seed: Seed) => { + return seed.seed_id == seed_id; + }); + temps.sort((b: Seed, a: Seed) => { + if (b.farmList[0].status == 'Ended') return 1; + if (a.farmList[0].status == 'Ended') return -1; + return 0; + }); + const target = temps[0]; + const [fixRange, pool_id, left_point, right_point] = mft_id.split('&'); + const status = target?.farmList[0].status == 'Ended' ? 'e' : 'r'; + const params = `${get_pool_name(pool_id)}[${left_point}-${right_point}]`; + const link = `/v2farms/${params}-${status}`; + return link; + } + function apr_title() { + if ( + liquidity_status_string == 'farming' || + liquidity_status_string == 'partialfarming' + ) { + return ; + } else { + return ; + } + } + const showStakeButton = + !isEnded && liquidity_operation_display.indexOf('stake') > -1; + const showUnStakeButton = liquidity_operation_display.indexOf('unstake') > -1; + return ( + <> + {/* for PC */} +
{ + setHover(false); + }} + > +
+ + + NFT ID #{liquidity.lpt_id.split('#')[1]} + +
+
+
setHover(true)} + className="flex items-stretch justify-between pt-7 pb-3.5 px-6" + > +
+ + + + + {get_liquidity_value_display(liquidity)} + +
+
+ + + + + {get_your_range(liquidity)} + +
+
+
+ {apr_title()} + { + e.stopPropagation(); + setDclCalcVisible(true); + }} + className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" + /> +
+
+ + {display_your_apr()} + + +
+
+
+ + + +
+ {liquidity_status_display} +
+
+
+
+
{ + goYourLiquidityDetail(liquidity); + }} + className="flex items-center text-sm text-primaryText hover:text-white cursor-pointer" + > + + + + +
+ {liquidity_status_string == 'unavailable' ? ( +
+ {unavailableDiv()} +
+ ) : null} +
+
+ { + stakeNFT(liquidity); + }} + color="#fff" + disabled={ + nft_stake_loading || + liquidity_partialfarming_stakebuttonstatus_display == + 'disable' + ? true + : false + } + btnClassName={ + nft_stake_loading || + liquidity_partialfarming_stakebuttonstatus_display == + 'disable' + ? 'cursor-not-allowed' + : '' + } + minWidth="6rem" + className={`h-8 px-4 text-center text-sm text-white focus:outline-none ${ + nft_stake_loading || + liquidity_partialfarming_stakebuttonstatus_display == + 'disable' + ? 'opacity-40' + : '' + } ${showStakeButton ? '' : 'hidden'}`} + > + ( + + )} + /> + + +
+ { + unStakeNFT(liquidity); + }} + color="#fff" + minWidth="6rem" + disabled={nft_unStake_loading ? true : false} + className={`flex items-center justify-center h-8 px-4 ml-2.5 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ + nft_unStake_loading ? 'opacity-40' : '' + } ${showUnStakeButton ? '' : 'hidden'}`} + > + ( + + )} + /> + +
+
+
+
+ {/* for Mobile */} +
+
+ + + NFT ID #{liquidity.lpt_id.split('#')[1]} + +
+
+
+ + + + + {get_liquidity_value_display(liquidity)} + +
+
+ + + + + {get_your_range(liquidity)} + +
+
+
+ {apr_title()} + { + e.stopPropagation(); + setDclCalcVisible(true); + }} + className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" + /> +
+ + {display_your_apr()} + +
+
+ + + +
+ {liquidity_status_display} +
+
+
+
+
+
+ { + stakeNFT(liquidity); + }} + color="#fff" + disabled={ + nft_stake_loading || + liquidity_partialfarming_stakebuttonstatus_display == + 'disable' || + !showStakeButton + } + className={`h-10 px-4 text-center text-sm text-white focus:outline-none ${ + nft_stake_loading || + liquidity_partialfarming_stakebuttonstatus_display == + 'disable' || + !showStakeButton + ? 'opacity-40' + : '' + }`} + > + ( + + )} + /> + + +
+ { + unStakeNFT(liquidity); + }} + color="#fff" + disabled={nft_unStake_loading ? true : false} + className={`flex items-center justify-center w-1 flex-grow h-10 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ml-2.5 ${ + nft_unStake_loading ? 'opacity-40' : '' + } ${showUnStakeButton ? '' : 'hidden'}`} + > + ( + + )} + /> + +
+ {liquidity_status_string == 'unavailable' ? ( +
+ {unavailableDiv()} +
+ ) : null} +
{ + goYourLiquidityDetail(liquidity); + }} + className="flex items-center text-sm text-primaryText hover:text-white cursor-pointer mt-4" + > + + + + +
+
+
+ {dclCalcVisible ? ( + { + e.stopPropagation(); + setDclCalcVisible(false); + }} + seed={detailData} + liquidity={liquidity} + tokenPriceList={tokenPriceList} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + /> + ) : null} + + ); +} +function AddLoginEntryBar() { + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + if (isSignedIn) return null; + return ( +
+
+
+ +
+
+ ); +} +type StatusType = 'farming' | 'unavailable' | 'unfarming' | 'partialfarming'; From b44e10b8757b2f66ba2d6ca50ea44ea33cac6b8b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 13 Jun 2023 15:22:07 +0800 Subject: [PATCH 022/204] update --- src/Content.tsx | 5 + src/components/d3Chart/DclChart.tsx | 4 +- src/components/farm/FarmsDclDetail.tsx | 1293 +++--------------------- src/components/farm/utils.ts | 60 ++ src/pages/farms/FarmsBoostPageCopy.tsx | 100 ++ 5 files changed, 302 insertions(+), 1160 deletions(-) create mode 100644 src/components/farm/utils.ts create mode 100644 src/pages/farms/FarmsBoostPageCopy.tsx diff --git a/src/Content.tsx b/src/Content.tsx index 8f4be74b8..788489551 100644 --- a/src/Content.tsx +++ b/src/Content.tsx @@ -11,6 +11,7 @@ import { ReferendumPage } from '~pages/ReferendumPage'; import FarmsMigrate from '~pages/farms/FarmsMigrate'; import FarmsBoosterPage from '~pages/farms/FarmsBoostPage'; +import FarmsBoosterPageCopy from '~pages/farms/FarmsBoostPageCopy'; import YourLiquidityPageV3 from './pages/poolsV3/YourLiquidityPageV3'; import AddYourLiquidityPageV3 from './pages/poolsV3/AddYourLiquidityPageV3'; import YourLiquidityDetailV3 from './pages/poolsV3/YourLiquidityDetailV3'; @@ -263,6 +264,10 @@ export function Content() { path="/v2farms/:id?" component={AutoHeight(FarmsBoosterPage)} /> + diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index f82f034b5..9876c5d9f 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -95,7 +95,7 @@ export default function DclChart({ } }, [pool, accountId]); useEffect(() => { - if (price_range && chartDataList?.length) { + if (price_range && chartDataList) { drawChart(); } }, [price_range, chartDataList]); @@ -737,7 +737,6 @@ export default function DclChart({ ); const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; - console.log('e.x', e.x); const p = scale.invert(e.x); const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); setDragRightPoint(newRightPoint); @@ -842,7 +841,6 @@ export default function DclChart({ }); const sortY = sortBy(Y); const sortX = sortBy(X); - console.log('sortX', sortX); // const max_x = sortX[X.length - 1] + getConfig().bin * pool.point_delta; // sortX.push(max_x); const sortX_map_P = sortX.map((x) => { diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 7dda4ca30..8396d714a 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -73,7 +73,9 @@ import { list_liquidities, dcl_mft_balance_of } from '../../services/swapV3'; import { AddNewPoolV3 } from '~components/pool/AddNewPoolV3'; import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; import CalcModelDcl from '../../components/farm/CalcModelDcl'; +import { formatWithCommas_usd, formatPercentage } from './utils'; import moment from 'moment'; +import Big from 'big.js'; const ONLY_ZEROS = /^0*\.?0*$/; const { REF_VE_CONTRACT_ID, REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const FarmContext = createContext(null); @@ -112,8 +114,6 @@ export default function FarmsDclDetail(props: { useState([]); const [listLiquiditiesLoading, setListLiquiditiesLoading] = useState(true); const [rangeSort, setRangeSort] = useState(true); - const [addLiquidityModalVisible, setAddLiquidityModalVisible] = - useState(false); const [mft_balance_in_dcl_account, set_mft_balance_in_dcl_account] = useState('0'); const [claimLoading, setClaimLoading] = useState(false); @@ -121,10 +121,8 @@ export default function FarmsDclDetail(props: { const [betterSeed, setBetterSeed] = useState(); const [isNewSeed, setIsNewSeed] = useState(false); const [seedDclCalcVisible, setSeedDclCalcVisible] = useState(false); - const s = localStorage.getItem('unavailable_positions'); - const [show_unavailable_positions, set_show_unavailable_positions] = useState( - s == '1' ? true : false - ); + const [nft_stake_loading, set_nft_stake_loading] = useState(false); + const [nft_unStake_loading, set_nft_unStake_loading] = useState(false); const { user_seeds_map = {}, user_unclaimed_map = {}, @@ -167,6 +165,21 @@ export default function FarmsDclDetail(props: { const unclaimedRewardsData = useMemo(() => { return getTotalUnclaimedRewards(); }, [user_unclaimed_map[detailData.seed_id]]); + console.log('99999999999', listLiquidities_inFarimg); + console.log('listLiquiditiesLoading', listLiquiditiesLoading); + const [yp_percent, yp_canFarm_value, yp_unFarm_value] = useMemo(() => { + if (!listLiquiditiesLoading) { + const { total_value, can_farm_parts_value, un_farm_parts_value } = caculate_values(); + if (total_value.gt(0)) { + const percent = can_farm_parts_value.div(total_value).mul(100); + return [formatPercentage(percent.toFixed()), formatWithCommas_usd(can_farm_parts_value.toFixed()), formatWithCommas_usd(un_farm_parts_value.toFixed())] + } else { + return ['0%', '$0', '$0'] + } + } else { + return ['0%', '$0', '$0'] + } + }, [listLiquiditiesLoading, listLiquidities_inFarimg.length, listLiquidities_unFarimg.length]) function getTotalUnclaimedRewards() { let totalPrice = 0; let resultTip = ''; @@ -383,6 +396,7 @@ export default function FarmsDclDetail(props: { set_listLiquidities_unFarimg(temp_free_final); set_listLiquidities_unavailable(temp_unavailable_final); setListLiquidities(matched_liquidities); + console.log('temp_farming_final, temp_unavailable_final, temp_free_final', temp_farming_final, temp_unavailable_final, temp_free_final); } if (!user_data_loading) { setListLiquiditiesLoading(false); @@ -912,7 +926,65 @@ export default function FarmsDclDetail(props: { const [tokenx, tokeny, fee] = detailData?.pool?.pool_id?.split('|') || ''; return +fee / 10000 + '%'; } + function get_liquidity_value(liquidity: UserLiquidityInfo, leftPoint?:number, rightPoint?:number) { + const { amount } = liquidity; + const poolDetail = detailData.pool; + const { token_x, token_y } = poolDetail; + const v = get_total_value_by_liquidity_amount_dcl({ + left_point:leftPoint || liquidity.left_point, + right_point:rightPoint || liquidity.right_point, + poolDetail, + amount, + price_x_y: { + [token_x]: tokenPriceList[token_x]?.price || '0', + [token_y]: tokenPriceList[token_y]?.price || '0', + }, + metadata_x_y: { + [token_x]: tokens[0], + [token_y]: tokens[1], + }, + }); + return v; + } const radio = getBoostMutil(); + /** new start */ + function caculate_values() { + const can_farm_liquidities = listLiquidities_inFarimg.concat(listLiquidities_unFarimg); + // 可以farm的所有流动性的总价值 + let total_value = Big(0); + can_farm_liquidities.forEach((l:UserLiquidityInfo) => { + const l_v = get_liquidity_value(l); + total_value = total_value.plus(l_v || 0); + }) + // 能farm部分的的流动性的总价值 + let can_farm_parts_value = Big(0); + can_farm_liquidities.forEach((l:UserLiquidityInfo) => { + const [left_point, right_point] = get_valid_range(l,detailData.seed_id); + const l_v = get_liquidity_value(l, left_point, right_point); + can_farm_parts_value = can_farm_parts_value.plus(l_v || 0); + }) + // unFarming 部分流动性的总价值(包含部分在farm中的那个NFT剩余的部分) + let un_farm_parts_value = Big(0); + listLiquidities_unFarimg.forEach((l:UserLiquidityInfo) => { + const l_v = get_liquidity_value(l); + un_farm_parts_value = un_farm_parts_value.plus(l_v || 0); + }) + const [ part_farm_liquidity ] = listLiquidities_inFarimg.filter((l:UserLiquidityInfo) => { + return Big(l.part_farm_ratio || 0).gt(0); + }) + if (part_farm_liquidity) { + const [left_point, right_point] = get_valid_range(part_farm_liquidity,detailData.seed_id); + const v = get_liquidity_value(part_farm_liquidity, left_point, right_point); + const un_farm_part_value = Big(100).minus(part_farm_liquidity.part_farm_ratio).div(100).mul(v) + un_farm_parts_value = un_farm_parts_value.plus(un_farm_part_value); + } + return { + total_value, + can_farm_parts_value, + un_farm_parts_value, + } + } + /** new end */ return (
@@ -1023,7 +1095,6 @@ export default function FarmsDclDetail(props: {
) : null} - {/* baseData for PC*/}
@@ -1192,6 +1263,51 @@ export default function FarmsDclDetail(props: {
{/* login area */} + {/* Your Farming Position */} +
+
+
+ Your Farming Position + {yp_canFarm_value} +
+
{yp_percent} of your total liquidity
+
+
+
{yp_unFarm_value} available to stake
+
+ + ( + + )} + /> + + + ( + + )} + /> + +
+
+
{/* unClaimed Rewards for PC */}
- {/* Your Positions */} -
-
-
{ - setAddLiquidityModalVisible(true); - }} - className={`flex items-center text-sm text-primaryText p-1.5 cursor-pointer border border-v3HoverDarkBgColor hover:text-white hover:bg-dclButtonBgColor border-dashed rounded-lg ${ - listLiquidities.length > 0 ? '' : 'hidden' - }`} - > - - -
-
- {listLiquidities.length == 0 && !listLiquiditiesLoading && !isEnded ? ( -
-
-
- - - - { - setAddLiquidityModalVisible(true); - }} - color="#fff" - borderRadius="8px" - className={`flex-shrink-0 px-4 h-10 text-center text-sm text-white ml-2 xsm:w-full xsm:ml-0 xsm:mt-2.5`} - > - - -
-
- ) : null} - - {listLiquidities_inFarimg.length > 0 ? ( - <> -
- -
- {listLiquidities_inFarimg.map((liquidity: UserLiquidityInfo) => { - return ( - - ); - })} - - ) : null} - - {listLiquidities_unFarimg.length > 0 ? ( - <> -
- -
- {listLiquidities_unFarimg.map((liquidity: UserLiquidityInfo) => { - return ( - - ); - })}{' '} - - ) : null} - - {listLiquidities_unavailable.length > 0 ? ( - <> -
{ - const current_status = !show_unavailable_positions; - localStorage.setItem( - 'unavailable_positions', - current_status ? '1' : '0' - ); - set_show_unavailable_positions(current_status); - }} - > - - {show_unavailable_positions ? ( - - ) : ( - - )} - - -
-
- {listLiquidities_unavailable.map( - (liquidity: UserLiquidityInfo) => { - return ( - - ); - } - )} -
- - ) : null} -
-
- {/* add liquidity modal */} - { - setAddLiquidityModalVisible(false); - }} - seed={detailData} - tokenPriceList={tokenPriceList} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > {/* caculator */} {seedDclCalcVisible ? ( ); } - -function LiquidityLine(props: { - liquidity: UserLiquidityInfo; - status: StatusType; -}) { - const { liquidity, status } = props; - const { - detailData, - tokenPriceList, - tokens, - mft_balance_in_dcl_account, - rate_need_to_reverse_display, - all_seeds, - isPending, - isEnded, - rangeSort, - } = useContext(FarmContext); - const [nft_stake_loading, set_nft_stake_loading] = useState(false); - const [nft_unStake_loading, set_nft_unStake_loading] = useState(false); - const [hover, setHover] = useState(false); - const [dclCalcVisible, setDclCalcVisible] = useState(false); - const liquidity_status_string: StatusType | string = useMemo(() => { - if (!(liquidity && detailData)) return ''; - const { unfarm_part_amount } = liquidity; - let farmStatus: StatusType = status; - if (status == 'farming' && +unfarm_part_amount > 0) { - farmStatus = 'partialfarming'; - } - return farmStatus; - }, [liquidity, detailData]); - const [ - liquidity_status_display, - liquidity_operation_display, - liquidity_partialfarming_stakebuttonstatus_display, - ]: any = useMemo(() => { - if (!(liquidity && detailData && liquidity_status_string)) - return [null, [], '']; - const part_farm_ratio = liquidity.part_farm_ratio; - let status; - let operation: string[] = []; - let stakeButtonStatus = ''; - if (liquidity_status_string == 'farming') { - status = ( - - - - ); - operation = ['unstake']; - } else if (liquidity_status_string == 'unavailable') { - status = ( - - - - ); - } else if (liquidity_status_string == 'unfarming') { - status = ( - - - - ); - operation = ['stake']; - } else { - const part_farm_ratio_big = new BigNumber(part_farm_ratio); - let percent = '-%'; - if (part_farm_ratio_big.isLessThan(1)) { - percent = '<1%'; - } else { - percent = `${part_farm_ratio_big.toFixed(0, 1)}%`; - } - status = ( - - {percent} Partial Farming - - ); - operation = ['stake', 'unstake']; - if ( - new BigNumber(liquidity.v_liquidity).isLessThan(detailData.min_deposit) - ) { - stakeButtonStatus = 'disable'; - } else { - stakeButtonStatus = 'enable'; - } - } - return [status, operation, stakeButtonStatus]; - }, [liquidity, detailData, liquidity_status_string]); - useEffect(() => { - get_each_token_apr_for_nft(); - }, [detailData, tokenPriceList, liquidity]); - const intl = useIntl(); - - function get_liquidity_value(liquidity: UserLiquidityInfo) { - const { left_point, right_point, amount } = liquidity; - const poolDetail = detailData.pool; - const { token_x, token_y } = poolDetail; - const v = get_total_value_by_liquidity_amount_dcl({ - left_point, - right_point, - poolDetail, - amount, - price_x_y: { - [token_x]: tokenPriceList[token_x]?.price || '0', - [token_y]: tokenPriceList[token_y]?.price || '0', - }, - metadata_x_y: { - [token_x]: tokens[0], - [token_y]: tokens[1], - }, - }); - return v; - } - function get_liquidity_value_display(liquidity: UserLiquidityInfo) { - const v = get_liquidity_value(liquidity); - const v_big = new BigNumber(v); - if (v_big.isLessThan(0.01)) { - return `<$0.01`; - } else { - return `$${formatWithCommas(toPrecision(v.toString(), 2))}`; - } - } - function get_your_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - const [token_x_metadata, token_y_metadata] = - detailData.pool.tokens_meta_data; - const [fixRange, dcl_pool_id, seed_left_point, seed_right_point] = - detailData.seed_id.split('@')[1].split('&'); - let icon; - const radio = get_intersection_radio({ - left_point_liquidity: left_point, - right_point_liquidity: right_point, - left_point_seed: seed_left_point, - right_point_seed: seed_right_point, - }); - const p = new BigNumber(radio); - const Icon = get_intersection_icon_by_radio(radio); - icon = ; - const decimalRate = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - let left_price = getPriceByPoint(+left_point, decimalRate); - let right_price = getPriceByPoint(+right_point, decimalRate); - if (!rangeSort) { - const temp = left_price; - left_price = new BigNumber(1).dividedBy(right_price).toFixed(); - right_price = new BigNumber(1).dividedBy(temp).toFixed(); - } - const display_left_price = left_price; - const display_right_price = right_price; - function rangeTip() { - // const tip = intl.formatMessage({ id: 'farmRewardsCopy' }); - const tip = - 'The intersection of your price range and the farm reward range.'; - let result: string = `
${tip}
`; - return result; - } - function displayP(p: BigNumber) { - if (p.isEqualTo(0)) { - return '0%'; - } else if (p.isLessThan(1)) { - return '1%'; - } else { - return p?.toFixed(0, 1) + '%'; - } - } - return ( -
- - {displayNumberToAppropriateDecimals(display_left_price)} ~{' '} - {displayNumberToAppropriateDecimals(display_right_price)} - -
- - {icon} - - - -
+function AddLoginEntryBar() { + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + if (isSignedIn) return null; + return ( +
+
+
+
- ); - } - function get_each_token_apr_for_nft() { - const { farmList, total_seed_amount, total_seed_power } = detailData; - // principal - const total_principal = get_liquidity_value(liquidity); - // lp percent - let percent: BigNumber = new BigNumber(0); - const mint_amount = mint_liquidity(liquidity, detailData.seed_id); - const part_farm_ratio = liquidity.part_farm_ratio; - if (+part_farm_ratio == 100) { - // full farming - if (+total_seed_power > 0) { - percent = new BigNumber(mint_amount).dividedBy(total_seed_power); - } - } else if (+part_farm_ratio > 0 && +part_farm_ratio < 100) { - // partial Farming - if (+total_seed_power > 0) { - const partial_amount = new BigNumber(mint_amount) - .multipliedBy(part_farm_ratio) - .dividedBy(100); - percent = partial_amount.dividedBy(total_seed_power); - } - } else { - // unFarming, unavailable - const temp_total = new BigNumber(total_seed_power || 0).plus(mint_amount); - if (temp_total.isGreaterThan(0)) { - percent = new BigNumber(mint_amount).dividedBy(temp_total); - } - } - // get apr per token - const farmList_parse = JSON.parse(JSON.stringify(farmList || [])); - farmList_parse.forEach((farm: FarmBoost) => { - const { token_meta_data } = farm; - const { daily_reward, reward_token } = farm.terms; - const quantity = toReadableNumber(token_meta_data.decimals, daily_reward); - const reward_token_price = Number( - tokenPriceList[reward_token]?.price || 0 - ); - const cur_token_rewards = new BigNumber(quantity) - .multipliedBy(reward_token_price) - .multipliedBy(365); - const user_can_get_token_rewards = new BigNumber( - cur_token_rewards - ).multipliedBy(percent); - if (+total_principal > 0) { - farm.yourNFTApr = new BigNumber(user_can_get_token_rewards) - .dividedBy(total_principal) - .toFixed(); - } else { - farm.yourNFTApr = '0'; - } - }); - liquidity.farmList = farmList_parse; - } - function get_your_total_apr() { - const farms = liquidity.farmList || []; - let apr = new BigNumber(0); - const allPendingFarms = isPending(); - farms.forEach(function (item: FarmBoost) { - const pendingFarm = item.status == 'Created' || item.status == 'Pending'; - if (allPendingFarms || !pendingFarm) { - apr = new BigNumber(apr).plus(item.yourNFTApr || 0); - } - }); - return apr.multipliedBy(100).toFixed(); - } - function display_your_apr() { - const total_apr = new BigNumber(get_your_total_apr()); - if (total_apr.isEqualTo('0')) { - return '0%'; - } else if (total_apr.isLessThan(0.01)) { - return `<0.01%`; - } else { - return `${toPrecision(total_apr.toFixed(), 2)}%`; - } - } - function get_your_apr_copy(liquidity: UserLiquidityInfo) { - const { farmList, total_seed_amount, total_seed_power } = detailData; - // principal - const total_principal = get_liquidity_value(liquidity); - // seed total rewards - let total_rewards = '0'; - farmList.forEach((farm: FarmBoost) => { - const { token_meta_data } = farm; - const { daily_reward, reward_token } = farm.terms; - const quantity = toReadableNumber(token_meta_data.decimals, daily_reward); - const reward_token_price = Number( - tokenPriceList[reward_token]?.price || 0 - ); - const cur_token_rewards = new BigNumber(quantity) - .multipliedBy(reward_token_price) - .multipliedBy(365); - total_rewards = cur_token_rewards.plus(total_rewards).toFixed(); - }); - // lp percent - let percent: BigNumber = new BigNumber(0); - const mint_amount = mint_liquidity(liquidity, detailData.seed_id); - const part_farm_ratio = liquidity.part_farm_ratio; - if (+part_farm_ratio == 100) { - // full farming - if (+total_seed_power > 0) { - percent = new BigNumber(mint_amount).dividedBy(total_seed_power); - } - } else if (+part_farm_ratio > 0 && +part_farm_ratio < 100) { - // partial Farming - if (+total_seed_power > 0) { - const partial_amount = new BigNumber(mint_amount) - .multipliedBy(part_farm_ratio) - .dividedBy(100); - percent = partial_amount.dividedBy(total_seed_power); - } - } else { - // unFarming, unavailable - const temp_total = new BigNumber(total_seed_power || 0).plus(mint_amount); - if (temp_total.isGreaterThan(0)) { - percent = new BigNumber(mint_amount).dividedBy(temp_total); - } - } - // profit - let profit; - if (percent) { - profit = percent.multipliedBy(total_rewards); - } - // your apr - if (profit) { - if (+total_principal == 0) { - return 0; - } - const your_apr = profit.dividedBy(total_principal).multipliedBy(100); - if (your_apr.isEqualTo('0')) { - return '0%'; - } else if (your_apr.isLessThan(0.01)) { - return `<0.01%`; - } else { - return `${toPrecision(your_apr.toFixed(), 2)}%`; - } - } else { - return '-'; - } - } - function getYourLiquidityAprTip() { - const { farmList } = liquidity; - if (!farmList) return ''; - const tempList = farmList; - const lastList: any[] = []; - const pending_farms: FarmBoost[] = []; - const no_pending_farms: FarmBoost[] = []; - const totalApr = get_your_total_apr(); - const txt = intl.formatMessage({ id: 'reward_apr' }); - tempList.forEach((farm: FarmBoost) => { - if (farm.status == 'Created') { - pending_farms.push(farm); - } else { - no_pending_farms.push(farm); - } - }); - if (pending_farms.length > 0) { - pending_farms.forEach((farm: FarmBoost) => { - lastList.push({ - rewardToken: farm.token_meta_data, - apr: new BigNumber(farm.yourNFTApr || 0) - .multipliedBy(100) - .toFixed() - .toString(), - startTime: farm.terms.start_at, - pending: true, - }); - }); - } - if (no_pending_farms.length > 0) { - const mergedFarms = mergeCommonRewardsFarms( - JSON.parse(JSON.stringify(no_pending_farms)) - ); - mergedFarms.forEach((farm: FarmBoost) => { - lastList.push({ - rewardToken: farm.token_meta_data, - apr: new BigNumber(farm.yourNFTApr || 0) - .multipliedBy(100) - .toFixed() - .toString(), - }); - }); - } - // show last display string - let result: string = ''; - result = ` -
- ${txt} - ${ - toPrecision(totalApr.toString(), 2) + '%' - }
- `; - lastList.forEach((item: any) => { - const { rewardToken, apr, pending, startTime } = item; - const token = rewardToken; - let itemHtml = ''; - if (pending) { - const startDate = moment.unix(startTime).format('YYYY-MM-DD'); - const txt = intl.formatMessage({ id: 'start' }); - itemHtml = `
- -
- - - -
-
`; - } else { - itemHtml = `
- - -
`; - } - result += itemHtml; - }); - return result; - } - function mergeCommonRewardsFarms(farms: FarmBoost[]) { - const tempMap = {}; - farms.forEach((farm: FarmBoost) => { - const { reward_token, daily_reward } = farm.terms; - let preMergedfarms: FarmBoost = tempMap[reward_token]; - if (preMergedfarms) { - preMergedfarms.yourNFTApr = new BigNumber( - preMergedfarms.yourNFTApr || 0 - ) - .plus(farm.yourNFTApr) - .toFixed() - .toString(); - preMergedfarms.terms.daily_reward = new BigNumber( - preMergedfarms.terms.daily_reward - ) - .plus(daily_reward) - .toFixed(); - } else { - tempMap[reward_token] = farm; - } - }); - return Object.values(tempMap); - } - function stakeNFT(liquidity: UserLiquidityInfo) { - set_nft_stake_loading(true); - let finalAmount; - const { seed_id, min_deposit } = detailData; - const { mft_id, part_farm_ratio, unfarm_part_amount } = liquidity; - const v_liquidity = mint_liquidity(liquidity, seed_id); - let needUnstake = false; - let withdraw_amount = '0'; - if (!mft_id || new BigNumber(part_farm_ratio || 0).isEqualTo(0)) { - finalAmount = v_liquidity; - } else { - if (new BigNumber(unfarm_part_amount).isLessThan(min_deposit)) { - needUnstake = true; - withdraw_amount = new BigNumber(v_liquidity) - .minus(unfarm_part_amount) - .toFixed(); - } else { - finalAmount = unfarm_part_amount; - } - } - stake_boost_nft({ - liquidity, - seed_id, - stakeAmount: finalAmount, - withdraw_amount, - needUnstake, - }); - } - function unStakeNFT(liquidity: UserLiquidityInfo) { - set_nft_unStake_loading(true); - const amount = liquidity.v_liquidity; - const mft_balance_big = new BigNumber(mft_balance_in_dcl_account); - const amount_big = new BigNumber(amount); - let finalAmount = '0'; - if (mft_balance_big.isEqualTo('0')) { - finalAmount = amount; - } else if (mft_balance_big.isGreaterThanOrEqualTo(amount)) { - finalAmount = '0'; - } else { - finalAmount = amount_big.minus(mft_balance_in_dcl_account).toFixed(); - } - unStake_boost_nft({ - lpt_id: liquidity.lpt_id, - seed_id: detailData.seed_id, - withdraw_amount: finalAmount, - }); - } - function goYourLiquidityDetail(liquidity: UserLiquidityInfo) { - liquidity.lpt_id.split('#')[0]; - const pool_id = liquidity.lpt_id.split('#')[0]; - const lpt_index = liquidity.lpt_id.split('#')[1]; - openUrl(`/yoursLiquidityDetailV2/${get_pool_name(pool_id)}@${lpt_index}`); - } - function unavailableTip() { - const tip = unavailableText(); - const result: string = `
${tip}
`; - return result; - } - function unavailableText() { - let tip = ''; - const { seed_id, min_deposit } = detailData; - const [left_point, right_point] = get_valid_range(liquidity, seed_id); - const inrange = +right_point > +left_point; - if (!inrange) { - tip = intl.formatMessage({ id: 'your_price_range_tip' }); - } else if (liquidity.status_in_other_seed == 'staked') { - tip = intl.formatMessage({ id: 'position_has_staked_tip' }); - } else { - const v_liquidity = mint_liquidity(liquidity, seed_id); - const rate = new BigNumber(min_deposit).dividedBy(v_liquidity); - let rate_display = rate.toFixed(1); - if (rate.isGreaterThan(rate_display)) { - rate_display = new BigNumber(rate_display).plus(0.1).toFixed(); - } - // your liquidity - tip = - intl.formatMessage({ id: 'minimum_tip' }) + - ' ' + - `${rate_display}x` + - ' ' + - intl.formatMessage({ id: 'your_liquidity_3' }); - } - return tip; - } - function unavailableDiv() { - let tip; - const { seed_id, min_deposit } = detailData; - const [left_point, right_point] = get_valid_range(liquidity, seed_id); - const inrange = +right_point > +left_point; - if (!inrange) { - tip = intl.formatMessage({ id: 'your_price_range_tip' }); - } else if (liquidity.status_in_other_seed == 'staked') { - const { mft_id } = liquidity; - const link: string = get_target_seed_url_link({ - all_seeds, - mft_id, - }); - tip = ( - <> - {' '} -
{ - openUrl(link); - }} - > - - - - -
- - ); - } else { - const v_liquidity = mint_liquidity(liquidity, seed_id); - const rate = new BigNumber(min_deposit).dividedBy(v_liquidity); - let rate_display = rate.toFixed(1); - if (rate.isGreaterThan(rate_display)) { - rate_display = new BigNumber(rate_display).plus(0.1).toFixed(); - } - tip = - intl.formatMessage({ id: 'minimum_tip' }) + - ' ' + - `${rate_display}x` + - ' ' + - intl.formatMessage({ id: 'your_liquidity_3' }); - } - return tip; - } - function get_target_seed_url_link({ - all_seeds, - mft_id, - }: { - all_seeds: Seed[]; - mft_id: string; - }) { - const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); - const temps = all_seeds.filter((seed: Seed) => { - return seed.seed_id == seed_id; - }); - temps.sort((b: Seed, a: Seed) => { - if (b.farmList[0].status == 'Ended') return 1; - if (a.farmList[0].status == 'Ended') return -1; - return 0; - }); - const target = temps[0]; - const [fixRange, pool_id, left_point, right_point] = mft_id.split('&'); - const status = target?.farmList[0].status == 'Ended' ? 'e' : 'r'; - const params = `${get_pool_name(pool_id)}[${left_point}-${right_point}]`; - const link = `/v2farms/${params}-${status}`; - return link; - } - function apr_title() { - if ( - liquidity_status_string == 'farming' || - liquidity_status_string == 'partialfarming' - ) { - return ; - } else { - return ; - } - } - const showStakeButton = - !isEnded && liquidity_operation_display.indexOf('stake') > -1; - const showUnStakeButton = liquidity_operation_display.indexOf('unstake') > -1; - return ( - <> - {/* for PC */} -
{ - setHover(false); - }} - > -
- - - NFT ID #{liquidity.lpt_id.split('#')[1]} - -
-
-
setHover(true)} - className="flex items-stretch justify-between pt-7 pb-3.5 px-6" - > -
- - - - - {get_liquidity_value_display(liquidity)} - -
-
- - - - - {get_your_range(liquidity)} - -
-
-
- {apr_title()} - { - e.stopPropagation(); - setDclCalcVisible(true); - }} - className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" - /> -
-
- - {display_your_apr()} - - -
-
-
- - - -
- {liquidity_status_display} -
-
-
-
-
{ - goYourLiquidityDetail(liquidity); - }} - className="flex items-center text-sm text-primaryText hover:text-white cursor-pointer" - > - - - - -
- {liquidity_status_string == 'unavailable' ? ( -
- {unavailableDiv()} -
- ) : null} -
-
- { - stakeNFT(liquidity); - }} - color="#fff" - disabled={ - nft_stake_loading || - liquidity_partialfarming_stakebuttonstatus_display == - 'disable' - ? true - : false - } - btnClassName={ - nft_stake_loading || - liquidity_partialfarming_stakebuttonstatus_display == - 'disable' - ? 'cursor-not-allowed' - : '' - } - minWidth="6rem" - className={`h-8 px-4 text-center text-sm text-white focus:outline-none ${ - nft_stake_loading || - liquidity_partialfarming_stakebuttonstatus_display == - 'disable' - ? 'opacity-40' - : '' - } ${showStakeButton ? '' : 'hidden'}`} - > - ( - - )} - /> - - -
- { - unStakeNFT(liquidity); - }} - color="#fff" - minWidth="6rem" - disabled={nft_unStake_loading ? true : false} - className={`flex items-center justify-center h-8 px-4 ml-2.5 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ - nft_unStake_loading ? 'opacity-40' : '' - } ${showUnStakeButton ? '' : 'hidden'}`} - > - ( - - )} - /> - -
-
-
-
- {/* for Mobile */} -
-
- - - NFT ID #{liquidity.lpt_id.split('#')[1]} - -
-
-
- - - - - {get_liquidity_value_display(liquidity)} - -
-
- - - - - {get_your_range(liquidity)} - -
-
-
- {apr_title()} - { - e.stopPropagation(); - setDclCalcVisible(true); - }} - className="text-farmText ml-1.5 cursor-pointer hover:text-greenColor" - /> -
- - {display_your_apr()} - -
-
- - - -
- {liquidity_status_display} -
-
-
-
-
-
- { - stakeNFT(liquidity); - }} - color="#fff" - disabled={ - nft_stake_loading || - liquidity_partialfarming_stakebuttonstatus_display == - 'disable' || - !showStakeButton - } - className={`h-10 px-4 text-center text-sm text-white focus:outline-none ${ - nft_stake_loading || - liquidity_partialfarming_stakebuttonstatus_display == - 'disable' || - !showStakeButton - ? 'opacity-40' - : '' - }`} - > - ( - - )} - /> - - -
- { - unStakeNFT(liquidity); - }} - color="#fff" - disabled={nft_unStake_loading ? true : false} - className={`flex items-center justify-center w-1 flex-grow h-10 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ml-2.5 ${ - nft_unStake_loading ? 'opacity-40' : '' - } ${showUnStakeButton ? '' : 'hidden'}`} - > - ( - - )} - /> - -
- {liquidity_status_string == 'unavailable' ? ( -
- {unavailableDiv()} -
- ) : null} -
{ - goYourLiquidityDetail(liquidity); - }} - className="flex items-center text-sm text-primaryText hover:text-white cursor-pointer mt-4" - > - - - - -
-
-
- {dclCalcVisible ? ( - { - e.stopPropagation(); - setDclCalcVisible(false); - }} - seed={detailData} - liquidity={liquidity} - tokenPriceList={tokenPriceList} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - /> - ) : null} - ); -} -function AddLoginEntryBar() { - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - if (isSignedIn) return null; - return ( -
-
-
- -
-
- ); -} -type StatusType = 'farming' | 'unavailable' | 'unfarming' | 'partialfarming'; +} \ No newline at end of file diff --git a/src/components/farm/utils.ts b/src/components/farm/utils.ts new file mode 100644 index 000000000..c621d0808 --- /dev/null +++ b/src/components/farm/utils.ts @@ -0,0 +1,60 @@ +import Big from 'big.js'; +import { + formatWithCommas, + toInternationalCurrencySystem, +} from '../../utils/numbers'; +export const formatWithCommas_usd = (v: string | number) => { + if (isInvalid(v)) return '$-'; + const big = Big(v); + if (big.eq(0)) { + return '$0'; + } else if (big.lt(0.01)) { + return '<$0.01'; + } else if (big.lt(10000)) { + return '$' + formatWithCommas(big.toFixed(2, 1)); + } else { + return '$' + formatWithCommas(big.toFixed(0, 1)); + } +}; + +export const formatPercentage = (v: string | number) => { + if (isInvalid(v)) return '-%'; + const big = Big(v); + if (big.eq(0)) { + return '0%'; + } else if (big.lt(1)) { + return '<1%'; + } else { + return big.toFixed(0, 1) + '%'; + } +}; +export const formatNumber = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return big.toFixed(2, 1); + } +}; +export const formatPrice = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.0001'; + } else { + return big.toFixed(4, 1); + } +}; +export const formatToInternationalCurrencySystem$ = (v: string | number) => { + if (isInvalid(v)) return '$-'; + return '$' + toInternationalCurrencySystem(Big(v || 0).toFixed(), 2); +}; +export const isInvalid = function (v: any) { + if (v === '' || v === undefined || v === null) return true; + return false; +}; diff --git a/src/pages/farms/FarmsBoostPageCopy.tsx b/src/pages/farms/FarmsBoostPageCopy.tsx new file mode 100644 index 000000000..1abda52d3 --- /dev/null +++ b/src/pages/farms/FarmsBoostPageCopy.tsx @@ -0,0 +1,100 @@ +import React, { useEffect, useRef, useState, useContext } from 'react'; +import FarmsHome from '~components/farm/FarmsHome'; +import FarmsDetail from '~components/farm/FarmsDetail'; +import FarmsDclDetail from '~components/farm/FarmsDclDetailCopy'; +import Loading, { BeatLoading } from '~components/layout/Loading'; +import { Seed, BoostConfig, UserSeedInfo } from '~services/farm'; +export default function FarmsBoosterPage(props: any) { + const [detailData, setDetailData] = useState(null); + const [tokenPriceList, setTokenPriceList] = useState(null); + const [loveSeed, serLoveSeed] = useState(null); + const [boostConfig, setBoostConfig] = useState(null); + const [user_data, set_user_data] = useState({}); + const [user_data_loading, set_user_data_loading] = useState(true); + const [dayVolumeMap, setDayVolumeMap] = useState({}); + const [all_seeds, set_all_seeds] = useState([]); + const paramId = decodeURIComponent(props.match.params.id || ''); + const is_dcl = paramId.indexOf('<>') > -1 || paramId.indexOf('|') > -1; + const getDetailData_user_data = (data: { + user_seeds_map: Record; + user_unclaimed_token_meta_map: Record; + user_unclaimed_map: Record; + }) => { + const { + user_seeds_map, + user_unclaimed_map, + user_unclaimed_token_meta_map, + } = data; + set_user_data({ + user_seeds_map, + user_unclaimed_map, + user_unclaimed_token_meta_map, + }); + set_user_data_loading(false); + }; + const getDayVolumeMap = (map: any) => { + setDayVolumeMap(map || {}); + }; + const getDetailData_boost_config = (boostConfig: BoostConfig) => { + setBoostConfig(boostConfig); + }; + const getDetailData = (data: { + detailData: Seed; + tokenPriceList: any; + loveSeed: Seed; + all_seeds: Seed[]; + }) => { + const { detailData, tokenPriceList, loveSeed, all_seeds } = data; + setDetailData(detailData); + setTokenPriceList(tokenPriceList); + serLoveSeed(loveSeed); + set_all_seeds(all_seeds); + }; + const emptyDetailData = () => { + setDetailData(null); + }; + const baseCondition = + paramId && + detailData && + tokenPriceList && + Object.keys(tokenPriceList).length > 0; + const showDetailPage = baseCondition && !is_dcl; + const showDclDetailPage = baseCondition && is_dcl; + const showLoading = paramId && !showDetailPage && !showDclDetailPage; + return ( + <> + + {showLoading ? : null} + {showDetailPage ? ( + + ) : null} + {showDclDetailPage ? ( + + ) : null} + + ); +} From 9ac5452c4e9756665122fe3ae466be62a6eb8857 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 13 Jun 2023 23:14:38 +0800 Subject: [PATCH 023/204] add dcl farm --- src/components/d3Chart/DclChart.tsx | 11 +- src/components/farm/FarmsDclDetail.tsx | 607 ++++++++++++++----- src/components/farm/FarmsHome.tsx | 14 +- src/components/pool/RemovePoolV3.tsx | 1 - src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6 +- src/services/farm.ts | 151 +++++ src/services/swapV3.ts | 4 +- 7 files changed, 648 insertions(+), 146 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 9876c5d9f..1e136e987 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -488,7 +488,9 @@ export default function DclChart({ }) .attr('y', function (d) { return ( - wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)) - get_final_bar_height(scaleBar(+d.order_liquidity)) + wholeBarHeight - + get_final_bar_height(scaleBar(+d.liquidity)) - + get_final_bar_height(scaleBar(+d.order_liquidity)) ); }) .attr('rx', 2) @@ -497,9 +499,10 @@ export default function DclChart({ }) .attr('opacity', '0.7'); } - function get_final_bar_height(h:number) { - if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) return min_bar_height; - return h + function get_final_bar_height(h: number) { + if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) + return min_bar_height; + return h; } function draw_background_bars({ data, diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 8396d714a..f93189cc7 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -29,8 +29,10 @@ import { Seed, claimRewardBySeed_boost, BoostConfig, - stake_boost_nft, - unStake_boost_nft, + batch_stake_boost_nft, + batch_unStake_boost_nft, + IStakeInfo, + UserSeedInfo, } from '~services/farm'; import { WalletContext } from '../../utils/wallets-integration'; import { @@ -101,6 +103,7 @@ export default function FarmsDclDetail(props: { dayVolumeMap, all_seeds, } = props; + console.log('8888888888-detailData', detailData); const [listLiquidities, setListLiquidities] = useState( [] ); @@ -165,21 +168,46 @@ export default function FarmsDclDetail(props: { const unclaimedRewardsData = useMemo(() => { return getTotalUnclaimedRewards(); }, [user_unclaimed_map[detailData.seed_id]]); - console.log('99999999999', listLiquidities_inFarimg); - console.log('listLiquiditiesLoading', listLiquiditiesLoading); const [yp_percent, yp_canFarm_value, yp_unFarm_value] = useMemo(() => { if (!listLiquiditiesLoading) { - const { total_value, can_farm_parts_value, un_farm_parts_value } = caculate_values(); + const { total_value, can_farm_parts_value, un_farm_parts_value } = + caculate_values(); if (total_value.gt(0)) { const percent = can_farm_parts_value.div(total_value).mul(100); - return [formatPercentage(percent.toFixed()), formatWithCommas_usd(can_farm_parts_value.toFixed()), formatWithCommas_usd(un_farm_parts_value.toFixed())] + return [ + formatPercentage(percent.toFixed()), + formatWithCommas_usd(can_farm_parts_value.toFixed()), + formatWithCommas_usd(un_farm_parts_value.toFixed()), + ]; } else { - return ['0%', '$0', '$0'] + return ['0%', '$0', '$0']; } } else { - return ['0%', '$0', '$0'] + return ['0%', '$0', '$0']; } - }, [listLiquiditiesLoading, listLiquidities_inFarimg.length, listLiquidities_unFarimg.length]) + }, [ + listLiquiditiesLoading, + listLiquidities_inFarimg.length, + listLiquidities_unFarimg.length, + ]); + + const canStake = useMemo(() => { + if (!listLiquiditiesLoading) { + const { canStake } = get_stake_info(); + return canStake; + } + return false; + }, [ + listLiquiditiesLoading, + listLiquidities_inFarimg.length, + listLiquidities_unFarimg.length, + ]); + const canUnStake = useMemo(() => { + if (!listLiquiditiesLoading) { + return listLiquidities_inFarimg.length; + } + }, [listLiquiditiesLoading, listLiquidities_inFarimg.length]); + function getTotalUnclaimedRewards() { let totalPrice = 0; let resultTip = ''; @@ -396,7 +424,6 @@ export default function FarmsDclDetail(props: { set_listLiquidities_unFarimg(temp_free_final); set_listLiquidities_unavailable(temp_unavailable_final); setListLiquidities(matched_liquidities); - console.log('temp_farming_final, temp_unavailable_final, temp_free_final', temp_farming_final, temp_unavailable_final, temp_free_final); } if (!user_data_loading) { setListLiquiditiesLoading(false); @@ -926,13 +953,17 @@ export default function FarmsDclDetail(props: { const [tokenx, tokeny, fee] = detailData?.pool?.pool_id?.split('|') || ''; return +fee / 10000 + '%'; } - function get_liquidity_value(liquidity: UserLiquidityInfo, leftPoint?:number, rightPoint?:number) { + function get_liquidity_value( + liquidity: UserLiquidityInfo, + leftPoint?: number, + rightPoint?: number + ) { const { amount } = liquidity; const poolDetail = detailData.pool; const { token_x, token_y } = poolDetail; const v = get_total_value_by_liquidity_amount_dcl({ - left_point:leftPoint || liquidity.left_point, - right_point:rightPoint || liquidity.right_point, + left_point: leftPoint || liquidity.left_point, + right_point: rightPoint || liquidity.right_point, poolDetail, amount, price_x_y: { @@ -949,40 +980,118 @@ export default function FarmsDclDetail(props: { const radio = getBoostMutil(); /** new start */ function caculate_values() { - const can_farm_liquidities = listLiquidities_inFarimg.concat(listLiquidities_unFarimg); + const can_farm_liquidities = listLiquidities_inFarimg.concat( + listLiquidities_unFarimg + ); // 可以farm的所有流动性的总价值 let total_value = Big(0); - can_farm_liquidities.forEach((l:UserLiquidityInfo) => { + can_farm_liquidities.forEach((l: UserLiquidityInfo) => { const l_v = get_liquidity_value(l); total_value = total_value.plus(l_v || 0); - }) + }); // 能farm部分的的流动性的总价值 let can_farm_parts_value = Big(0); - can_farm_liquidities.forEach((l:UserLiquidityInfo) => { - const [left_point, right_point] = get_valid_range(l,detailData.seed_id); - const l_v = get_liquidity_value(l, left_point, right_point); + can_farm_liquidities.forEach((l: UserLiquidityInfo) => { + const l_v = get_range_part_value(l); can_farm_parts_value = can_farm_parts_value.plus(l_v || 0); - }) + }); // unFarming 部分流动性的总价值(包含部分在farm中的那个NFT剩余的部分) let un_farm_parts_value = Big(0); - listLiquidities_unFarimg.forEach((l:UserLiquidityInfo) => { - const l_v = get_liquidity_value(l); - un_farm_parts_value = un_farm_parts_value.plus(l_v || 0); - }) - const [ part_farm_liquidity ] = listLiquidities_inFarimg.filter((l:UserLiquidityInfo) => { - return Big(l.part_farm_ratio || 0).gt(0); - }) + listLiquidities_unFarimg.forEach((l: UserLiquidityInfo) => { + const l_v = get_range_part_value(l); + un_farm_parts_value = un_farm_parts_value.plus(l_v || 0); + }); + const [part_farm_liquidity] = listLiquidities_inFarimg.filter( + (l: UserLiquidityInfo) => { + return Big(l.part_farm_ratio || 0).gt(0); + } + ); if (part_farm_liquidity) { - const [left_point, right_point] = get_valid_range(part_farm_liquidity,detailData.seed_id); - const v = get_liquidity_value(part_farm_liquidity, left_point, right_point); - const un_farm_part_value = Big(100).minus(part_farm_liquidity.part_farm_ratio).div(100).mul(v) + const v = get_range_part_value(part_farm_liquidity); + const un_farm_part_value = Big(100) + .minus(part_farm_liquidity.part_farm_ratio) + .div(100) + .mul(v); un_farm_parts_value = un_farm_parts_value.plus(un_farm_part_value); } return { total_value, can_farm_parts_value, un_farm_parts_value, + }; + } + function get_range_part_value(liquidity: UserLiquidityInfo) { + const [left_point, right_point] = get_valid_range( + liquidity, + detailData.seed_id + ); + const v = get_liquidity_value(liquidity, left_point, right_point); + return v; + } + function batchStakeNFT() { + set_nft_stake_loading(true); + const { liquidities, total_v_liquidity, withdraw_amount } = + get_stake_info(); + batch_stake_boost_nft({ + liquidities, + total_v_liquidity, + withdraw_amount, + seed_id: detailData.seed_id, + }); + } + function batchUnStakeNFT() { + set_nft_unStake_loading(true); + const unStake_info: IStakeInfo = get_unStake_info(); + batch_unStake_boost_nft(unStake_info); + } + function get_stake_info(): IStakeInfo { + const { seed_id, min_deposit } = detailData; + let total_v_liquidity = Big(0); + let withdraw_amount = Big(0); + const liquidities: UserLiquidityInfo[] = listLiquidities_unFarimg; + listLiquidities_unFarimg.forEach((l: UserLiquidityInfo) => { + const v_liquidity = mint_liquidity(l, seed_id); + total_v_liquidity = total_v_liquidity.plus(v_liquidity); + }); + + const [part_farm_liquidity] = listLiquidities_inFarimg.filter( + (l: UserLiquidityInfo) => { + return Big(l.unfarm_part_amount || 0).gt(0); + } + ); + if (part_farm_liquidity) { + total_v_liquidity = total_v_liquidity.plus( + part_farm_liquidity.unfarm_part_amount + ); + liquidities.push(part_farm_liquidity); } + if (total_v_liquidity.lt(min_deposit)) { + if (part_farm_liquidity) { + const v_liquidity = mint_liquidity(part_farm_liquidity, seed_id); + withdraw_amount = new Big(v_liquidity).minus( + part_farm_liquidity.unfarm_part_amount + ); + total_v_liquidity = total_v_liquidity.plus(withdraw_amount); + } + } + return { + liquidities, + total_v_liquidity: total_v_liquidity.toFixed(), + withdraw_amount: withdraw_amount.toFixed(), + canStake: total_v_liquidity.lt(min_deposit) ? false : true, + }; + } + function get_unStake_info() { + const { free_amount = '0', locked_amount = '0' } = + user_seeds_map[detailData.seed_id] || {}; + const user_seed_amount = new BigNumber(free_amount) + .plus(locked_amount) + .toFixed(); + return { + liquidities: listLiquidities_inFarimg, + withdraw_amount: user_seed_amount, + seed_id: detailData.seed_id, + }; } /** new end */ return ( @@ -1263,116 +1372,82 @@ export default function FarmsDclDetail(props: {
{/* login area */} - {/* Your Farming Position */} -
-
-
- Your Farming Position - {yp_canFarm_value} -
-
{yp_percent} of your total liquidity
-
-
-
{yp_unFarm_value} available to stake
-
- - ( - - )} - /> - - - ( - - )} - /> - -
-
-
- {/* unClaimed Rewards for PC */} -
-
- -
- - +
+ {/* Your Farming Position */} +
+
+
+ + Your Farming Position + + {yp_canFarm_value} +
+
+ {yp_percent} of your total liquidity +
-
-
-
- - {unclaimedRewardsData.worth} - - +
+
+ {yp_unFarm_value} available to + stake +
+
+ {canStake ? ( + + ( + + )} + /> + + ) : null} + {canUnStake ? ( + + ( + + )} + /> + + ) : null} +
- { - if (!unclaimedRewardsData.showClaimButton) return; - e.stopPropagation(); - claimReward(); - }} - > - } - /> -
+ {/* unClaimed Rewards for PC */} + {user_seeds_map[detailData.seed_id] ? ( + + ) : null}
+ {/* unClaimed Rewards for Mobile */}
); } +function UserTotalUnClaimBlock(props: { + detailData: Seed; + tokenPriceList: any; + user_seeds_map: Record; + user_unclaimed_token_meta_map: Record; + user_unclaimed_map: Record; + radio?: string | number; +}) { + const { + detailData, + tokenPriceList, + user_seeds_map, + user_unclaimed_token_meta_map, + user_unclaimed_map, + radio, + } = props; + const [claimLoading, setClaimLoading] = useState(false); + const [showDetail, setShowDetail] = useState(false); + const { seed_id } = detailData; + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const intl = useIntl(); + function claimReward() { + if (claimLoading) return; + setClaimLoading(true); + claimRewardBySeed_boost(detailData.seed_id) + // .then(() => { + // window.location.reload(); + // }) + .catch((error) => { + setClaimLoading(false); + // setError(error); + }); + } + function getTotalUnclaimedRewards() { + let totalPrice = 0; + let resultTip = ''; + const tempFarms = {}; + + detailData.farmList.forEach((farm: FarmBoost) => { + tempFarms[farm.terms.reward_token] = true; + }); + const isEnded = detailData.farmList[0].status == 'Ended'; + const unclaimed = user_unclaimed_map[seed_id] || {}; + const unClaimedTokenIds = Object.keys(unclaimed); + const tokenList: any[] = []; + unClaimedTokenIds?.forEach((tokenId: string) => { + const token: TokenMetadata = user_unclaimed_token_meta_map[tokenId]; + // total price + const { id, decimals, icon } = token; + const amount = toReadableNumber(decimals, unclaimed[id] || '0'); + const tokenPrice = tokenPriceList[id]?.price; + if (tokenPrice && tokenPrice != 'N/A') { + totalPrice += +amount * tokenPrice; + } + // rewards number + let displayNum = ''; + if (new BigNumber('0').isEqualTo(amount)) { + displayNum = '-'; + } else if (new BigNumber('0.001').isGreaterThan(amount)) { + displayNum = '<0.001'; + } else { + displayNum = new BigNumber(amount).toFixed(3, 1); + } + // before boost number + let beforeNum = ''; + if (radio) { + const v = new BigNumber(amount).dividedBy(radio); + if (new BigNumber('0').isEqualTo(v)) { + beforeNum = '-'; + } else if (new BigNumber('0.001').isGreaterThan(v)) { + beforeNum = '<0.001'; + } else { + beforeNum = new BigNumber(v).toFixed(3, 1); + } + } + const tempTokenData = { + token, + amount: displayNum, + preAmount: beforeNum, + }; + tokenList.push(tempTokenData); + const txt = intl.formatMessage({ id: 'ended_search' }); + const itemHtml = `
+ +
+ ${formatWithCommas(displayNum)} + ${ + !isEnded && !tempFarms[id] + ? `${txt}` + : '' + } +
+
`; + resultTip += itemHtml; + }); + if (totalPrice == 0) { + return { + worth: , + showClaimButton: false, + tip: resultTip, + list: tokenList, + }; + } else if (new BigNumber('0.01').isGreaterThan(totalPrice)) { + return { + worth: '<$0.01', + showClaimButton: true, + tip: resultTip, + list: tokenList, + }; + } else { + return { + worth: `$${toInternationalCurrencySystem(totalPrice.toString(), 2)}`, + showClaimButton: true, + tip: resultTip, + list: tokenList, + }; + } + } + const unclaimedRewardsData = useMemo(() => { + return getTotalUnclaimedRewards(); + }, [user_unclaimed_map[seed_id]]); + function switchDetailButton() { + setShowDetail(!showDetail); + } + function valueOfRewardsTip() { + const tip = intl.formatMessage({ id: 'farmRewardsCopy' }); + let result: string = `
${tip}
`; + return result; + } + return ( +
+
+
+ +
+ + +
+
+ +
+ + {unclaimedRewardsData.worth} + + +
+
+ {unclaimedRewardsData.showClaimButton ? ( +
+
+ + +
+ { + e.stopPropagation(); + claimReward(); + }} + > + } + /> + +
+ ) : null} + +
+ {unclaimedRewardsData.list.map( + ( + { + token, + amount, + preAmount, + }: { token: TokenMetadata; amount: string; preAmount: string }, + index: number + ) => { + return ( +
+
+ + + {toRealSymbol(token.symbol)} + +
+
+ {preAmount ? ( + <> + + {preAmount} + + + + + {amount} + + ) : ( + {amount} + )} +
+
+ ); + } + )} +
+
+ ); +} function AddLoginEntryBar() { const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; @@ -1538,4 +1871,4 @@ function AddLoginEntryBar() {
); -} \ No newline at end of file +} diff --git a/src/components/farm/FarmsHome.tsx b/src/components/farm/FarmsHome.tsx index 6e832e6b9..9640e0351 100644 --- a/src/components/farm/FarmsHome.tsx +++ b/src/components/farm/FarmsHome.tsx @@ -592,6 +592,7 @@ export default function FarmsHome(props: any) { is_dcl_pool = true; } const targetFarms = farm_display_List.find((seed: Seed) => { + debugger; const { seed_id, farmList } = seed; const status = farmList[0].status; const id = getPoolIdBySeedId(seed_id); @@ -655,7 +656,7 @@ export default function FarmsHome(props: any) { const pool_id = get_pool_id(layer2[0]); const point_str = layer2[1].substring(0, layer2[1].length - 3); const status = layer2[1].substring(layer2[1].length - 1); - const p_arr = point_str.split('-'); + const p_arr = split_point_string(point_str); const [lp, rp] = p_arr; return `${pool_id}&${lp}&${rp}-${status}`; } else { @@ -669,6 +670,17 @@ export default function FarmsHome(props: any) { return ''; } + function split_point_string(str: string) { + const arr = Array.from(str); + let index; + for (let i = 0; i < arr.length; i++) { + if (str[i] == '-' && i !== 0) { + index = i; + break; + } + } + return [str.slice(0, index), str.slice(index + 1)]; + } async function get_user_unWithDraw_rewards() { if (isSignedIn) { const userRewardList = await get_unWithDraw_rewards(); diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index d864745f8..a0b40c661 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -623,7 +623,6 @@ export const RemovePoolV3 = (props: any) => { sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); - // replace to batch remove lp remove_liquidity({ token_x: tokenX, diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index bd3ca7c98..c171cbc2e 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -722,7 +722,11 @@ export default function AddYourLiquidityPageV3() { {/* 添加页图 */} {/* 用户流动性图表*/} - + {/* 删除流动性图表 从右侧部分删除 */} {/* */} {/* 删除流动性图表 从左侧部分删除 */} diff --git a/src/services/farm.ts b/src/services/farm.ts index 009b30a19..8fa15b8f8 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -1344,6 +1344,150 @@ export const unStake_boost_nft = async ({ return executeFarmMultipleTransactions(transactions); }; +export const batch_unStake_boost_nft = async ({ + seed_id, + withdraw_amount, + liquidities, +}: IStakeInfo) => { + const transactions: Transaction[] = []; + if (new BigNumber(withdraw_amount).isGreaterThan('0')) { + transactions.push({ + receiverId: REF_FARM_BOOST_CONTRACT_ID, + functionCalls: [ + { + methodName: 'unlock_and_withdraw_seed', + args: { + seed_id: seed_id, + unlock_amount: '0', + withdraw_amount, + }, + amount: ONE_YOCTO_NEAR, + gas: '200000000000000', + }, + ], + }); + } + liquidities.forEach((l: UserLiquidityInfo) => { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'burn_v_liquidity', + args: { + lpt_id: l.lpt_id, + }, + gas: '200000000000000', + }, + ], + }); + }); + const neededStorage = await checkTokenNeedsStorageDeposit_boost(); + if (neededStorage) { + transactions.unshift({ + receiverId: REF_FARM_BOOST_CONTRACT_ID, + functionCalls: [storageDepositAction({ amount: neededStorage })], + }); + } + + return executeFarmMultipleTransactions(transactions); +}; +export const batch_stake_boost_nft = async ({ + liquidities, + total_v_liquidity, + withdraw_amount, + seed_id, +}: IStakeInfo) => { + const [contractId, temp_pool_id] = seed_id.split('@'); + const [fixRange, dcl_pool_id, left_point, right_point] = + temp_pool_id.split('&'); + const transactions: Transaction[] = []; + liquidities.forEach((l: UserLiquidityInfo) => { + const { lpt_id, mft_id } = l; + const functionCalls = []; + if (!mft_id) { + functionCalls.push({ + methodName: 'mint_v_liquidity', + args: { + lpt_id, + dcl_farming_type: JSON.parse(fixRange), + }, + gas: '60000000000000', + }); + } else if (liquidity_is_in_other_seed(seed_id, mft_id)) { + functionCalls.push( + { + methodName: 'burn_v_liquidity', + args: { + lpt_id, + }, + gas: '60000000000000', + }, + { + methodName: 'mint_v_liquidity', + args: { + lpt_id, + dcl_farming_type: JSON.parse(fixRange), + }, + gas: '60000000000000', + } + ); + } else if (Big(withdraw_amount).gt(0)) { + transactions.push({ + receiverId: REF_FARM_BOOST_CONTRACT_ID, + functionCalls: [ + { + methodName: 'unlock_and_withdraw_seed', + args: { + seed_id: seed_id, + unlock_amount: '0', + withdraw_amount, + }, + amount: ONE_YOCTO_NEAR, + gas: '200000000000000', + }, + ], + }); + } + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls, + }); + }); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'mft_transfer_call', + args: { + receiver_id: REF_FARM_BOOST_CONTRACT_ID, + token_id: `:${temp_pool_id}`, + amount: total_v_liquidity, + msg: JSON.stringify('Free'), + }, + amount: ONE_YOCTO_NEAR, + gas: '180000000000000', + }, + ], + }); + const neededStorage = await checkTokenNeedsStorageDeposit_boost(); + if (neededStorage) { + transactions.unshift({ + receiverId: REF_FARM_BOOST_CONTRACT_ID, + functionCalls: [storageDepositAction({ amount: neededStorage })], + }); + } + return executeFarmMultipleTransactions(transactions); +}; +function liquidity_is_in_other_seed(seed_id: string, mft_id: string) { + const [temp_pool_id] = seed_id.split('@'); + const [fixRange_s, pool_id_s, left_point_s, right_point_s] = + temp_pool_id.split('&'); + const [fixRange_l, pool_id_l, left_point_l, right_point_l] = + mft_id.split('&'); + const is_in_other_seed = + left_point_s != left_point_l || right_point_s != right_point_l; + return is_in_other_seed; +} export interface MigrateSeed { seed_id: string; amount: string; @@ -1551,3 +1695,10 @@ export function getFarmClassification(): any { }; } } +export interface IStakeInfo { + liquidities: UserLiquidityInfo[]; + total_v_liquidity?: string; + withdraw_amount?: string; + canStake?: boolean; + seed_id?: string; +} diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 88ee2b11b..fbf88bf0a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -780,8 +780,8 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); } } - if(deposit_fee.eq(0)){ - return '' + if (deposit_fee.eq(0)) { + return ''; } return utils.format.formatNearAmount(deposit_fee.toFixed(0)); From 432f5ffa8e7eb93746ff8d629f962bf899510b50 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 13 Jun 2023 23:41:04 +0800 Subject: [PATCH 024/204] update dcl farm --- src/services/farm.ts | 13 ++++++++----- 1 file changed, 8 insertions(+), 5 deletions(-) diff --git a/src/services/farm.ts b/src/services/farm.ts index 8fa15b8f8..02726b26c 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -1205,6 +1205,7 @@ export const stake_boost_nft = async ({ const transactions: Transaction[] = []; const functionCalls = []; const { lpt_id, mft_id } = liquidity; + debugger; if (needUnstake) { const v_liquidity = mint_liquidity(liquidity, seed_id); if (+withdraw_amount > 0) { @@ -1448,10 +1449,12 @@ export const batch_stake_boost_nft = async ({ ], }); } - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls, - }); + if (functionCalls.length > 0) { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls, + }); + } }); transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, @@ -1479,7 +1482,7 @@ export const batch_stake_boost_nft = async ({ return executeFarmMultipleTransactions(transactions); }; function liquidity_is_in_other_seed(seed_id: string, mft_id: string) { - const [temp_pool_id] = seed_id.split('@'); + const [contractId, temp_pool_id] = seed_id.split('@'); const [fixRange_s, pool_id_s, left_point_s, right_point_s] = temp_pool_id.split('&'); const [fixRange_l, pool_id_l, left_point_l, right_point_l] = From aa6d3c6866584de2ccd7e770248b2cd510781e68 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 17 Jun 2023 21:04:32 +0800 Subject: [PATCH 025/204] add order chart --- src/components/swap/SwapLimitOrderChart.tsx | 1088 +++++++++++++++++++ src/components/swap/SwapProTab.tsx | 31 + src/components/swap/SwapRateChart.tsx | 198 ++-- src/global.css | 5 + src/pages/SwapPage.tsx | 22 +- src/state/swapV3.ts | 37 + tailwind.config.js | 1 + 7 files changed, 1279 insertions(+), 103 deletions(-) create mode 100644 src/components/swap/SwapLimitOrderChart.tsx create mode 100644 src/components/swap/SwapProTab.tsx diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx new file mode 100644 index 000000000..0d2708551 --- /dev/null +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -0,0 +1,1088 @@ +import React, { + useContext, + useEffect, + useMemo, + useState, + createContext, +} from 'react'; +import { get_pointorder_range } from '../../services/swapV3'; +import { get_pool, PoolInfo } from '../../services/swapV3'; +import { ftGetTokenMetadata } from '../../services/ft-contract'; +import { getPriceByPoint } from '../../services/commonV3'; +import SwapProTab from './SwapProTab'; +import { + toReadableNumber, + toInternationalCurrencySystem, +} from '~utils/numbers'; +import { toRealSymbol } from '../../utils/token'; +import { SwapProContext } from '../../pages/SwapPage'; +import Big from 'big.js'; +import * as d3 from 'd3'; +const LimitOrderChartData = createContext(null); +export default function SwapLimitOrderChart() { + // CONST start + const limitOrderContainerHeight = '150'; + // CONST end + const [pool, setPool] = useState(); + const [orders, setOrders] = useState(); + const [switch_token, set_switch_token] = useState('X'); + const [buy_token_x_list, set_buy_token_x_list] = + useState(); + const [sell_token_x_list, set_sell_token_x_list] = + useState(); + const [buy_token_y_list, set_buy_token_y_list] = + useState(); + const [sell_token_y_list, set_sell_token_y_list] = + useState(); + const [buy_list, set_buy_list] = useState(); + const [sell_list, set_sell_list] = useState(); + const [market_loading, set_market_loading] = useState(false); + const { dcl_pool_id } = useContext(SwapProContext); + // todo + const pool_id = dcl_pool_id; + const left_point = -800000; + const right_point = 600000; + useEffect(() => { + if (pool_id) { + if (market_loading) { + refresh(); + } else { + get_points_of_orders(); + get_pool_detail(); + set_switch_token('X'); + } + } + }, [pool_id, market_loading]); + useEffect(() => { + if (pool && orders) { + process_orders(); + } + }, [pool, orders]); + useEffect(() => { + if (switch_token == 'X' && buy_token_x_list && sell_token_x_list) { + set_buy_list(buy_token_x_list); + set_sell_list(sell_token_x_list); + } else if (switch_token == 'Y' && buy_token_y_list && sell_token_y_list) { + set_buy_list(buy_token_y_list); + set_sell_list(sell_token_y_list); + } + }, [ + switch_token, + buy_token_x_list, + sell_token_x_list, + buy_token_y_list, + sell_token_y_list, + ]); + const [cur_pairs, cur_token_symbol, cur_pair_icons] = useMemo(() => { + if (pool) { + const classStr = 'w-6 h-6 rounded-full border border-gradientFromHover'; + const { token_x_metadata, token_y_metadata } = pool; + const x_symbol = toRealSymbol(token_x_metadata.symbol); + const y_symbol = toRealSymbol(token_y_metadata.symbol); + if (switch_token == 'X') { + const x_icons = ( + <> + + + + ); + return [`${x_symbol}/${y_symbol}`, `${x_symbol}`, x_icons]; + } else if (switch_token == 'Y') { + const y_icons = ( + <> + + + + ); + return [`${y_symbol}/${x_symbol}`, `${y_symbol}`, y_icons]; + } + } + return []; + }, [switch_token, pool]); + async function refresh() { + await get_points_of_orders(); + await get_pool_detail(); + set_market_loading(false); + } + async function get_points_of_orders() { + const result = await get_pointorder_range({ + pool_id, + left_point, + right_point, + }); + setOrders(result); + } + async function get_pool_detail() { + const p: PoolInfo = await get_pool(pool_id); + const { token_x, token_y } = p; + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + setPool(p); + } + function process_orders() { + const list = Object.values(orders); + const sell_token_x_list: IOrderPointItem[] = []; + const sell_token_y_list: IOrderPointItem[] = []; + const buy_token_x_list: IOrderPointItem[] = []; + const buy_token_y_list: IOrderPointItem[] = []; + const list_x = list.filter((item: IOrderPointItem) => + Big(item.amount_x).gt(0) + ); + const list_y = list + .filter((item: IOrderPointItem) => Big(item.amount_y).gt(0)) + .reverse(); + const { token_x_metadata, token_y_metadata } = pool; + // accumulate + list_x.forEach((item: IOrderPointItem) => { + const { point, amount_x } = item; + const price_x_base = get_price_by_point(point); + const price_y_base = Big(price_x_base).eq(0) + ? '0' + : Big(1).div(price_x_base).toFixed(); + const sell_x_readable = toReadableNumber( + token_x_metadata.decimals, + amount_x + ); + const buy_y_readable = Big(price_x_base).mul(sell_x_readable).toFixed(); + const length_sell_token_x_list = sell_token_x_list.length; + const length_buy_token_y_list = sell_token_x_list.length; + sell_token_x_list.push({ + ...item, + price: price_x_base, + amount_x_readable: sell_x_readable, + accumulated_x_readable: + length_sell_token_x_list == 0 + ? sell_x_readable + : Big( + sell_token_x_list[length_sell_token_x_list - 1] + .accumulated_x_readable + ) + .plus(sell_x_readable) + .toFixed(), + }); + buy_token_y_list.push({ + ...item, + price: price_y_base, + amount_y_readable: buy_y_readable, + accumulated_y_readable: + length_buy_token_y_list == 0 + ? buy_y_readable + : Big( + buy_token_y_list[length_buy_token_y_list - 1] + .accumulated_y_readable + ) + .plus(buy_y_readable) + .toFixed(), + }); + }); + list_y.forEach((item: IOrderPointItem) => { + const { point, amount_y } = item; + const price_x_base = get_price_by_point(point); + const price_y_base = Big(price_x_base).eq(0) + ? '0' + : Big(1).div(price_x_base).toFixed(); + const sell_y_readable = toReadableNumber( + token_y_metadata.decimals, + amount_y + ); + const buy_x_readable = Big(price_y_base).mul(sell_y_readable).toFixed(); + const length_sell_token_y_list = sell_token_y_list.length; + const length_buy_token_x_list = buy_token_x_list.length; + sell_token_y_list.push({ + ...item, + price: price_y_base, + amount_y_readable: sell_y_readable, + accumulated_y_readable: + length_sell_token_y_list == 0 + ? sell_y_readable + : Big( + sell_token_y_list[length_sell_token_y_list - 1] + .accumulated_y_readable + ) + .plus(sell_y_readable) + .toFixed(), + }); + buy_token_x_list.push({ + ...item, + price: price_x_base, + amount_x_readable: buy_x_readable, + accumulated_x_readable: + length_buy_token_x_list == 0 + ? buy_x_readable + : Big( + buy_token_x_list[length_buy_token_x_list - 1] + .accumulated_x_readable + ) + .plus(buy_x_readable) + .toFixed(), + }); + }); + const sell_token_x_list_reverse = sell_token_x_list.reverse(); + const sell_token_y_list_reverse = sell_token_y_list.reverse(); + set_buy_token_x_list(buy_token_x_list); + set_sell_token_x_list(sell_token_x_list_reverse); + set_buy_token_y_list(buy_token_y_list); + set_sell_token_y_list(sell_token_y_list_reverse); + + console.log('buy_token_x_list', buy_token_x_list); + console.log('sell_token_x_list', sell_token_x_list_reverse); + console.log('buy_token_y_list', buy_token_y_list); + console.log('sell_token_y_list', sell_token_y_list_reverse); + } + function get_price_by_point(point: number) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_rate_element() { + if (pool) { + const { current_point, token_x_metadata, token_y_metadata } = pool; + const current_price_x = get_price_by_point(current_point); + const current_price_y = Big(current_price_x).gt(0) + ? Big(1).div(current_price_x).toFixed() + : '0'; + return ( + <> + + 1{' '} + {switch_token == 'X' + ? token_x_metadata.symbol + : token_y_metadata.symbol}{' '} + = + + + {switch_token == 'X' + ? formatPrice(current_price_x) + : formatPrice(current_price_y)} + + + {switch_token == 'X' + ? token_y_metadata.symbol + : token_x_metadata.symbol} + + + ); + } + } + function marketRefresh() { + set_market_loading(true); + } + return ( + +
+
+
{cur_pair_icons}
+ {cur_pairs} +
+ +
+
+ {/* chart area */} +
+ {/* base data */} +
+
{get_rate_element()}
+
+
+ { + set_switch_token('X'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'X' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_x_metadata?.symbol} + + { + set_switch_token('Y'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'Y' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_y_metadata?.symbol} + +
+
+
+ +
+
+ +
+
+ +
+
+ +
+
+
+
+ {/* chart */} + +
+ {/* table area */} +
+
+ Limit Orders +
+
+
+ Price + {cur_pairs} +
+
+ Qty + + {cur_token_symbol} + +
+
+ Total Qty + + {cur_token_symbol} + +
+
+
+ {buy_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPrice(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || item.accumulated_y_readable + )} + +
+ ); + })} +
+
+ + Market Pirce + + +
+
+ {sell_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPrice(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || item.accumulated_y_readable + )} + +
+ ); + })} +
+
+
+
+ ); +} +function OrderChart() { + const { buy_list, sell_list, cur_pairs, cur_token_symbol } = + useContext(LimitOrderChartData); + const [foucsOrderPoint, setFoucsOrderPoint] = useState(); + const [side, setSide] = useState(); + // CONST start + const svg_width = 600; + const svg_height = 400; + const svg_padding = 40; + const axisRightWidth = 60; + const disFromHoverBoxToPointer = 20; + // CONST end + useEffect(() => { + if (sell_list?.length || buy_list?.length) { + drawChart(); + } else { + clearChart(); + } + }, [buy_list, sell_list]); + function drawChart() { + clearChart(); + // 获取价格区间、数量区间 + const prices: string[] = []; + const amounts: string[] = []; + buy_list.concat(sell_list).forEach((item: IOrderPointItem) => { + prices.push(item.price); + amounts.push( + Big(item.accumulated_x_readable || item.accumulated_y_readable).toFixed( + 0 + ) + ); + }); + prices.sort((b, a) => { + return Big(b).minus(a).toNumber(); + }); + amounts.sort((b, a) => { + return Big(b).minus(a).toNumber(); + }); + let price_range: number[]; + if (prices.length == 1) { + const p = +prices[0]; + const minP = Big(p).mul(0.5).toNumber(); + const maxP = Big(p).mul(2).toNumber(); + price_range = [minP, maxP]; + } else { + price_range = [+prices[0], +prices[prices.length - 1]]; + } + const amount_range: number[] = [+amounts[amounts.length - 1], 0]; + // 创建一个横坐标轴 + const scaleBottom = d3 + .scaleLinear() + .domain(price_range) + .range([0, svg_width - svg_padding - axisRightWidth]) + .clamp(true); + const axisBottom: any = d3.axisTop(scaleBottom).tickSize(0).tickPadding(10); + d3.select('.axisBottom') + .attr('transform', `translate(0, ${svg_height - svg_padding})`) + .call(axisBottom) + .selectAll('text') + .attr('fill', '#7E8A93'); + d3.select('.axisBottom').select('.domain').attr('stroke', 'transparent'); + + // 创建一个纵坐标 + const scaleRight = d3 + .scaleLinear() + .domain(amount_range) + .range([0, svg_height - svg_padding * 2]) + .clamp(true); + const axisRight: any = d3.axisLeft(scaleRight).tickSize(0).tickPadding(10); + d3.select('.axisRight') + .attr('transform', `translate(${svg_width - svg_padding}, 0)`) + .call(axisRight) + .selectAll('text') + .attr('fill', '#7E8A93') + .select('.domain'); + d3.select('.axisRight').select('.domain').attr('stroke', 'transparent'); + + // 面积 path data 生成器 + const areaGenerator = d3 + .area() + .x((d: any) => { + return +Big(scaleBottom(+d.price)).toFixed(0); + }) + .y0(() => { + return svg_height - svg_padding * 2; + }) + .y1((d: any) => { + return +Big( + scaleRight(+(d.accumulated_x_readable || d.accumulated_y_readable)) + ).toFixed(0); + }); + + // 折线path data生成器 + const lineGenerator = d3 + .line() + .x((d: any) => { + return +Big(scaleBottom(+d.price)).toFixed(0); + }) + .y((d: any) => { + return +Big( + scaleRight(+(d.accumulated_x_readable || d.accumulated_y_readable)) + ).toFixed(0); + }); + + // 虚线 path data 生成器 + const dashLineGenerator = d3.line(); + + /** 创建左侧区域 */ + // 面积 + if (buy_list?.length) { + const area_path_data_left = areaGenerator(buy_list); + d3.select('.areaLeft') + .append('path') + .attr('opacity', '0.3') + .attr('d', area_path_data_left) + .attr('fill', 'url(#paint0_linear_7545_2924)'); + + // 渐变色 + const max_y = buy_list[buy_list.length - 1]; + const y = +Big( + scaleRight( + +(max_y.accumulated_x_readable || max_y.accumulated_y_readable) + ) + ).toNumber(); + d3.select('.greenLinearGradient') + .attr('y1', y) + .attr('y2', svg_height - svg_padding * 2); + + // 折线 + var line_path_data_left = lineGenerator(buy_list); + d3.select('.areaLeft') + .append('path') + .attr('d', line_path_data_left) + .attr('stroke', '#00FFD1') + .attr('strokeWidth', '2') + .attr('fill', 'none'); + + // 触发鼠标事件的矩形区域 + const buy_list_first = buy_list[0]; + const buy_list_last = buy_list[buy_list.length - 1]; + d3.select('.rectLeft') + .append('rect') + .attr('width', () => { + return ( + scaleBottom(+buy_list_first.price) - + scaleBottom(+buy_list_last.price) + + svg_padding + ); + }) + .attr('height', () => { + return svg_height; + }) + .attr('x', () => { + return scaleBottom(+buy_list_last.price) - svg_padding; + }) + .attr('y', `${-svg_padding}`) + .attr('fill', 'transparent') + .on('mousemove', function (e) { + const { offsetX, offsetY } = e; + const list = buy_list.concat([]).reverse(); + const [targetX, targetY, targetItem] = searchNearCoordinate( + list, + e, + scaleBottom, + scaleRight + ); + if (!isInvalid(targetX) && !isInvalid(targetY)) { + showCrossDot({ + dashLineGenerator, + targetX, + targetY, + offsetX, + offsetY, + dotFillColor: '#00FFD1', + }); + setSide('buy'); + setFoucsOrderPoint(targetItem); + } + }) + .on('mouseleave', function (e, d) { + hideCrossDot(); + }); + } + + /** 创建右侧区域 */ + // 面积 + if (sell_list?.length) { + const area_path_data_right = areaGenerator(sell_list); + d3.select('.areaRight') + .append('path') + .attr('opacity', '0.3') + .attr('d', area_path_data_right) + .attr('fill', 'url(#paint0_linear_7545_2926)'); + + // 渐变色 + const max_y = sell_list[0]; + const y = +Big( + scaleRight( + +(max_y.accumulated_x_readable || max_y.accumulated_y_readable) + ) + ).toNumber(); + d3.select('.redLinearGradient') + .attr('y1', y) + .attr('y2', svg_height - svg_padding * 2); + + // 折线 + const line_path_data_right = lineGenerator(sell_list); + d3.select('.areaRight') + .append('path') + .attr('d', line_path_data_right) + .attr('stroke', '#FF6A8E') + .attr('strokeWidth', '2') + .attr('fill', 'none'); + // 触发鼠标事件的矩形区域 + const sell_list_first = sell_list[0]; + const sell_list_last = sell_list[sell_list.length - 1]; + d3.select('.rectRight') + .append('rect') + .attr('width', () => { + return ( + scaleBottom(+sell_list_first.price) - + scaleBottom(+sell_list_last.price) + + svg_padding + ); + }) + .attr('height', () => { + return svg_height; + }) + .attr('x', () => { + return scaleBottom(+sell_list_last.price); + }) + .attr('y', `${-svg_padding}`) + .attr('fill', 'transparent') + .on('mousemove', function (e) { + const { offsetX, offsetY } = e; + const list = sell_list.concat([]).reverse(); + const [targetX, targetY, targetItem] = searchNearCoordinate( + list, + e, + scaleBottom, + scaleRight + ); + if (!isInvalid(targetX) && !isInvalid(targetY)) { + showCrossDot({ + dashLineGenerator, + targetX, + targetY, + offsetX, + offsetY, + dotFillColor: '#FF6A8E', + }); + setSide('sell'); + setFoucsOrderPoint(targetItem); + } + }) + .on('mouseleave', function (e, d) { + hideCrossDot(); + }); + } + } + + function clearChart() { + d3.selectAll('.axisBottom *').remove(); + d3.selectAll('.axisRight *').remove(); + d3.selectAll('.areaLeft *').remove(); + d3.selectAll('.areaRight *').remove(); + d3.selectAll('.rectLeft *').remove(); + d3.selectAll('.rectRight *').remove(); + d3.select('.verticalDashLine').attr('d', ''); + d3.select('.horizontalDashLine').attr('d', ''); + } + // 找到离这个点最近的一个数据 + function searchNearCoordinate( + list: IOrderPointItem[], + e: any, + scaleBottom: Function, + scaleRight: Function + ) { + const { offsetX } = e; + const x = offsetX - svg_padding; + let targetX; + let targetY; + let targetItem; + let gtIndex = list.findIndex((item: IOrderPointItem) => { + return scaleBottom(+item.price) >= x; + }); + if (gtIndex == -1) { + gtIndex = list.length - 1; + } + const gtItem = list[gtIndex]; + const x1 = scaleBottom(+gtItem.price); + if (gtIndex == 0) { + targetY = scaleRight( + +(gtItem.accumulated_x_readable || gtItem.accumulated_y_readable) + ); + targetX = x1; + targetItem = gtItem; + } else { + const ltIndex = gtIndex - 1; + const ltItem = list[ltIndex]; + const x0 = scaleBottom(+ltItem.price); + if (x1 - x > x - x0) { + targetX = x0; + targetY = scaleRight( + +(ltItem.accumulated_x_readable || ltItem.accumulated_y_readable) + ); + targetItem = ltItem; + } else { + targetX = x1; + targetY = scaleRight( + +(gtItem.accumulated_x_readable || gtItem.accumulated_y_readable) + ); + targetItem = gtItem; + } + } + return [targetX, targetY, targetItem]; + } + function showCrossDot({ + dashLineGenerator, + targetX, + targetY, + offsetX, + offsetY, + dotFillColor, + }: { + dashLineGenerator: Function; + targetX: number; + targetY: number; + offsetX: number; + offsetY: number; + dotFillColor: string; + }) { + const pathDataX = dashLineGenerator([ + [targetX, -40], + [targetX, 360], + ]); + const pathDataY = dashLineGenerator([ + [0, targetY], + [520, targetY], + ]); + d3.select('.verticalDashLine').attr('d', pathDataX).attr('opacity', '1'); + d3.select('.horizontalDashLine').attr('d', pathDataY).attr('opacity', '1'); + d3.select('.dot') + .attr('cx', targetX) + .attr('cy', targetY) + .attr('opacity', '1') + .attr('fill', dotFillColor); + d3.select('.hoverBox').attr( + 'style', + `visibility:visible;transform:translate(${ + offsetX + disFromHoverBoxToPointer + }px, ${offsetY - disFromHoverBoxToPointer}px)` + ); + } + function hideCrossDot() { + d3.select('.verticalDashLine').attr('opacity', '0'); + d3.select('.horizontalDashLine').attr('opacity', '0'); + d3.select('.dot').attr('opacity', '0'); + d3.select('.hoverBox').attr('style', `visibility:invisible`); + } + return ( +
+ + + {/* 横坐标 */} + + {/* 纵坐标 */} + + {/* 左侧面积图 */} + + {/* 右侧面积图 */} + + {/* 左侧触发鼠标事件区域 */} + + {/* 右侧触发鼠标事件区域 */} + + {/* 垂直 虚线 */} + + {/* 水平 虚线 */} + + {/* 折线上的点 */} + + + {/* 渐变色绿色 */} + + + + + + + {/* 渐变色红色 */} + + + + + + + + {/* hover上去的悬浮框 */} +
+
+ Side + + {side} + +
+
+ Price({cur_pairs}) + + {formatPrice(foucsOrderPoint?.price)} + +
+
+ + Qty({cur_token_symbol}) + + + {formatNumber( + foucsOrderPoint?.amount_x_readable || + foucsOrderPoint?.amount_y_readable + )} + +
+
+ + Total Qty({cur_token_symbol}) + + + {formatNumber( + foucsOrderPoint?.accumulated_x_readable || + foucsOrderPoint?.accumulated_y_readable + )} + +
+
+
+ ); +} +function RefreshIcon(props: any) { + return ( + + + + + + + + + + + ); +} +function LeftArrowIcon(props: any) { + return ( + + + + ); +} +function RightArrowIcon(props: any) { + return ( + + + + ); +} + +function AddIcon(props: any) { + return ( + + + + + ); +} + +function SubIcon(props: any) { + return ( + + + + ); +} + +const formatNumber = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return toInternationalCurrencySystem(big.toFixed(2, 1)); + } +}; +const formatPrice = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.0001'; + } else { + return big.toFixed(4, 1); + } +}; +const isInvalid = function (v: any) { + if (v === '' || v === undefined || v === null) return true; + return false; +}; + +interface IOrderPoint { + [point: string]: IOrderPointItem; +} +interface IOrderPointItem { + point: number; + amount_x: string; + amount_y: string; + price?: string; + amount_x_readable?: string; + amount_y_readable?: string; + accumulated_x_readable?: string; + accumulated_y_readable?: string; +} + +type ISwitchToken = 'X' | 'Y'; +type ISide = 'buy' | 'sell'; diff --git a/src/components/swap/SwapProTab.tsx b/src/components/swap/SwapProTab.tsx new file mode 100644 index 000000000..5525e313b --- /dev/null +++ b/src/components/swap/SwapProTab.tsx @@ -0,0 +1,31 @@ +import React, { useContext, useEffect, useState } from 'react'; +import { SwapProContext } from '../../pages/SwapPage'; +export default function ProTab() { + const { proTab, setProTab, dcl_pool_id } = useContext(SwapProContext); + if (!dcl_pool_id) return null; + return ( +
+
{ + setProTab('PRICE'); + }} + className={`flex items-center justify-center px-4 py-0.5 rounded-md text-sm cursor-pointer ${ + proTab == 'PRICE' ? 'bg-proTabBgColor text-white' : 'text-primaryText' + }`} + > + Price +
+
{ + setProTab('ORDER'); + }} + className={`flex items-center justify-center px-4 py-0.5 rounded-md text-sm cursor-pointer ${ + proTab == 'ORDER' ? 'bg-proTabBgColor text-white' : 'text-primaryText' + }`} + > + Orders +
+
+ ); +} +export type IProTab = 'PRICE' | 'ORDER'; diff --git a/src/components/swap/SwapRateChart.tsx b/src/components/swap/SwapRateChart.tsx index e555bb76f..0dab27d93 100644 --- a/src/components/swap/SwapRateChart.tsx +++ b/src/components/swap/SwapRateChart.tsx @@ -35,6 +35,7 @@ import { SwapProContext } from '../../pages/SwapPage'; import { scientificNotationToString, toPrecision } from '../../utils/numbers'; import Big from 'big.js'; import { toRealSymbol } from '../../utils/token'; +import SwapProTab from './SwapProTab'; export interface SwapRateChartProps { tokenIn: TokenMetadata; tokenOut: TokenMetadata; @@ -356,28 +357,7 @@ export default function SwapRateChart(props: SwapRateChartProps) { }} />
- -
- {dimensionList.map((d) => { - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - - changeDisplayDimension(d); - }} - > - {d} -
- ); - })} -
+
@@ -402,93 +382,115 @@ export default function SwapRateChart(props: SwapRateChartProps) { })}
-
-
- - {diff ? priceFormatter(diff.curPrice) : '-'} - - - {diff && ( - - {displayTokenOut && toRealSymbol(displayTokenOut.symbol)} - - )} - {diff && ( - - {diff.direction !== 'unChange' && ( - - )} - - {diff.percent} +
+
+
+ + {diff ? priceFormatter(diff.curPrice) : '-'} - )} -
- {diff && ( -
- - - + {diff && ( + + {displayTokenOut && toRealSymbol(displayTokenOut.symbol)} + + )} + {diff && ( + + {diff.direction !== 'unChange' && ( + + )} - {diff.lastUpdate} + {diff.percent} + + )}
- )} - - {diff && ( - <> -
- + {diff && ( +
+ - {`(${displayDimension})`} - - {priceList && - priceFormatter( - minBy(priceList.price_list, (p) => p.price)?.price || 0 - )} - + {diff.lastUpdate}
+ )} -
- - - {`(${displayDimension})`} - + {diff && ( + <> +
+ + + {`(${displayDimension})`} + + + + {priceList && + priceFormatter( + minBy(priceList.price_list, (p) => p.price)?.price || 0 + )} + +
- - {priceList && - priceFormatter( - maxBy(priceList.price_list, (p) => p.price)?.price || 0 - )} - -
- - )} +
+ + + {`(${displayDimension})`} + + + + {priceList && + priceFormatter( + maxBy(priceList.price_list, (p) => p.price)?.price || 0 + )} + +
+ + )} +
+
+ {dimensionList.map((d) => { + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + + changeDisplayDimension(d); + }} + > + {d} +
+ ); + })} +
{diff && ( diff --git a/src/global.css b/src/global.css index fb1ee8781..aa8db163f 100644 --- a/src/global.css +++ b/src/global.css @@ -1337,3 +1337,8 @@ input[type='range']::-webkit-slider-runnable-track { background-color: #00d6af; height: 10px; } + +.refresh-loader { + animation: spin 1s linear infinite; + transform-origin: center; +} diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index 20f2fdb2c..42a7e88a1 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -2,6 +2,7 @@ import React, { createContext, useContext, useEffect, useState } from 'react'; import SwapCard from '../components/swap/SwapCard'; import Loading from '../components/layout/Loading'; +import { IProTab } from '../components/swap/SwapProTab'; import { useTriTokens, useWhitelistTokens, @@ -22,6 +23,7 @@ import { import AdSwiper from '../components/layout/Swiper'; import LimitOrderCard from '../components/swap/LimitOrderCard'; import SwapRateChart from '../components/swap/SwapRateChart'; +import SwapLimitOrderChart from '../components/swap/SwapLimitOrderChart'; import { EstimateSwapView } from '../services/swap'; import { TradeRoute } from '../components/layout/SwapRoutes'; import { MarketList } from '../components/layout/SwapRoutes'; @@ -29,6 +31,7 @@ import MyOrderPage from './MyOrder'; import MyOrderComponent from './Orderly/components/MyOrder'; import { useWalletSelector } from '../context/WalletSelectorContext'; import { useClientMobile } from '../utils/device'; +import { useDclPoolIdByUrl } from '../state/swapV3'; export const SWAP_MODE_KEY = 'SWAP_MODE_VALUE'; @@ -99,6 +102,9 @@ interface SwapProContextValue { swapMode: SWAP_MODE; forceEstimatePro?: boolean; setForceEstimatePro?: (e?: boolean) => void; + proTab?: IProTab; + setProTab?: Function; + dcl_pool_id?: string; } export const SwapProContext = createContext(null); @@ -224,13 +230,13 @@ function SwapPage() { ); const [forceEstimatePro, setForceEstimatePro] = useState(false); + const [proTab, setProTab] = useState('ORDER'); const changeSwapType = (type: SWAP_TYPE) => { setSwapType(type); sessionStorage.setItem(SWAP_TYPE_KEY, type); setForceEstimatePro(true); }; - useEffect(() => { const changeWindowCommonMenuCollapsed = (e: any) => { if (e?.[SWAP_TYPE_KEY]) { @@ -273,7 +279,7 @@ function SwapPage() { const [swapMode, setSwapMode] = useState( storageMode || SWAP_MODE.NORMAL ); - + const dcl_pool_id = useDclPoolIdByUrl(); useEffect(() => { if (swapMode === SWAP_MODE.LIMIT) { setLimitTokenTrigger(!limitTokenTrigger ? true : false); @@ -315,7 +321,6 @@ function SwapPage() { changeSwapType={changeSwapType} /> ); - return (
@@ -340,7 +348,12 @@ function SwapPage() { maxWidth: '850px', }} > - + {((dcl_pool_id && proTab == 'PRICE') || !dcl_pool_id) && ( + + )} + {dcl_pool_id && proTab == 'ORDER' && ( + + )} {swapMode === SWAP_MODE.NORMAL ? ( <>
}
)} -
{swapMode === SWAP_MODE.NORMAL && ( diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index 26b0b6e6e..2299532b4 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -1,4 +1,5 @@ import React, { useState, useEffect, useContext } from 'react'; +import { useLocation } from 'react-router'; import { UserOrderInfo, list_active_orders, @@ -12,6 +13,8 @@ import { ftGetTokenMetadata } from '../services/ft-contract'; import { ONLY_ZEROS, toReadableNumber } from '../utils/numbers'; import BigNumber from 'bignumber.js'; import { getDCLTopBinFee } from '../services/indexer'; +import { list_pools } from '../services/swapV3'; +import { WRAP_NEAR_CONTRACT_ID } from '../services/wrap-near'; import Big from 'big.js'; export const useMyOrders = () => { @@ -104,3 +107,37 @@ export const useAllPoolsV2 = () => { }, [tokenPriceList]); return allPools; }; + +export const useDclPoolIdByUrl = () => { + const [pool_id, set_pool_id] = useState(); + const location = useLocation(); + const hash = location.hash; + useEffect(() => { + get_all_dcl_pools_by_url(); + }, [hash]); + + async function get_all_dcl_pools_by_url() { + const dcl_pools: PoolInfo[] = await list_pools(); + const [urlTokenIn, urlTokenOut] = decodeURIComponent( + location.hash.slice(1) + ).split('|'); + const url_token_in = + urlTokenIn == 'near' ? WRAP_NEAR_CONTRACT_ID : urlTokenIn; + const url_token_out = + urlTokenOut == 'near' ? WRAP_NEAR_CONTRACT_ID : urlTokenOut; + const target: PoolInfo = dcl_pools.find((pool: PoolInfo) => { + const { token_x, token_y } = pool; + return ( + (url_token_in == token_x && url_token_out == token_y) || + (url_token_in == token_y && url_token_out == token_x) + ); + }); + if (target) { + set_pool_id(target.pool_id); + } else { + set_pool_id(''); + } + } + + return pool_id; +}; diff --git a/tailwind.config.js b/tailwind.config.js index 207ba18f7..dbffbee17 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -407,6 +407,7 @@ module.exports = { overviewGreyColor:'#314758', chartHoverBoxBg:'rgba(26, 39, 48, 0.9)', chartBorderColor:'#344451', + proTabBgColor:'#324451' }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From a8c3026ef9480b78eaa750b37adb27d67fa78f08 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 17 Jun 2023 21:15:05 +0800 Subject: [PATCH 026/204] update --- src/pages/SwapPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index 42a7e88a1..0e579690f 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -230,7 +230,7 @@ function SwapPage() { ); const [forceEstimatePro, setForceEstimatePro] = useState(false); - const [proTab, setProTab] = useState('ORDER'); + const [proTab, setProTab] = useState('PRICE'); const changeSwapType = (type: SWAP_TYPE) => { setSwapType(type); From 7590d73b4fd9a6a7cb63426830f7d7dba8a3fbcf Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 18 Jun 2023 10:38:39 +0800 Subject: [PATCH 027/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 0d2708551..2e31e2751 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -379,7 +379,7 @@ export default function SwapLimitOrderChart() {
- Total Qty + Total Qty {cur_token_symbol} From 550d39e44e6fb4fbbc9b434b5d3659ae43a54ffc Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 18 Jun 2023 18:13:54 +0800 Subject: [PATCH 028/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 2e31e2751..1731a8471 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -510,7 +510,7 @@ function OrderChart() { .range([0, svg_width - svg_padding - axisRightWidth]) .clamp(true); const axisBottom: any = d3.axisTop(scaleBottom).tickSize(0).tickPadding(10); - d3.select('.axisBottom') + d3.select('.axisBottom').transition() .attr('transform', `translate(0, ${svg_height - svg_padding})`) .call(axisBottom) .selectAll('text') @@ -524,7 +524,7 @@ function OrderChart() { .range([0, svg_height - svg_padding * 2]) .clamp(true); const axisRight: any = d3.axisLeft(scaleRight).tickSize(0).tickPadding(10); - d3.select('.axisRight') + d3.select('.axisRight').transition() .attr('transform', `translate(${svg_width - svg_padding}, 0)`) .call(axisRight) .selectAll('text') From 55d4f3615340254cac6c5231a88dc9c4f2f7b8bf Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 18 Jun 2023 21:51:44 +0800 Subject: [PATCH 029/204] fix swap bug --- src/services/swapV3.ts | 232 ++++++++++++++++++++--------------------- tailwind.config.js | 28 ++--- 2 files changed, 129 insertions(+), 131 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 88ee2b11b..f8e1f52f2 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -316,151 +316,149 @@ export const v3Swap = async ({ ) ); } + } - if (SwapByOutput) { - const pool_ids = Swap.pool_ids; - const output_token = tokenB.id; - const output_amount = toNonDivisibleNumber(tokenB.decimals, amountB); - const msg = JSON.stringify({ - SwapByOutput: { - pool_ids, - output_token, - output_amount, - }, - }); - const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { - throw new Error(`${tokenB.id} doesn't exist.`); - }); - - if (tokenRegistered === null) { - transactions.push({ - receiverId: tokenB.id, - functionCalls: [registerAccountOnToken()], - }); - } + if (SwapByOutput) { + const pool_ids = Swap.pool_ids; + const output_token = tokenB.id; + const output_amount = toNonDivisibleNumber(tokenB.decimals, amountB); + const msg = JSON.stringify({ + SwapByOutput: { + pool_ids, + output_token, + output_amount, + }, + }); + const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { + throw new Error(`${tokenB.id} doesn't exist.`); + }); + if (tokenRegistered === null) { transactions.push({ - receiverId: tokenA.id, - functionCalls: [ - { - methodName: 'ft_transfer_call', - args: { - receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, - amount: toNonDivisibleNumber(tokenA.decimals, amountA), - msg, - }, - gas: '180000000000000', - amount: ONE_YOCTO_NEAR, - }, - ], + receiverId: tokenB.id, + functionCalls: [registerAccountOnToken()], }); } - if (LimitOrderWithSwap) { - const pool_id = LimitOrderWithSwap.pool_id; - - const fee = Number(pool_id.split(V3_POOL_SPLITER)[2]); - - const buy_token = tokenB.id; - const point = priceToPoint({ - amountA, - amountB, - tokenA, - tokenB, - fee, - }); - - const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { - throw new Error(`${tokenB.id} doesn't exist.`); - }); + transactions.push({ + receiverId: tokenA.id, + functionCalls: [ + { + methodName: 'ft_transfer_call', + args: { + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: toNonDivisibleNumber(tokenA.decimals, amountA), + msg, + }, + gas: '180000000000000', + amount: ONE_YOCTO_NEAR, + }, + ], + }); + } - if (tokenRegistered === null) { - transactions.push({ - receiverId: tokenB.id, - functionCalls: [registerAccountOnToken()], - }); - } + if (LimitOrderWithSwap) { + const pool_id = LimitOrderWithSwap.pool_id; - const DCLRegistered = await swapV3GetStorageBalance(tokenB.id).catch( - () => { - throw new Error(`${tokenB.id} doesn't exist.`); - } - ); + const fee = Number(pool_id.split(V3_POOL_SPLITER)[2]); - if (DCLRegistered === null) { - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - { - methodName: 'storage_deposit', - args: { - registration_only: true, - account_id: getCurrentWallet()?.wallet?.getAccountId(), - }, - gas: '30000000000000', - amount: '0.5', - }, - ], - }); - } + const buy_token = tokenB.id; + const point = priceToPoint({ + amountA, + amountB, + tokenA, + tokenB, + fee, + }); - const new_point = - pool_id.split(V3_POOL_SPLITER)[0] === tokenA.id ? point : -point; + const tokenRegistered = await ftGetStorageBalance(tokenB.id).catch(() => { + throw new Error(`${tokenB.id} doesn't exist.`); + }); - const msg = JSON.stringify({ - LimitOrderWithSwap: { - client_id: '', - pool_id, - buy_token, - point: new_point, - }, + if (tokenRegistered === null) { + transactions.push({ + receiverId: tokenB.id, + functionCalls: [registerAccountOnToken()], }); + } + const DCLRegistered = await swapV3GetStorageBalance(tokenB.id).catch(() => { + throw new Error(`${tokenB.id} doesn't exist.`); + }); + + if (DCLRegistered === null) { transactions.push({ - receiverId: tokenA.id, + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ { - methodName: 'ft_transfer_call', + methodName: 'storage_deposit', args: { - receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, - amount: toNonDivisibleNumber(tokenA.decimals, amountA), - msg, + registration_only: true, + account_id: getCurrentWallet()?.wallet?.getAccountId(), }, - gas: '250000000000000', - amount: ONE_YOCTO_NEAR, + gas: '30000000000000', + amount: '0.5', }, ], }); } - if (tokenA.id === WRAP_NEAR_CONTRACT_ID && tokenA.symbol == 'NEAR') { - transactions.unshift(nearDepositTransaction(amountA)); - } + const new_point = + pool_id.split(V3_POOL_SPLITER)[0] === tokenA.id ? point : -point; - if (tokenA.id === WRAP_NEAR_CONTRACT_ID) { - const registered = await ftGetStorageBalance(WRAP_NEAR_CONTRACT_ID); - if (registered === null) { - transactions.unshift({ - receiverId: WRAP_NEAR_CONTRACT_ID, - functionCalls: [registerAccountOnToken()], - }); - } - } + const msg = JSON.stringify({ + LimitOrderWithSwap: { + client_id: '', + pool_id, + buy_token, + point: new_point, + }, + }); - const neededStorage = await get_user_storage_detail({ size: 1 }); - if (!ONLY_ZEROS.test(neededStorage)) { + transactions.push({ + receiverId: tokenA.id, + functionCalls: [ + { + methodName: 'ft_transfer_call', + args: { + receiver_id: REF_UNI_V3_SWAP_CONTRACT_ID, + amount: toNonDivisibleNumber(tokenA.decimals, amountA), + msg, + }, + gas: '250000000000000', + amount: ONE_YOCTO_NEAR, + }, + ], + }); + } + + if (tokenA.id === WRAP_NEAR_CONTRACT_ID && tokenA.symbol == 'NEAR') { + transactions.unshift(nearDepositTransaction(amountA)); + } + + if (tokenA.id === WRAP_NEAR_CONTRACT_ID) { + const registered = await ftGetStorageBalance(WRAP_NEAR_CONTRACT_ID); + if (registered === null) { transactions.unshift({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - storageDepositAction({ - amount: neededStorage, - registrationOnly: true, - }), - ], + receiverId: WRAP_NEAR_CONTRACT_ID, + functionCalls: [registerAccountOnToken()], }); } } + const neededStorage = await get_user_storage_detail({ size: 1 }); + if (!ONLY_ZEROS.test(neededStorage)) { + transactions.unshift({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), + ], + }); + } + return executeMultipleTransactions(transactions); }; @@ -780,8 +778,8 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); } } - if(deposit_fee.eq(0)){ - return '' + if (deposit_fee.eq(0)) { + return ''; } return utils.format.formatNearAmount(deposit_fee.toFixed(0)); diff --git a/tailwind.config.js b/tailwind.config.js index 207ba18f7..200d6c3f6 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -393,20 +393,20 @@ module.exports = { burrowTitleGreenColor: '#78FF9E', burrowPurpleColor: '#BCAB8F', burrowDarkColor: '#04121F', - burrowTabColor:'#22333E', - burrowTableBorderColor:"rgba(48, 67, 82, 0.5)", - burrowTitleGreenColor:'#78FF9E', - burrowPurpleColor:'#BCAB8F', - burrowDarkColor:'#04121F', - overviewBurrowColor:'#93806E', - overviewBurrowRedColor:'#F083BE', - overviewBorderColor:'#263540', - overviewLightBlueColor:'#ACE1FF', - overviewMaskColor:'rgba(13, 29, 39, 0.5)', - overviewPurpleColor:'#816CFF', - overviewGreyColor:'#314758', - chartHoverBoxBg:'rgba(26, 39, 48, 0.9)', - chartBorderColor:'#344451', + burrowTabColor: '#22333E', + burrowTableBorderColor: 'rgba(48, 67, 82, 0.5)', + burrowTitleGreenColor: '#78FF9E', + burrowPurpleColor: '#BCAB8F', + burrowDarkColor: '#04121F', + overviewBurrowColor: '#93806E', + overviewBurrowRedColor: '#F083BE', + overviewBorderColor: '#263540', + overviewLightBlueColor: '#ACE1FF', + overviewMaskColor: 'rgba(13, 29, 39, 0.5)', + overviewPurpleColor: '#816CFF', + overviewGreyColor: '#314758', + chartHoverBoxBg: 'rgba(26, 39, 48, 0.9)', + chartBorderColor: '#344451', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From da215b0deeed674f620acff547f1d63b5c7e7c47 Mon Sep 17 00:00:00 2001 From: Max Date: Sun, 18 Jun 2023 21:53:14 +0800 Subject: [PATCH 030/204] add limit order check storage function --- src/services/swapV3.ts | 26 +++++++++++++------------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index f8e1f52f2..9355c4395 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -430,6 +430,19 @@ export const v3Swap = async ({ }, ], }); + + const neededStorage = await get_user_storage_detail({ size: 1 }); + if (!ONLY_ZEROS.test(neededStorage)) { + transactions.unshift({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ + amount: neededStorage, + registrationOnly: true, + }), + ], + }); + } } if (tokenA.id === WRAP_NEAR_CONTRACT_ID && tokenA.symbol == 'NEAR') { @@ -446,19 +459,6 @@ export const v3Swap = async ({ } } - const neededStorage = await get_user_storage_detail({ size: 1 }); - if (!ONLY_ZEROS.test(neededStorage)) { - transactions.unshift({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - storageDepositAction({ - amount: neededStorage, - registrationOnly: true, - }), - ], - }); - } - return executeMultipleTransactions(transactions); }; From 5c174fc74d2af8691fb5331432f014e1dd0f2691 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 18 Jun 2023 22:17:53 +0800 Subject: [PATCH 031/204] fix bugs --- src/components/farm/FarmsDclDetail.tsx | 134 ++++++++++++++------ src/components/swap/SwapLimitOrderChart.tsx | 10 +- src/services/farm.ts | 15 --- 3 files changed, 102 insertions(+), 57 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index f93189cc7..524d1efd4 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -103,7 +103,6 @@ export default function FarmsDclDetail(props: { dayVolumeMap, all_seeds, } = props; - console.log('8888888888-detailData', detailData); const [listLiquidities, setListLiquidities] = useState( [] ); @@ -168,15 +167,15 @@ export default function FarmsDclDetail(props: { const unclaimedRewardsData = useMemo(() => { return getTotalUnclaimedRewards(); }, [user_unclaimed_map[detailData.seed_id]]); - const [yp_percent, yp_canFarm_value, yp_unFarm_value] = useMemo(() => { + const [yp_percent, yp_farming_value, yp_unFarm_value] = useMemo(() => { if (!listLiquiditiesLoading) { - const { total_value, can_farm_parts_value, un_farm_parts_value } = + const { farming_parts_value, can_farm_parts_value, un_farm_parts_value } = caculate_values(); - if (total_value.gt(0)) { - const percent = can_farm_parts_value.div(total_value).mul(100); + if (can_farm_parts_value.gt(0)) { + const percent = farming_parts_value.div(can_farm_parts_value).mul(100); return [ formatPercentage(percent.toFixed()), - formatWithCommas_usd(can_farm_parts_value.toFixed()), + formatWithCommas_usd(farming_parts_value.toFixed()), formatWithCommas_usd(un_farm_parts_value.toFixed()), ]; } else { @@ -424,8 +423,6 @@ export default function FarmsDclDetail(props: { set_listLiquidities_unFarimg(temp_free_final); set_listLiquidities_unavailable(temp_unavailable_final); setListLiquidities(matched_liquidities); - } - if (!user_data_loading) { setListLiquiditiesLoading(false); } } @@ -983,18 +980,13 @@ export default function FarmsDclDetail(props: { const can_farm_liquidities = listLiquidities_inFarimg.concat( listLiquidities_unFarimg ); - // 可以farm的所有流动性的总价值 - let total_value = Big(0); - can_farm_liquidities.forEach((l: UserLiquidityInfo) => { - const l_v = get_liquidity_value(l); - total_value = total_value.plus(l_v || 0); - }); // 能farm部分的的流动性的总价值 let can_farm_parts_value = Big(0); can_farm_liquidities.forEach((l: UserLiquidityInfo) => { const l_v = get_range_part_value(l); can_farm_parts_value = can_farm_parts_value.plus(l_v || 0); }); + // unFarming 部分流动性的总价值(包含部分在farm中的那个NFT剩余的部分) let un_farm_parts_value = Big(0); listLiquidities_unFarimg.forEach((l: UserLiquidityInfo) => { @@ -1014,10 +1006,13 @@ export default function FarmsDclDetail(props: { .mul(v); un_farm_parts_value = un_farm_parts_value.plus(un_farm_part_value); } + + // farming 部分流动性的总价值 + const farming_parts_value = can_farm_parts_value.minus(un_farm_parts_value); return { - total_value, can_farm_parts_value, un_farm_parts_value, + farming_parts_value, }; } function get_range_part_value(liquidity: UserLiquidityInfo) { @@ -1078,7 +1073,7 @@ export default function FarmsDclDetail(props: { liquidities, total_v_liquidity: total_v_liquidity.toFixed(), withdraw_amount: withdraw_amount.toFixed(), - canStake: total_v_liquidity.lt(min_deposit) ? false : true, + canStake: total_v_liquidity.lt(min_deposit) || isEnded ? false : true, }; } function get_unStake_info() { @@ -1094,6 +1089,7 @@ export default function FarmsDclDetail(props: { }; } /** new end */ + const isEmpty = !canStake && !canUnStake; return (
@@ -1372,28 +1368,49 @@ export default function FarmsDclDetail(props: {
{/* login area */} + {/* add liquidity entry bar */} +
{/* Your Farming Position */}
- + Your Farming Position - {yp_canFarm_value} + + {yp_farming_value} +
- {yp_percent} of your total liquidity + {yp_percent} of your liquidity that can be farmed
- {yp_unFarm_value} available to - stake + + {yp_unFarm_value} + {' '} + available to stake
{canStake ? ( @@ -1437,15 +1454,15 @@ export default function FarmsDclDetail(props: {
{/* unClaimed Rewards for PC */} - {user_seeds_map[detailData.seed_id] ? ( - - ) : null} + {/* {user_seeds_map[detailData.seed_id] ? ( */} + + {/* ) : null} */}
{/* unClaimed Rewards for Mobile */} @@ -1598,6 +1615,50 @@ export default function FarmsDclDetail(props: {
); } +function AddLiquidityEntryBar(props: { + loading: boolean; + inFarimg: UserLiquidityInfo[]; + unFarimg: UserLiquidityInfo[]; + unavailable: UserLiquidityInfo[]; + detailData: Seed; + isEnded: boolean; +}) { + let tip: any; + const { loading, inFarimg, unFarimg, unavailable, detailData, isEnded } = + props; + if (!loading && inFarimg.length == 0 && unFarimg.length == 0) { + if (unavailable.length == 0) { + tip = ; + } else { + tip = + 'The price range of your liquidity is out of reward range. Please add liquidity within reward range.'; + } + } + if (loading || !tip || isEnded) return null; + return ( +
+
+
+

{tip}

+ { + const poolId = detailData.pool.pool_id; + const params_str = get_pool_name(poolId); + openUrl(`/addLiquidityV2#${params_str}`); + }} + color="#fff" + minWidth="9rem" + className={`flex-shrink-0 px-1 h-8 ml-5 text-center text-sm text-white focus:outline-none font-semibold `} + > + + +
+
+ ); +} function UserTotalUnClaimBlock(props: { detailData: Seed; tokenPriceList: any; @@ -1623,14 +1684,9 @@ function UserTotalUnClaimBlock(props: { function claimReward() { if (claimLoading) return; setClaimLoading(true); - claimRewardBySeed_boost(detailData.seed_id) - // .then(() => { - // window.location.reload(); - // }) - .catch((error) => { - setClaimLoading(false); - // setError(error); - }); + claimRewardBySeed_boost(detailData.seed_id).catch((error) => { + setClaimLoading(false); + }); } function getTotalUnclaimedRewards() { let totalPrice = 0; diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 1731a8471..2244b2f81 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -379,7 +379,9 @@ export default function SwapLimitOrderChart() {
- Total Qty + + Total Qty + {cur_token_symbol} @@ -510,7 +512,8 @@ function OrderChart() { .range([0, svg_width - svg_padding - axisRightWidth]) .clamp(true); const axisBottom: any = d3.axisTop(scaleBottom).tickSize(0).tickPadding(10); - d3.select('.axisBottom').transition() + d3.select('.axisBottom') + .transition() .attr('transform', `translate(0, ${svg_height - svg_padding})`) .call(axisBottom) .selectAll('text') @@ -524,7 +527,8 @@ function OrderChart() { .range([0, svg_height - svg_padding * 2]) .clamp(true); const axisRight: any = d3.axisLeft(scaleRight).tickSize(0).tickPadding(10); - d3.select('.axisRight').transition() + d3.select('.axisRight') + .transition() .attr('transform', `translate(${svg_width - svg_padding}, 0)`) .call(axisRight) .selectAll('text') diff --git a/src/services/farm.ts b/src/services/farm.ts index 02726b26c..edb8e098c 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -867,11 +867,6 @@ export const getBoostSeeds = async (): Promise<{ list.forEach((s: BoostSeeds) => { const { id, update_time, ...info } = s; const { seed, farmList, pool } = info; - // ////// for test test test start todo - // const { seed_id } = seed; - // const contractId = seed_id.split('@')[0]; - // if (contractId == REF_UNI_V3_SWAP_CONTRACT_ID) return; - // ////// for test test test end todo seeds.push(seed); farms.push(farmList); if (pool) { @@ -904,16 +899,6 @@ export const getBoostSeedsFromServer = async (): Promise<{ const dcl_all_pools: PoolInfo[] = await listPools(); let pools: any[] = []; const both_normalPools_dclPools: any[] = []; - // ////// for test test test start todo - // const no_dcl_seeds:Seed[] = []; - // list_seeds.forEach((seed:Seed) => { - // const { seed_id } = seed; - // const contractId = seed_id.split('@')[0]; - // if (contractId == REF_UNI_V3_SWAP_CONTRACT_ID) return; - // no_dcl_seeds.push(seed); - // }) - // list_seeds = no_dcl_seeds; - // ////// for test test test end todo list_seeds.forEach((seed: Seed) => { const { seed_id } = seed; // seed type: [commonSeed, loveSeed, dclSeed] From 655332f4dc1b1a66668e3971e8ded6ac2ed653f4 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 18 Jun 2023 23:29:36 +0800 Subject: [PATCH 032/204] update code for tester --- src/components/farm/FarmsHome.tsx | 43 +++++++++++++++++-------------- src/services/farm.ts | 9 ++++++- src/worker.ts | 19 +++++++------- 3 files changed, 41 insertions(+), 30 deletions(-) diff --git a/src/components/farm/FarmsHome.tsx b/src/components/farm/FarmsHome.tsx index 9640e0351..2e3764543 100644 --- a/src/components/farm/FarmsHome.tsx +++ b/src/components/farm/FarmsHome.tsx @@ -592,7 +592,6 @@ export default function FarmsHome(props: any) { is_dcl_pool = true; } const targetFarms = farm_display_List.find((seed: Seed) => { - debugger; const { seed_id, farmList } = seed; const status = farmList[0].status; const id = getPoolIdBySeedId(seed_id); @@ -3508,12 +3507,14 @@ function WithDrawBox(props: { const [key, v] = arr; const singlePrice = tokenPriceList[key]?.price; const token = rewardTokenList[key]; - const number: any = toReadableNumber(token.decimals, v); - if (singlePrice && singlePrice != 'N/A') { - totalUnWithDraw = BigNumber.sum( - singlePrice * number, - totalUnWithDraw - ).toNumber(); + if (token) { + const number: any = toReadableNumber(token.decimals, v); + if (singlePrice && singlePrice != 'N/A') { + totalUnWithDraw = BigNumber.sum( + singlePrice * number, + totalUnWithDraw + ).toNumber(); + } } }); if (totalUnWithDraw > 0) { @@ -3763,12 +3764,14 @@ function WithDrawb(props: { const [key, v] = arr; const singlePrice = tokenPriceList[key]?.price; const token = rewardTokenList[key]; - const number: any = toReadableNumber(token.decimals, v); - if (singlePrice && singlePrice != 'N/A') { - totalUnWithDraw = BigNumber.sum( - singlePrice * number, - totalUnWithDraw - ).toNumber(); + if (token) { + const number: any = toReadableNumber(token.decimals, v); + if (singlePrice && singlePrice != 'N/A') { + totalUnWithDraw = BigNumber.sum( + singlePrice * number, + totalUnWithDraw + ).toNumber(); + } } }); if (totalUnWithDraw > 0) { @@ -4099,12 +4102,14 @@ function WithDrawModal(props: { const [key, v] = arr; const singlePrice = tokenPriceList[key]?.price; const token = rewardTokenList[key]; - const number: any = toReadableNumber(token.decimals, v); - if (singlePrice && singlePrice != 'N/A') { - totalUnWithDraw = BigNumber.sum( - singlePrice * number, - totalUnWithDraw - ).toNumber(); + if (token) { + const number: any = toReadableNumber(token.decimals, v); + if (singlePrice && singlePrice != 'N/A') { + totalUnWithDraw = BigNumber.sum( + singlePrice * number, + totalUnWithDraw + ).toNumber(); + } } }); if (totalUnWithDraw > 0) { diff --git a/src/services/farm.ts b/src/services/farm.ts index edb8e098c..cd6a8eda0 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -891,6 +891,14 @@ export const getBoostSeedsFromServer = async (): Promise<{ try { // get all seeds let list_seeds = await list_seeds_info(); + // not the classic and dcl seeds would be filtered + list_seeds = list_seeds.filter((seed: Seed) => { + const contract_id = seed.seed_id.split('@')?.[0]; + return ( + contract_id == REF_UNI_V3_SWAP_CONTRACT_ID || + contract_id == REF_FI_CONTRACT_ID + ); + }); // get all farms const farmsPromiseList: Promise[] = []; const poolIds = new Set(); @@ -1190,7 +1198,6 @@ export const stake_boost_nft = async ({ const transactions: Transaction[] = []; const functionCalls = []; const { lpt_id, mft_id } = liquidity; - debugger; if (needUnstake) { const v_liquidity = mint_liquidity(liquidity, seed_id); if (+withdraw_amount > 0) { diff --git a/src/worker.ts b/src/worker.ts index 3d9fe09f6..2b1a565e4 100644 --- a/src/worker.ts +++ b/src/worker.ts @@ -14,6 +14,7 @@ const { XREF_TOKEN_ID, REF_FARM_BOOST_CONTRACT_ID, REF_UNI_V3_SWAP_CONTRACT_ID, + REF_FI_CONTRACT_ID, DCL_POOL_BLACK_LIST, } = getConfig(); @@ -201,6 +202,14 @@ async function getXrefPrice(tokenPriceList: Record) { const cacheBoost_Seed_Farms_Pools = async () => { // get all seeds let list_seeds = await get_list_seeds_info(); + // not the classic and dcl seeds would be filtered + list_seeds = list_seeds.filter((seed: Seed) => { + const contract_id = seed.seed_id.split('@')?.[0]; + return ( + contract_id == REF_UNI_V3_SWAP_CONTRACT_ID || + contract_id == REF_FI_CONTRACT_ID + ); + }); // get all farms const farmsPromiseList: Promise[] = []; // get all dcl pools @@ -208,16 +217,6 @@ const cacheBoost_Seed_Farms_Pools = async () => { const poolIds = new Set(); const dcl_poolIds = new Set(); let pools: any[] = []; - // ////// for test test test start todo - // const no_dcl_seeds:Seed[] = []; - // list_seeds.forEach((seed:Seed) => { - // const { seed_id } = seed; - // const contractId = seed_id.split('@')[0]; - // if (contractId == REF_UNI_V3_SWAP_CONTRACT_ID) return; - // no_dcl_seeds.push(seed); - // }) - // list_seeds = no_dcl_seeds; - // ////// for test test test end todo list_seeds.forEach((seed: Seed) => { const { seed_id } = seed; // seed type: [commonSeed, loveSeed, dclSeed] From 08c3bf8d23adb5c8edf446640e45372c3fb32434 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 19 Jun 2023 14:19:45 +0800 Subject: [PATCH 033/204] update --- src/components/farm/FarmsDclDetail.tsx | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 524d1efd4..cf4dfd7af 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -995,7 +995,7 @@ export default function FarmsDclDetail(props: { }); const [part_farm_liquidity] = listLiquidities_inFarimg.filter( (l: UserLiquidityInfo) => { - return Big(l.part_farm_ratio || 0).gt(0); + return Big(l.unfarm_part_amount || 0).gt(0); } ); if (part_farm_liquidity) { @@ -1043,7 +1043,9 @@ export default function FarmsDclDetail(props: { const { seed_id, min_deposit } = detailData; let total_v_liquidity = Big(0); let withdraw_amount = Big(0); - const liquidities: UserLiquidityInfo[] = listLiquidities_unFarimg; + const liquidities: UserLiquidityInfo[] = listLiquidities_unFarimg.concat( + [] + ); listLiquidities_unFarimg.forEach((l: UserLiquidityInfo) => { const v_liquidity = mint_liquidity(l, seed_id); total_v_liquidity = total_v_liquidity.plus(v_liquidity); From 15a9ddb24807535be0a7ff161508c7a4fd51e1b2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 19 Jun 2023 15:24:01 +0800 Subject: [PATCH 034/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 29 ++++++++++----------- 1 file changed, 14 insertions(+), 15 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 2244b2f81..861082699 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -44,15 +44,16 @@ export default function SwapLimitOrderChart() { const right_point = 600000; useEffect(() => { if (pool_id) { - if (market_loading) { - refresh(); - } else { - get_points_of_orders(); - get_pool_detail(); - set_switch_token('X'); - } + get_points_of_orders(); + get_pool_detail(); + set_switch_token('X'); + } + }, [pool_id]); + useEffect(() => { + if (pool_id && market_loading) { + refresh(); } - }, [pool_id, market_loading]); + }, [market_loading]); useEffect(() => { if (pool && orders) { process_orders(); @@ -391,13 +392,13 @@ export default function SwapLimitOrderChart() { className="p-3 pr-0 overflow-auto" style={{ maxHeight: `${limitOrderContainerHeight}px` }} > - {buy_list?.map((item: IOrderPointItem, index) => { + {sell_list?.map((item: IOrderPointItem, index) => { return (
- + {formatPrice(item.price)} @@ -415,9 +416,7 @@ export default function SwapLimitOrderChart() { })}
- - Market Pirce - + Market Pirce - {sell_list?.map((item: IOrderPointItem, index) => { + {buy_list?.map((item: IOrderPointItem, index) => { return (
- + {formatPrice(item.price)} From c7c10a955404f9b1aa8a2bda67c97cdd33fca9de Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 20 Jun 2023 00:15:17 +0800 Subject: [PATCH 035/204] update order chart --- src/components/swap/SwapLimitOrderChart.tsx | 218 +++++++++++++++----- 1 file changed, 167 insertions(+), 51 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 861082699..a99109750 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -230,11 +230,6 @@ export default function SwapLimitOrderChart() { set_sell_token_x_list(sell_token_x_list_reverse); set_buy_token_y_list(buy_token_y_list); set_sell_token_y_list(sell_token_y_list_reverse); - - console.log('buy_token_x_list', buy_token_x_list); - console.log('sell_token_x_list', sell_token_x_list_reverse); - console.log('buy_token_y_list', buy_token_y_list); - console.log('sell_token_y_list', sell_token_y_list_reverse); } function get_price_by_point(point: number) { const { token_x_metadata, token_y_metadata } = pool; @@ -279,14 +274,13 @@ export default function SwapLimitOrderChart() { return (
@@ -457,8 +451,24 @@ export default function SwapLimitOrderChart() { ); } function OrderChart() { - const { buy_list, sell_list, cur_pairs, cur_token_symbol } = - useContext(LimitOrderChartData); + const { + buy_list, + sell_list, + cur_pairs, + cur_token_symbol, + pool, + get_price_by_point, + switch_token, + }: { + buy_list: IOrderPointItem[]; + sell_list: IOrderPointItem[]; + cur_pairs: string; + cur_token_symbol: string; + pool: PoolInfo; + get_price_by_point: Function; + switch_token: ISwitchToken; + } = useContext(LimitOrderChartData); + const [foucsOrderPoint, setFoucsOrderPoint] = useState(); const [side, setSide] = useState(); // CONST start @@ -477,33 +487,8 @@ function OrderChart() { }, [buy_list, sell_list]); function drawChart() { clearChart(); - // 获取价格区间、数量区间 - const prices: string[] = []; - const amounts: string[] = []; - buy_list.concat(sell_list).forEach((item: IOrderPointItem) => { - prices.push(item.price); - amounts.push( - Big(item.accumulated_x_readable || item.accumulated_y_readable).toFixed( - 0 - ) - ); - }); - prices.sort((b, a) => { - return Big(b).minus(a).toNumber(); - }); - amounts.sort((b, a) => { - return Big(b).minus(a).toNumber(); - }); - let price_range: number[]; - if (prices.length == 1) { - const p = +prices[0]; - const minP = Big(p).mul(0.5).toNumber(); - const maxP = Big(p).mul(2).toNumber(); - price_range = [minP, maxP]; - } else { - price_range = [+prices[0], +prices[prices.length - 1]]; - } - const amount_range: number[] = [+amounts[amounts.length - 1], 0]; + const { price_range, amount_range, buy_list_new, sell_list_new } = + get_data_for_drawing(); // 创建一个横坐标轴 const scaleBottom = d3 .scaleLinear() @@ -568,7 +553,7 @@ function OrderChart() { /** 创建左侧区域 */ // 面积 if (buy_list?.length) { - const area_path_data_left = areaGenerator(buy_list); + const area_path_data_left = areaGenerator(buy_list_new as any); d3.select('.areaLeft') .append('path') .attr('opacity', '0.3') @@ -576,7 +561,7 @@ function OrderChart() { .attr('fill', 'url(#paint0_linear_7545_2924)'); // 渐变色 - const max_y = buy_list[buy_list.length - 1]; + const max_y = buy_list_new[buy_list_new.length - 1]; const y = +Big( scaleRight( +(max_y.accumulated_x_readable || max_y.accumulated_y_readable) @@ -587,7 +572,7 @@ function OrderChart() { .attr('y2', svg_height - svg_padding * 2); // 折线 - var line_path_data_left = lineGenerator(buy_list); + var line_path_data_left = lineGenerator(buy_list_new as any); d3.select('.areaLeft') .append('path') .attr('d', line_path_data_left) @@ -596,8 +581,8 @@ function OrderChart() { .attr('fill', 'none'); // 触发鼠标事件的矩形区域 - const buy_list_first = buy_list[0]; - const buy_list_last = buy_list[buy_list.length - 1]; + const buy_list_first = buy_list_new[0]; + const buy_list_last = buy_list_new[buy_list_new.length - 1]; d3.select('.rectLeft') .append('rect') .attr('width', () => { @@ -645,7 +630,7 @@ function OrderChart() { /** 创建右侧区域 */ // 面积 if (sell_list?.length) { - const area_path_data_right = areaGenerator(sell_list); + const area_path_data_right = areaGenerator(sell_list_new as any); d3.select('.areaRight') .append('path') .attr('opacity', '0.3') @@ -653,7 +638,7 @@ function OrderChart() { .attr('fill', 'url(#paint0_linear_7545_2926)'); // 渐变色 - const max_y = sell_list[0]; + const max_y = sell_list_new[0]; const y = +Big( scaleRight( +(max_y.accumulated_x_readable || max_y.accumulated_y_readable) @@ -664,7 +649,7 @@ function OrderChart() { .attr('y2', svg_height - svg_padding * 2); // 折线 - const line_path_data_right = lineGenerator(sell_list); + const line_path_data_right = lineGenerator(sell_list_new as any); d3.select('.areaRight') .append('path') .attr('d', line_path_data_right) @@ -672,8 +657,8 @@ function OrderChart() { .attr('strokeWidth', '2') .attr('fill', 'none'); // 触发鼠标事件的矩形区域 - const sell_list_first = sell_list[0]; - const sell_list_last = sell_list[sell_list.length - 1]; + const sell_list_first = sell_list_new[0]; + const sell_list_last = sell_list_new[sell_list_new.length - 1]; d3.select('.rectRight') .append('rect') .attr('width', () => { @@ -718,7 +703,138 @@ function OrderChart() { }); } } + function get_data_for_drawing() { + // 获取价格区间 + let min_price: any; + let max_price: any; + if (buy_list.length == 0) { + min_price = Big(sell_list[sell_list.length - 1].price || 0) + .mul(0.9) + .toFixed(); + } else if (buy_list.length == 1) { + min_price = Big(buy_list[0].price).mul(0.9).toFixed(); + } else { + min_price = Big(buy_list[buy_list.length - 1].price) + .mul(0.9) + .toFixed(); + } + if (sell_list.length == 0) { + max_price = Big(buy_list[0].price || 0) + .mul(1.1) + .toFixed(); + } else if (sell_list.length == 1) { + max_price = Big(sell_list[0].price).mul(1.1).toFixed(); + } else { + max_price = Big(sell_list[0].price).mul(1.1).toFixed(); + } + // 获取 数量区间 + const amounts: string[] = []; + buy_list.concat(sell_list).forEach((item: IOrderPointItem) => { + amounts.push( + Big(item.accumulated_x_readable || item.accumulated_y_readable).toFixed( + 0 + ) + ); + }); + amounts.sort((b, a) => { + return Big(b).minus(a).toNumber(); + }); + // 给绘制的图 添加辅助点 + const buy_list_new: IOrderPointItem[] = []; + if (buy_list.length == 1) { + const ele = buy_list[0]; + buy_list_new.push( + { + price: ele.price, + accumulated_x_readable: '0', + accumulated_y_readable: '0', + }, + ele, + { + price: min_price, + accumulated_x_readable: ele.accumulated_x_readable, + accumulated_y_readable: ele.accumulated_y_readable, + } + ); + } else { + buy_list.forEach((item: IOrderPointItem, index) => { + if (index == 0) { + buy_list_new.push({ + price: item.price, + accumulated_x_readable: '0', + accumulated_y_readable: '0', + }); + } + buy_list_new.push(item); + const nextItem = buy_list[index + 1]; + if (index < buy_list.length - 1) { + buy_list_new.push({ + price: nextItem.price, + accumulated_x_readable: item.accumulated_x_readable, + accumulated_y_readable: item.accumulated_y_readable, + }); + } + if (index == buy_list.length - 1) { + buy_list_new.push({ + price: min_price, + accumulated_x_readable: item.accumulated_x_readable, + accumulated_y_readable: item.accumulated_y_readable, + }); + } + }); + } + const sell_list_new: IOrderPointItem[] = []; + if (sell_list.length == 1) { + const ele = sell_list[0]; + sell_list_new.push( + { + price: max_price, + accumulated_x_readable: ele.accumulated_x_readable, + accumulated_y_readable: ele.accumulated_y_readable, + }, + ele, + { + price: ele.price, + accumulated_x_readable: '0', + accumulated_y_readable: '0', + } + ); + } else { + sell_list.forEach((item: IOrderPointItem, index) => { + if (index == 0) { + sell_list_new.push({ + price: max_price, + accumulated_x_readable: item.accumulated_x_readable, + accumulated_y_readable: item.accumulated_y_readable, + }); + } + if (index < sell_list.length - 1) { + const nextItem = sell_list[index + 1]; + sell_list_new.push(item, { + price: item.price, + accumulated_x_readable: nextItem.accumulated_x_readable, + accumulated_y_readable: nextItem.accumulated_y_readable, + }); + } + if (index == sell_list.length - 1) { + sell_list_new.push(item, { + price: item.price, + accumulated_x_readable: '0', + accumulated_y_readable: '0', + }); + } + }); + } + const price_range: number[] = [+min_price, +max_price]; + const amount_range: number[] = [+amounts[amounts.length - 1], 0]; + return { + price_range, + amount_range, + buy_list_new, + sell_list_new, + }; + } function clearChart() { d3.selectAll('.axisBottom *').remove(); d3.selectAll('.axisRight *').remove(); @@ -1077,9 +1193,9 @@ interface IOrderPoint { [point: string]: IOrderPointItem; } interface IOrderPointItem { - point: number; - amount_x: string; - amount_y: string; + point?: number; + amount_x?: string; + amount_y?: string; price?: string; amount_x_readable?: string; amount_y_readable?: string; From 2161461a1fdf6aedbf093e0ce8bb1b2e445320ee Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 26 Jun 2023 09:10:37 +0800 Subject: [PATCH 036/204] add liquidity --- src/components/d3Chart/DclChart.tsx | 95 +- src/components/d3Chart/config.tsx | 14 +- src/components/d3Chart/interfaces.tsx | 2 + src/pages/Orderly/components/Common/Icons.tsx | 86 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 3248 +++++++---------- .../poolsV3/AddYourLiquidityPageV3Copy.tsx | 2483 +++++++++++++ src/pages/poolsV3/interfaces.tsx | 19 + src/services/commonV3.ts | 50 + src/services/near.ts | 3 - src/services/swapV3.ts | 47 +- src/state/swapV3.ts | 35 +- tailwind.config.js | 1 + 12 files changed, 3982 insertions(+), 2101 deletions(-) create mode 100644 src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx create mode 100644 src/pages/poolsV3/interfaces.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 1e136e987..09fac6a86 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -54,10 +54,11 @@ export default function DclChart({ const [price_range, set_price_range] = useState(); const [chartDataList, setChartDataList] = useState(); const [binDetail, setBinDetail] = useState(); - const [dragLeftPoint, setDragLeftPoint] = useState(leftPoint); - const [dragRightPoint, setDragRightPoint] = useState(rightPoint); + const [dragLeftPoint, setDragLeftPoint] = useState(); + const [dragRightPoint, setDragRightPoint] = useState(); const [zoom, setZoom] = useState(); const [randomId, setRandomId] = useState('.' + createRandomString()); + const [drawChartDone, setDrawChartDone] = useState(false); /** constant start */ const appearanceConfig: IPoolChartConfig = config || {}; const dragBarWidth = 28; @@ -87,6 +88,8 @@ export default function DclChart({ useEffect(() => { if (pool_id) { get_pool_detail(pool_id); + leftPoint && setDragLeftPoint(leftPoint); + rightPoint && setDragRightPoint(rightPoint); } }, [pool_id]); useEffect(() => { @@ -97,10 +100,15 @@ export default function DclChart({ useEffect(() => { if (price_range && chartDataList) { drawChart(); + setDrawChartDone(true); } }, [price_range, chartDataList]); useEffect(() => { - if (isValid(dragLeftPoint) && !appearanceConfig.controlHidden) { + if ( + isValid(dragLeftPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { const scale = scaleAxis(); const newPoint = dragLeftPoint; const newPrice = get_price_by_point(newPoint); @@ -134,14 +142,22 @@ export default function DclChart({ .attr('width', W); setLeftPoint && setLeftPoint(newPoint); } - }, [dragLeftPoint, price_range]); + }, [dragLeftPoint, price_range, drawChartDone]); useEffect(() => { - if (isValid(leftPoint) && leftPoint !== dragLeftPoint) { + if ( + isValid(leftPoint) && + isValid(dragLeftPoint) && + leftPoint !== dragLeftPoint + ) { setDragLeftPoint(leftPoint); } }, [leftPoint]); useEffect(() => { - if (isValid(dragRightPoint) && !appearanceConfig.controlHidden) { + if ( + isValid(dragRightPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { const scale = scaleAxis(); const newPoint = dragRightPoint; const newPrice = get_price_by_point(newPoint); @@ -171,12 +187,28 @@ export default function DclChart({ d3.select(`${randomId} .overlap rect`).attr('width', W); setRightPoint && setRightPoint(newPoint); } - }, [dragRightPoint, price_range]); + }, [dragRightPoint, price_range, drawChartDone]); useEffect(() => { - if (isValid(rightPoint) && rightPoint !== dragRightPoint) { + if ( + isValid(rightPoint) && + isValid(dragRightPoint) && + rightPoint !== dragRightPoint + ) { setDragRightPoint(rightPoint); } }, [rightPoint]); + useEffect(() => { + if (config?.radiusMode && config?.targetPoint) { + // hide drag bar and show target price bar + draw_radius_mode_bar(); + d3.select('.leftBar').attr('style', 'display:none'); + d3.select('.rightBar').attr('style', 'display:none'); + } else { + d3.select('.leftBar').attr('style', ''); + d3.select('.rightBar').attr('style', ''); + d3.select('.radiusBar').attr('style', 'display:none'); + } + }, [config?.radiusMode, config?.targetPoint]); async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; @@ -662,8 +694,8 @@ export default function DclChart({ // 初始化左的位置 const price = get_current_price(); let price_l; - if (leftPoint) { - price_l = get_price_by_point(leftPoint); + if (dragLeftPoint) { + price_l = get_price_by_point(dragLeftPoint); } else { const price_l_temp = Big(1 - defaultPercent / 100) .mul(price) @@ -708,8 +740,8 @@ export default function DclChart({ // 初始化右的位置 const price = get_current_price(); let price_r; - if (rightPoint) { - price_r = get_price_by_point(rightPoint); + if (dragRightPoint) { + price_r = get_price_by_point(dragRightPoint); } else { const price_r_temp = Big(1 + defaultPercent / 100) .mul(price) @@ -746,6 +778,15 @@ export default function DclChart({ }); d3.select(`${randomId} .drag-right`).call(dragRight); } + function draw_radius_mode_bar() { + const scale: any = scaleAxis(); + const { targetPoint } = config; + const price = get_price_by_point(targetPoint); + const x = scale(price); + d3.select(`${randomId} .radiusBar`) + .attr('transform', `translate(${x}, -${axisHeight})`) + .attr('style', 'display:block'); + } function get_current_price() { return get_price_by_point(pool.current_point); } @@ -926,7 +967,7 @@ export default function DclChart({ } ${randomId.slice(1)}`} > {/* 控件按钮*/} -
+
{/* 左右坐标轴中间的重叠区域 */} - + + + {/* radius 模式下 target price 对应的柱子 */} + + + + + diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index f3bf2802a..ea4e2f2d6 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -52,9 +52,19 @@ export function get_custom_config_for_chart(): IChartConfig { export function get_default_config_for_chart(): IChartItemConfig { const env: string = process.env.NEAR_ENV; if (env == 'pub-testnet') { - return {}; + return { + bin: 20, + range: 10, + rangeGear: [100, 80, 60, 40, 20, 10], + colors: ['#707C84', '#2775CA'], + }; } else if (env == 'testnet') { - return {}; + return { + bin: 20, + range: 10, + rangeGear: [100, 80, 60, 40, 20, 10], + colors: ['#707C84', '#2775CA'], + }; } else { return { bin: 20, diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index b03005294..1bf05ff6c 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -50,4 +50,6 @@ export interface IPoolChartConfig { axisHidden?: boolean; currentBarHidden?: boolean; hoverBoxHidden?: boolean; + radiusMode?: boolean; + targetPoint?: number; } diff --git a/src/pages/Orderly/components/Common/Icons.tsx b/src/pages/Orderly/components/Common/Icons.tsx index 974339a9d..ca4d34c81 100644 --- a/src/pages/Orderly/components/Common/Icons.tsx +++ b/src/pages/Orderly/components/Common/Icons.tsx @@ -1982,31 +1982,51 @@ export function CurveShape() { export function BidAskShape() { return ( + + + + + + + + + + + + + + + + + + + + ); diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index c171cbc2e..4b207c6df 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1,4 +1,10 @@ -import React, { useState, useContext, useEffect, useRef } from 'react'; +import React, { + useState, + useContext, + useEffect, + useRef, + createContext, +} from 'react'; import { ReturnIcon, @@ -50,7 +56,6 @@ import { PoolInfo, get_pool_marketdepth, regularizedPoint, - AddLiquidityInfo, batch_add_liquidity, } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; @@ -64,14 +69,13 @@ import { POINTLEFTRANGE, POINTRIGHTRANGE, useAddAndRemoveUrlHandle, - drawChartData, - TOKEN_LIST_FOR_RATE, get_matched_seeds_for_dcl_pool, get_all_seeds, - displayNumberToAppropriateDecimals, get_pool_id, get_pool_name, openUrl, + getBinPointByPrice, + getBinPointByPoint, } from '../../services/commonV3'; import { formatWithCommas, @@ -85,7 +89,7 @@ import { ONLY_ZEROS, } from '~utils/numbers'; import { WalletContext } from '../../utils/wallets-integration'; -import _, { set } from 'lodash'; +import _, { forEach, set } from 'lodash'; import BigNumber from 'bignumber.js'; import { toRealSymbol } from '../../utils/token'; import ReactTooltip from 'react-tooltip'; @@ -108,43 +112,60 @@ import { SpotShape, BidAskShape, } from '../Orderly/components/Common/Icons'; -import { SLOT_NUMBER } from '~services/near'; import DclChart from '../../components/d3Chart/DclChart'; - -export type LiquidityShape = 'Spot' | 'Curve' | 'BidAsk'; - +import { + IAddLiquidityInfo, + IaddLiquidityInfoHelp, + LiquidityShape, + PriceRangeModeType, +} from './interfaces'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, +} from '../../components/d3Chart/config'; +import { + IChartItemConfig, + IChartConfig, +} from '../../components/d3Chart/interfaces'; +import { isInvalid } from '../../components/d3Chart/utils'; +const LiquidityProviderData = createContext(null); export default function AddYourLiquidityPageV3() { const [tokenX, setTokenX] = useState(null); const [tokenY, setTokenY] = useState(null); const [tokenXAmount, setTokenXAmount] = useState(''); const [tokenYAmount, setTokenYAmount] = useState(''); - const [listPool, setListPool] = useState([]); - - const [binNumber, setBinNumber] = useState(10); - const [curPointInBinBoundry, setCurPointInBinBoundry] = useState(false); + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + const [currentPoint, setCurrentPoint] = useState(); + const [onlyAddYToken, setOnlyAddYToken] = useState(false); + const [onlyAddXToken, setOnlyAddXToken] = useState(false); + const [invalidRange, setInvalidRange] = useState(false); + const [currentSelectedPool, setCurrentSelectedPool] = + useState(null); + const [listPool, setListPool] = useState([]); const [tokenPriceList, setTokenPriceList] = useState>({}); const [currentPools, setCurrentPools] = useState>(null); - const [onlyAddYToken, setOnlyAddYToken] = useState(false); // real - const [onlyAddXToken, setOnlyAddXToken] = useState(false); // real - const [invalidRange, setInvalidRange] = useState(false); // real const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - const [leftPoint, setLeftPoint] = useState(0); // real - const [rightPoint, setRightPoint] = useState(0); // real - const [currentPoint, setCurrentPoint] = useState(); // real - const [currentSelectedPool, setCurrentSelectedPool] = - useState(null); // real + const [feeBoxStatus, setFeeBoxStatus] = useState(true); const [buttonSort, setButtonSort] = useState(false); const [selectHover, setSelectHover] = useState(false); - const [topPairs, setTopPairs] = useState([]); + const [hoverFeeBox, setHoverFeeBox] = useState(false); + + // abandon const [seed_list, set_seed_list] = useState(); const [related_seeds, set_related_seeds] = useState([]); - const [hoverFeeBox, setHoverFeeBox] = useState(false); + // new + const [binNumber, setBinNumber] = useState(); const [liquidityShape, setLiquidityShape] = useState('Spot'); + const [curPointInBinBoundry, setCurPointInBinBoundry] = useState(false); + const [topPairs, setTopPairs] = useState([]); + const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); + const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); // callBack handle useAddAndRemoveUrlHandle(); @@ -159,10 +180,6 @@ export default function AddYourLiquidityPageV3() { const intl_select = intl.formatMessage({ id: 'select_s' }); const OPEN_CREATE_POOL_ENTRY = false; - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - useEffect(() => { getBoostTokenPrices().then(setTokenPriceList); get_list_pools(); @@ -225,7 +242,6 @@ export default function AddYourLiquidityPageV3() { getTopPairs(); } }, [listPool, tokenPriceList]); - // trigger useEffect(() => { if ( @@ -237,6 +253,18 @@ export default function AddYourLiquidityPageV3() { } }, [liquidityShape]); + // new + useEffect(() => { + // init + if (currentSelectedPool && tokenX && tokenY) { + const { current_point, point_delta } = currentSelectedPool; + const n = get_slot_number_in_a_bin(); + const bin_width = n * point_delta; + SET_SLOT_NUMBER(n); + SET_BIN_WIDTH(bin_width); + setCurrentPoint(current_point); + } + }, [currentSelectedPool, tokenX, tokenY]); async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -271,7 +299,6 @@ export default function AddYourLiquidityPageV3() { const top3 = list.slice(0, 3); setTopPairs(top3); } - if (!refTokens || !triTokens || !triTokenIds) return ; const allTokens = getAllTokens(refTokens, triTokens); async function get_seeds() { @@ -449,20 +476,20 @@ export default function AddYourLiquidityPageV3() { const { token_x, token_y } = currentSelectedPool; const sort = tokenX.id == token_x; setTokenXAmount(amount); - if (sort) { - if (!onlyAddXToken) { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); + /*if (sort) {*/ + if (!onlyAddXToken) { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); - if (liquidityShape === 'Spot') { - setTokenYAmount(amount_result); - } + if (liquidityShape === 'Spot') { + setTokenYAmount(amount_result); } - } else { + } + /*} else { if (!onlyAddYToken) { const amount_result = getTokenXAmountByCondition({ amount, @@ -475,25 +502,25 @@ export default function AddYourLiquidityPageV3() { setTokenYAmount(amount_result); } } - } + }*/ } function changeTokenYAmount(amount: string = '0') { const { token_x, token_y } = currentSelectedPool; const sort = tokenX.id == token_x; setTokenYAmount(amount); - if (sort) { - if (!onlyAddYToken) { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - if (liquidityShape === 'Spot') { - setTokenXAmount(amount_result); - } + /*if (sort) {*/ + if (!onlyAddYToken) { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + if (liquidityShape === 'Spot') { + setTokenXAmount(amount_result); } - } else { + } + /*} else { if (!onlyAddXToken) { const amount_result = getTokenYAmountByCondition({ amount, @@ -505,10 +532,9 @@ export default function AddYourLiquidityPageV3() { setTokenXAmount(amount_result); } } - } + }*/ } function getTokenYAmountByCondition({ - // real amount, leftPoint, rightPoint, @@ -549,7 +575,6 @@ export default function AddYourLiquidityPageV3() { } } function getTokenXAmountByCondition({ - // real amount, leftPoint, rightPoint, @@ -591,9 +616,6 @@ export default function AddYourLiquidityPageV3() { return X_result.toString(); } } - function switchFeeBoxStatus() { - setFeeBoxStatus(!feeBoxStatus); - } function pointChange({ leftPoint, rightPoint, @@ -607,7 +629,6 @@ export default function AddYourLiquidityPageV3() { const sort = tokenX.id == token_x; setLeftPoint(leftPoint); setRightPoint(rightPoint); - setCurrentPoint(currentPoint); setInvalidRange(false); setOnlyAddXToken(false); setOnlyAddYToken(false); @@ -678,17 +699,6 @@ export default function AddYourLiquidityPageV3() { } } } - function switchButtonSort() { - if (tokenX || tokenY) { - setTokenX(tokenY); - setTokenY(tokenX); - setTokenXAmount(tokenYAmount); - setTokenYAmount(tokenXAmount); - setTokenXBalanceFromNear(tokenYBalanceFromNear); - setTokenYBalanceFromNear(tokenXBalanceFromNear); - } - setButtonSort(!buttonSort); - } function displayTvl(tvl: any) { if (!tokenPriceList) { return '-'; @@ -700,33 +710,59 @@ export default function AddYourLiquidityPageV3() { return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; } } - function goPoolsPage() { - const poolId = currentSelectedPool?.pool_id; - if (poolId) { - const newPoolId = get_pool_name(poolId); - openUrl(`/poolV2/${newPoolId}`); - } else { - localStorage.setItem(REF_FI_POOL_ACTIVE_TAB, 'v2'); - openUrl('/pools'); - } + // start + function get_slot_number_in_a_bin() { + const pool_id = currentSelectedPool?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; } const tokenSort = tokenX?.id == currentSelectedPool?.token_x; const mobileDevice = isMobile(); + return ( - <> +
{/* 缩略图 */} {/* */} {/* 详情页图 */} {/* */} {/* 添加页图 */} - + {/* */} {/* 用户流动性图表*/} - + > */} {/* 删除流动性图表 从右侧部分删除 */} {/* */} {/* 删除流动性图表 从左侧部分删除 */} @@ -735,6 +771,7 @@ export default function AddYourLiquidityPageV3() { {/* */}
+ {/* mobile head */}
+ {/* pc head */}
{ @@ -767,27 +805,14 @@ export default function AddYourLiquidityPageV3() {
+ {/* content */}
- {/*
-
- -
- - - -
*/}
- {/* left area */} - - {/* right area */} {/* no Data */} {currentSelectedPool ? null : } {/* add pool part */} @@ -808,31 +833,11 @@ export default function AddYourLiquidityPageV3() { ) : null} {/* add Liquidity part */} + {/* left area */} {currentSelectedPool && currentSelectedPool.pool_id ? ( - + ) : null} - + {/* right area */}
{currentSelectedPool && currentSelectedPool.pool_id && ( - + )} -
- - ); -} - -function Slider({ - min, - max, - step, - values, - setValues, - invert, -}: { - min: number; - max: number; - step: number; - values: any; - setValues: any; - invert: boolean; -}) { - return ( - + ); } -function CreatePoolComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - buttonSort, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - buttonSort: boolean; -}) { - const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); - const [createPoolRate, setCreatePoolRate] = useState(''); - const [rateStatus, setRateStatus] = useState(true); +function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + binNumber, + SLOT_NUMBER, + leftPoint, + rightPoint, + currentPoint, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + } = useContext(LiquidityProviderData); + console.log('leftPoint', leftPoint); + console.log('rightPoint', rightPoint); + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; - useEffect(() => { - if (createPoolRate) { - const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); - setCreatePoolRate(toPrecision(rateString, 6)); + const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; + const max_nft_divisional_per_side = 3; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const { pool_id } = currentSelectedPool; + add_liquidity({ + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + // amount_x: tokenSort + // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + // amount_y: tokenSort + // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + // token_x: tokenSort ? tokenX : tokenY, + // token_y: tokenSort ? tokenY : tokenX, + }); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); } - }, [buttonSort]); - function getCurrentPriceValue(token: TokenMetadata) { - if (token) { - const price = tokenPriceList[token.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * 把包含当前点位的那个bin单独拿出来作为一个nft处理,不参与做等差,因为参与做等差会导致 不成立。 + * 除去参与做等差的 x,y,剩余 x,y 作为一个nft,由于这个nft的liquidity太小了,不能添加,可以忽略。 + * */ + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + currentPoint, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + currentPoint, + 'ceil' + ); + const nftList_y = get_y_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + const nftList_x = get_x_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + /** 包含当前点位的bin start */ + const used_x_amount = nftList_x.reduce((acc, cur) => { + return acc.plus(cur.amount_x || '0'); + }, Big(0)); + const used_y_amount = nftList_y.reduce((acc, cur) => { + return acc.plus(cur.amount_y || '0'); + }, Big(0)); + const current_bin_nft: IAddLiquidityInfo = { + pool_id, + left_point: current_l_point, + right_point: current_r_point, + amount_x: Big(tokenXAmount_nonDivisible) + .minus(used_x_amount) + .toFixed(0), + amount_y: Big(tokenYAmount_nonDivisible) + .minus(used_y_amount) + .toFixed(0), + min_amount_x: '0', + min_amount_y: '0', + }; + /** 包含当前点位的bin end */ + nftList = nftList_x.concat(nftList_y); } + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: tokenXAmount_nonDivisible, + amount_y: tokenYAmount_nonDivisible, + }); } - function createPool() { - setCreatePoolButtonLoading(true); - const { fee } = currentSelectedPool; - const pointDelta = POINTDELTAMAP[fee]; - let decimalRate = - Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); - let init_point = getPointByPrice( - pointDelta, - createPoolRate, - decimalRate, - true + // 待删除项 + function addLiquidityCurve() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' ); - const arr = [tokenX.id, tokenY.id]; - arr.sort(); - if (arr[0] !== tokenX.id) { - decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - init_point = getPointByPrice( - pointDelta, - new BigNumber(1).dividedBy(createPoolRate).toFixed(), - decimalRate, - true + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + if (onlyAddYToken) { + nftList = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * 把包含当前点位的那个bin单独拿出来作为一个nft处理,不参与做等差,因为参与做等差会导致 不成立。 + * 除去参与做等差的 x,y,剩余 x,y 作为一个nft,由于这个nft的liquidity太小了,不能添加,可以忽略。 + * */ + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + currentPoint, + 'floor' ); - create_pool({ - token_a: tokenY.id, - token_b: tokenX.id, - fee: currentSelectedPool.fee, - init_point, + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + currentPoint, + 'ceil' + ); + const nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, }); - } else { - create_pool({ - token_a: tokenX.id, - token_b: tokenY.id, - fee: currentSelectedPool.fee, - init_point, + const nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, }); + /** 包含当前点位的bin start */ + const used_x_amount = nftList_x.reduce((acc, cur) => { + return acc.plus(cur.amount_x || '0'); + }, Big(0)); + const used_y_amount = nftList_y.reduce((acc, cur) => { + return acc.plus(cur.amount_y || '0'); + }, Big(0)); + const current_bin_nft: IAddLiquidityInfo = { + pool_id, + left_point: current_l_point, + right_point: current_r_point, + amount_x: Big(tokenXAmount_nonDivisible) + .minus(used_x_amount) + .toFixed(0), + amount_y: Big(tokenYAmount_nonDivisible) + .minus(used_y_amount) + .toFixed(0), + min_amount_x: '0', + min_amount_y: '0', + }; + /** 包含当前点位的bin end */ + nftList = nftList_x.concat(nftList_y); } + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: tokenXAmount_nonDivisible, + amount_y: tokenYAmount_nonDivisible, + }); } - function switchRate() { - setRateStatus(!rateStatus); - } - function getPoolRate() { - if (createPoolRate) { - const rate = 1 / +createPoolRate; - return toPrecision(rate.toString(), 6); + function addLiquidityBidAsk() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + if (onlyAddYToken) { + nftList = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); } - return ''; - } - const mobileDevice = isMobile(); - return ( -
-
- - : -
-
- -
-
-

- -

-
-
-

- -

-
- - 1 {toRealSymbol(tokenX?.symbol)} = - -
- { - setCreatePoolRate(target.value); - }} - /> - - {toRealSymbol(tokenY?.symbol)} - -
-
-
- - - -
- {rateStatus ? ( -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue(tokenX)}) - - - - {createPoolRate} {toRealSymbol(tokenY?.symbol)} - -
- ) : ( -
- 1 {toRealSymbol(tokenY?.symbol)} - - ({getCurrentPriceValue(tokenY)}) - - - - {getPoolRate()} {toRealSymbol(tokenX?.symbol)} - -
- )} - - -
-
-
-
-
- {isSignedIn ? ( - - ( - <> - - - )} - /> - - ) : ( - - )} -
- ); -} - -export type PriceRangeModeType = 'by_range' | 'by_radius'; - -function AddLiquidityComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - pointChange, - onlyAddXToken, - onlyAddYToken, - invalidRange, - seeds, - priceRangeMode, - setPriceRangeMode, - binNumber, - setBinNumber, - curPointInBinBoundry, - setCurPointInBinBoundry, - setCurrentSelectedPool, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - tokenXAmount: string; - tokenYAmount: string; - tokenXBalanceFromNear: string; - tokenYBalanceFromNear: string; - pointChange: any; - onlyAddXToken: boolean; - onlyAddYToken: boolean; - invalidRange: boolean; - seeds: Seed[]; - priceRangeMode: PriceRangeModeType; - setPriceRangeMode: (mode: PriceRangeModeType) => void; - binNumber: number; - setBinNumber: (binNumber: number) => void; - curPointInBinBoundry: boolean; - setCurPointInBinBoundry: (curPointInBinBoundry: boolean) => void; - setCurrentSelectedPool: (pool: PoolInfo) => void; -}) { - let [leftCustomPrice, setLeftCustomPrice] = useState(''); - let [rightCustomPrice, setRightCustomPrice] = useState(''); - let [targetCustomPrice, setTargetCustomPrice] = useState(''); - - let [leftPoint, setLeftPoint] = useState(0); - let [rightPoint, setRightPoint] = useState(0); - - let [targetPoint, setTargetPoint] = useState(0); - - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [targetInputStatus, setTargetInputStatus] = useState(false); - - const [currentPoint, setCurrentPoint] = useState(); - - const [chartLoading, setChartLoading] = useState(false); - const [noDataForChart, setNoDataForChart] = useState(false); - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - - const [radius, setRadius] = useState(5); - - useEffect(() => { - if (!currentSelectedPool || !targetPoint) return; - if (priceRangeMode === 'by_range') { - setCurrentSelectedPool({ - ...currentSelectedPool, - current_point: currentPoint, - }); - } else { - setCurrentSelectedPool({ - ...currentSelectedPool, - current_point: targetPoint, - }); - } - setCurrentPoint(targetPoint); - }, [priceRangeMode, targetPoint]); - console.log('currentSelectedPool: ', currentSelectedPool); - - useEffect(() => { - const { point_delta } = currentSelectedPool; - - if (tokenSort) { - setRightPoint(leftPoint + point_delta * SLOT_NUMBER * binNumber); - } else { - setLeftPoint(rightPoint - point_delta * SLOT_NUMBER * binNumber); - } - }, [binNumber]); - - const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< - number | string - >(); - const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); - const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< - Record - >({}); - - const { globalState } = useContext(WalletContext); - const [timer, setTimer] = useState(null); - const [depthData, setDepthData] = useState(null); - - const [displayedSeedIndex, setDisplayedSeedIndex] = useState(0); - const isSignedIn = globalState.isSignedIn; - const intl = useIntl(); - const chartDom = useRef(null); - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - useEffect(() => { - if (priceRangeMode === 'by_radius') { - setBinNumber(radius * 2); - } - - if (!quickOptionsMapPoint['full']?.['left_p']) return; - - // get curpoint in bin boundry - - const left_b = quickOptionsMapPoint['full']['left_p']; - - const { current_point, point_delta } = currentSelectedPool; - - const point_diff = - Math.floor((current_point - left_b) / (point_delta * SLOT_NUMBER)) * - (point_delta * SLOT_NUMBER); - - const temp_cur_point = left_b + point_diff; - - const new_left_point = temp_cur_point - point_delta * SLOT_NUMBER * radius; - - const new_right_point = temp_cur_point + point_delta * SLOT_NUMBER * radius; - - setLeftPoint(new_left_point); - setRightPoint(new_right_point); - }, [radius, priceRangeMode, quickOptionsMapPoint['full']?.['left_p']]); - - // init - - // quick options - useEffect(() => { - const { current_point, point_delta, fee } = currentSelectedPool; - // 5, 10 20 50 - - const BIN_SIZE = point_delta * SLOT_NUMBER; - - const optionsMapPoints_temp = {}; - quickOptions.forEach((p: number) => { - const { left_p, right_p } = getPointByCondition(p); - optionsMapPoints_temp[p] = { left_p, right_p }; - // if (p == 10) { - // leftPoint = left_p; - // rightPoint = right_p; - // setLeftPoint(left_p); - // setRightPoint(right_p); - // } - }); - // full - const l_p_temp = _.max([current_point - 400000, -800000]); - const r_p_temp = _.min([current_point + 400000, 800000]); - let l_p = Math.floor(l_p_temp / point_delta) * point_delta; - let r_p = Math.floor(r_p_temp / point_delta) * point_delta; - if (r_p - l_p >= POINTRIGHTRANGE) { - l_p = l_p + point_delta; - } - - r_p = Math.floor((r_p - l_p) / BIN_SIZE) * BIN_SIZE + l_p; - - optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; - - let { left_p, right_p } = optionsMapPoints_temp['10']; - - left_p = Math.floor((left_p - l_p) / BIN_SIZE) * BIN_SIZE + l_p; - - right_p = left_p + BIN_SIZE * binNumber; - - setCurPointInBinBoundry((current_point - l_p) / BIN_SIZE === 0); - - setLeftPoint(left_p); - setRightPoint(right_p); - - // set - setCurrentPoint(current_point); - - setTargetPoint(current_point); - setQuickOptionsMapPoint(optionsMapPoints_temp); - setCurrentCheckedQuickOption(10); - // show chart data - setChartLoading(true); - setNoDataForChart(false); - clearTimeout(timer); - const timer_latest = setTimeout(() => { - getChartData(); - }, 1000); - setTimer(timer_latest); - }, [currentSelectedPool]); - console.log('currentSelectedPool: ', currentSelectedPool); - useEffect(() => { - pointChange({ leftPoint, rightPoint, currentPoint }); - const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { - const { left_p, right_p } = quickOptionsMapPoint[key]; - if (left_p == leftPoint && right_p == rightPoint) { - return key; - } - }); - if (targetKey) { - setCurrentCheckedQuickOption(targetKey); - } else { - setCurrentCheckedQuickOption(undefined); - } - if (depthData) { - drawChartData({ - depthData, + if (onlyAddXToken) { + nftList = get_rise_pattern_nfts({ left_point: leftPoint, right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, }); } - }, [leftPoint, rightPoint]); - async function getChartData() { - const depthData = await get_pool_marketdepth(currentSelectedPool.pool_id); - const length = drawChartData({ - depthData, - left_point: leftPoint, - right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, - }); - if (length == 0) { - setNoDataForChart(true); - } else { - setDepthData(depthData); - } - setChartLoading(false); - } - function getPointByCondition(p: number) { - const { point_delta, token_x } = currentSelectedPool; - const c_price = getCurrentPrice_real_decimal(); - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const tokenSort = tokenX.id == token_x ? true : false; - const reduce_p = 1 - p / 100; - const add_p = 1 + p / 100; - if (tokenSort) { - // -10% example - const l_price = new BigNumber(c_price).multipliedBy(reduce_p).toFixed(); - const point_l = getPointByPrice( - point_delta, - l_price.toString(), - decimalRate - ); - // +10% example - const r_price = new BigNumber(c_price).multipliedBy(add_p).toFixed(); - const point_r = getPointByPrice( - point_delta, - r_price.toString(), - decimalRate - ); - return { left_p: point_l, right_p: point_r }; - } else { - const c_price2 = new BigNumber(1).dividedBy(c_price).toFixed(); - // +10% example - const priceAdd = new BigNumber(c_price2).multipliedBy(add_p); - const l_price_2 = new BigNumber(1).dividedBy(priceAdd).toFixed(); - const point_l_2 = getPointByPrice( + if (!onlyAddXToken && !onlyAddYToken) { + /** + * 把包含当前点位的那个bin单独拿出来作为一个nft处理,不参与做等差,因为参与做等差会导致 不成立。 + * 除去参与做等差的 x,y,剩余 x,y 作为一个nft,由于这个nft的liquidity太小了,不能添加,可以忽略。 + * */ + const current_l_point = getBinPointByPoint( point_delta, - l_price_2.toString(), - decimalRate + SLOT_NUMBER, + currentPoint, + 'floor' ); - // -10% example - const priceDivide = new BigNumber(c_price2).multipliedBy(reduce_p); - const r_price_2 = new BigNumber(1).dividedBy(priceDivide).toFixed(); - const point_r_2 = getPointByPrice( + const current_r_point = getBinPointByPoint( point_delta, - r_price_2.toString(), - decimalRate + SLOT_NUMBER, + currentPoint, + 'ceil' ); - return { left_p: point_l_2, right_p: point_r_2 }; + const nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + const nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + /** 包含当前点位的bin start */ + const used_x_amount = nftList_x.reduce((acc, cur) => { + return acc.plus(cur.amount_x || '0'); + }, Big(0)); + const used_y_amount = nftList_y.reduce((acc, cur) => { + return acc.plus(cur.amount_y || '0'); + }, Big(0)); + const current_bin_nft: IAddLiquidityInfo = { + pool_id, + left_point: current_l_point, + right_point: current_r_point, + amount_x: Big(tokenXAmount_nonDivisible) + .minus(used_x_amount) + .toFixed(0), + amount_y: Big(tokenYAmount_nonDivisible) + .minus(used_y_amount) + .toFixed(0), + min_amount_x: '0', + min_amount_y: '0', + }; + /** 包含当前点位的bin end */ + nftList = nftList_x.concat(nftList_y); } + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: tokenXAmount_nonDivisible, + amount_y: tokenYAmount_nonDivisible, + }); } - function getCurrentPrice() { - let price = getCurrentPrice_real_decimal(); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } - if (price) { - return toPrecision(price.toString(), 8); + + function get_decline_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐下降模式 + * nft 从右往左计算 + * e.g. + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; } else { - return '-'; + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + let left_i_point; + let right_i_point; + if (i == total_nft_number - 1) { + left_i_point = left_point; + } else { + left_i_point = right_point - nftWidth * (i + 1); + } + right_i_point = right_point - nftWidth * i; + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; } - } - function getCurrentPriceValue() { - if (tokenX) { - const price = tokenPriceList[tokenX.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } } + return addLiquidityInfoList; } - function getCurrentPrice_real_decimal() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { current_point, token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(current_point, decimalRate); - return price; - } - return 0; - } - function addOneSlot(direction: string) { - const { point_delta } = currentSelectedPool; - const l_p = leftPoint + point_delta; - const r_p = rightPoint + point_delta; - if (direction == 'l') { - setLeftPoint(Math.max(Math.min(POINTRIGHTRANGE, l_p), POINTLEFTRANGE)); - } else if (direction == 'r') { - const target_slot_r = Math.max( - Math.min(POINTRIGHTRANGE, r_p), - POINTLEFTRANGE + function get_rise_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side ); - if (target_slot_r - leftPoint >= POINTRIGHTRANGE) return; - setRightPoint(target_slot_r); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + const left_i_point = left_point + nftWidth * i; + let right_i_point; + if (i == total_nft_number - 1) { + right_i_point = right_point; + } else { + right_i_point = left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; } - } - function reduceOneSlot(direction: string) { - const { point_delta } = currentSelectedPool; - const l_p = leftPoint - point_delta; - const r_p = rightPoint - point_delta; - if (direction == 'l') { - const target_slot_l = Math.max( - Math.min(POINTRIGHTRANGE, l_p), - POINTLEFTRANGE - ); - if (rightPoint - target_slot_l >= POINTRIGHTRANGE) return; - setLeftPoint(target_slot_l); - } else if (direction == 'r') { - setRightPoint(Math.max(Math.min(POINTRIGHTRANGE, r_p), POINTLEFTRANGE)); + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } } + return addLiquidityInfoList; } - function handlePriceToAppropriatePoint() { - const { point_delta, token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - if (leftCustomPrice) { - if (!tokenSort) { - leftCustomPrice = new BigNumber(1).dividedBy(leftCustomPrice).toFixed(); - } - const c_point = getPointByPrice( - point_delta, - leftCustomPrice, - decimalRate + + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + function getButtonText() { + let txt: any = ( + + ); + if (invalidRange) { + txt = ( + ); - setLeftCustomPrice(''); - setLeftPoint(c_point); - if (rightPoint - c_point >= POINTRIGHTRANGE) { - const appropriate_r_point = POINTRIGHTRANGE + c_point - point_delta; - setRightPoint(appropriate_r_point); - } - } - if (targetCustomPrice) { - if (!tokenSort) { - targetCustomPrice = new BigNumber(1) - .dividedBy(targetCustomPrice) - .toFixed(); - } - const c_point = getPointByPrice( - point_delta, - targetCustomPrice, - decimalRate + } else if ( + (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || + (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) + ) { + txt = ( + ); - setTargetCustomPrice(''); - setTargetPoint(c_point); - // if (c_point - leftPoint >= POINTRIGHTRANGE ) { - // const appropriate_l_point = c_point - POINTRIGHTRANGE + point_delta; - // setLeftPoint(appropriate_l_point); - // } - } - - if (rightCustomPrice) { - if (!tokenSort) { - rightCustomPrice = new BigNumber(1) - .dividedBy(rightCustomPrice) - .toFixed(); - } - const c_point = getPointByPrice( - point_delta, - rightCustomPrice, - decimalRate + } else if ( + (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || + (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + !onlyAddXToken && + !onlyAddYToken && + (+tokenXAmount == 0 || +tokenYAmount == 0) + ) { + txt = ( + + ); + } else if ( + +tokenXAmount > 0 && + new BigNumber(tokenXAmount).isGreaterThan( + getMax(tokenX, tokenXBalanceFromNear) + ) + ) { + txt = ( + + ); + } else if ( + +tokenYAmount > 0 && + new BigNumber(tokenYAmount).isGreaterThan( + getMax(tokenY, tokenYBalanceFromNear) + ) + ) { + txt = ( + ); - setRightCustomPrice(''); - setRightPoint(c_point); - if (c_point - leftPoint >= POINTRIGHTRANGE) { - const appropriate_l_point = c_point - POINTRIGHTRANGE + point_delta; - setLeftPoint(appropriate_l_point); - } } + return txt; } - function getButtonStatus() { const condition1 = currentSelectedPool?.pool_id; let condition2; @@ -1833,12 +1813,122 @@ function AddLiquidityComponent({ } return !(condition1 && condition2); } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); + const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquiditySpot + : addLiquidityForCurveAndBidAskMode; + + return ( +
+ {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} +
+ ); +} +function SetPointsComponent() { + const { + binNumber, + setBinNumber, + currentSelectedPool, + tokenX, + tokenY, + + pointChange, + currentPoint, + + leftPoint, + setLeftPoint, + rightPoint, + setRightPoint, + + SLOT_NUMBER, + BIN_WIDTH, + } = useContext(LiquidityProviderData); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const [radius, setRadius] = useState(); + + const [targetCustomPrice, setTargetCustomPrice] = useState(''); + const [leftCustomPrice, setLeftCustomPrice] = useState(''); + const [rightCustomPrice, setRightCustomPrice] = useState(''); + const [targetPoint, setTargetPoint] = useState(); + + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + const tokenSort = tokenX.id == currentSelectedPool.token_x; + // init + useEffect(() => { + if (currentSelectedPool && tokenX && tokenY) { + setTargetPoint(currentSelectedPool.current_point); + setRadius(3); + setPriceRangeMode('by_range'); + } + }, [currentSelectedPool, tokenX, tokenY]); + // change event in radius mode + useEffect(() => { + if (radius && +radius > 0 && targetPoint && BIN_WIDTH) { + const left_point_temp = handlePointToAppropriatePoint( + targetPoint - (radius - 1) * BIN_WIDTH + ); + const right_point_temp = handlePointToAppropriatePoint( + targetPoint + (radius + 1) * BIN_WIDTH + ); + setLeftPoint(left_point_temp); + setRightPoint(right_point_temp); + } + }, [radius, targetPoint, BIN_WIDTH, priceRangeMode]); + + useEffect(() => { + const diff = rightPoint - leftPoint; + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect right area + pointChange({ leftPoint, rightPoint, currentPoint }); + }, [leftPoint, rightPoint]); + + function handlePointToAppropriatePoint(point: number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } + function handlePriceToAppropriatePoint(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; } function getLeftPrice() { if (currentSelectedPool && currentSelectedPool.pool_id) { @@ -1846,9 +1936,9 @@ function AddLiquidityComponent({ const decimalRate = Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); let price = getPriceByPoint(leftPoint, decimalRate); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -1864,9 +1954,9 @@ function AddLiquidityComponent({ const decimalRate = Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); let price = getPriceByPoint(rightPoint, decimalRate); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -1876,16 +1966,15 @@ function AddLiquidityComponent({ return ''; } } - function getTargetPrice() { if (currentSelectedPool && currentSelectedPool.pool_id) { const { token_x, token_y } = currentSelectedPool; const decimalRate = Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); let price = getPriceByPoint(targetPoint, decimalRate); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -1895,173 +1984,6 @@ function AddLiquidityComponent({ return ''; } } - - function addLiquidity() { - setAddLiquidityButtonLoading(true); - const { pool_id } = currentSelectedPool; - add_liquidity({ - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: tokenSort - ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - amount_y: tokenSort - ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - token_x: tokenSort ? tokenX : tokenY, - token_y: tokenSort ? tokenY : tokenX, - }); - } - - function quickChangePoint(item: string | number) { - if (currentCheckedQuickOption == item) return; - const { point_delta, current_point } = currentSelectedPool; - const decimalRateTurn = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - if (item == 'full') { - const l_p_temp = _.max([current_point - 400000, -800000]); - const r_p_temp = _.min([current_point + 400000, 800000]); - let l_p = Math.floor(l_p_temp / point_delta) * point_delta; - let r_p = Math.floor(r_p_temp / point_delta) * point_delta; - if (r_p - l_p >= POINTRIGHTRANGE) { - l_p = l_p + point_delta; - } - setLeftPoint(l_p); - setRightPoint(r_p); - } else { - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price_c = getPriceByPoint(currentPoint, decimalRate); - if (tokenSort) { - const price_l_new = new BigNumber(1 - +item / 100) - .multipliedBy(price_c) - .toFixed(); - const price_r_new = new BigNumber(1 + +item / 100) - .multipliedBy(price_c) - .toFixed(); - const l_point = getPointByPrice( - point_delta, - price_l_new, - decimalRateTurn - ); - const r_point = getPointByPrice( - point_delta, - price_r_new, - decimalRateTurn - ); - setLeftPoint(l_point); - setRightPoint(r_point); - } else { - const price_c_2 = new BigNumber(1).dividedBy(price_c).toFixed(); - const price_l_new_2 = new BigNumber(1 - +item / 100) - .multipliedBy(price_c_2) - .toFixed(); - const price_r_new_2 = new BigNumber(1 + +item / 100) - .multipliedBy(price_c_2) - .toFixed(); - const price_l_new = new BigNumber(1).dividedBy(price_r_new_2).toFixed(); - const price_r_new = new BigNumber(1).dividedBy(price_l_new_2).toFixed(); - const l_point = getPointByPrice( - point_delta, - price_l_new, - decimalRateTurn - ); - const r_point = getPointByPrice( - point_delta, - price_r_new, - decimalRateTurn - ); - setLeftPoint(l_point); - setRightPoint(r_point); - } - } - } - - function get_related_seeds() { - const temp_seeds = seeds.map((seed: Seed) => { - const [contractId, temp_mft_id] = seed.seed_id.split('@'); - const [fixRange, pool_id, left_point, right_point] = - temp_mft_id.split('&'); - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price_left = getPriceByPoint(+left_point, decimalRate); - const price_right = getPriceByPoint(+right_point, decimalRate); - return { - seed, - price_left, - price_right, - }; - }); - const tokenSort = tokenX.id == token_x ? true : false; - const targetSeed = temp_seeds[displayedSeedIndex]; - const { price_left, price_right } = targetSeed; - let price_left_final = price_left; - let price_right_final = price_right; - - if (!tokenSort) { - price_left_final = new BigNumber(1).dividedBy(price_right).toFixed(); - price_right_final = new BigNumber(1).dividedBy(price_left).toFixed(); - } - const display_price_left = displayNumberToAppropriateDecimals( - price_left_final.toString() - ); - const display_price_right = displayNumberToAppropriateDecimals( - price_right_final.toString() - ); - return ( -
- - 1 {tokenX.symbol} ={' '} - -
- { - setDisplayedSeedIndex(displayedSeedIndex - 1); - }} - className={`transform rotate-90 mr-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex > 0 ? '' : 'hidden' - }`} - > - { - if (!tokenSort) { - leftCustomPrice = price_right_final; - rightCustomPrice = price_left_final; - } else { - leftCustomPrice = price_left_final; - rightCustomPrice = price_right_final; - } - setLeftCustomPrice(leftCustomPrice); - setRightCustomPrice(rightCustomPrice); - handlePriceToAppropriatePoint(); - }} - > - {display_price_left} ~ {display_price_right} - - {tokenY.symbol} - { - setDisplayedSeedIndex(displayedSeedIndex + 1); - }} - className={`transform -rotate-90 ml-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex < seeds.length - 1 - ? '' - : 'hidden' - }`} - > -
-
- ); - } - function rewardRangeTip() { - const tip = intl.formatMessage({ id: 'reward_range_tip' }); - let result: string = `
${tip}
`; - return result; - } - const tokenSort = tokenX.id == currentSelectedPool.token_x; - function getPair() { if (tokenSort) { return `(${tokenX.symbol}/${tokenY.symbol})`; @@ -2069,12 +1991,54 @@ function AddLiquidityComponent({ return `(${tokenY.symbol}/${tokenX.symbol})`; } } - + function changebinNunber(v: string) { + if (!isInvalid(v)) { + const right_point_temp = leftPoint + +v * BIN_WIDTH; + setRightPoint(right_point_temp); + setBinNumber(v); + } + } + console.log('leftPoint', leftPoint); + console.log('rightPoint', rightPoint); return (
+ {/* chart area */} +
+
+ + Liquidity + + + Yours + +
+ {leftPoint && rightPoint && ( + + )} +
+ {/* set price range area */}
+ {/* price range mode area */}
+ {/* content */} +
+ {/* target price input box */} +
+ + + + +
-
-
-
- {/* target price */} - -
- {priceRangeMode === 'by_range' && - quickOptionsMapPoint['full']?.['left_p'] && - quickOptionsMapPoint['full']?.['right_p'] && ( - { - setLeftPoint(values[0]); - setRightPoint(values[1]); + {/* radius input box */} +
+ + + + { + setRadius(value); + }} + /> +
- setBinNumber( - (values[1] - values[0]) / - (currentSelectedPool.point_delta * SLOT_NUMBER) - ); - }} - >
- )} -
- -
- - - - {/* {tokenSort ? ( */} - -
- - {/* radius */} -
- - - - { - setRadius(value); - }} - /> -
- - {/* min price */} - -
- - - - {tokenSort ? ( - - ) : ( - { - addOneSlot('r'); - }} - addOneSlot={() => { - reduceOneSlot('r'); - }} - disbaled={priceRangeMode === 'by_radius'} - handlePriceToAppropriatePoint={ - handlePriceToAppropriatePoint - } - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - )} -
- - {/* max price */} -
- - - - {tokenSort ? ( - { - reduceOneSlot('r'); - }} - disbaled={priceRangeMode === 'by_radius'} - addOneSlot={() => { - addOneSlot('r'); - }} - handlePriceToAppropriatePoint={ - handlePriceToAppropriatePoint - } - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - ) : ( - { - addOneSlot('l'); - }} - addOneSlot={() => { - reduceOneSlot('l'); - }} - handlePriceToAppropriatePoint={ - handlePriceToAppropriatePoint - } - disbaled={priceRangeMode === 'by_radius'} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - > - )} -
+ {/* min price input box */} +
+ + + + {/* {tokenSort ? ( */} + + {/* ) : ( + + )} */} +
- {/* bin number */} -
- - - - { - setBinNumber(value); - }} - /> -
-
+ {/* max price input box */} +
+ + + + {/* {tokenSort ? ( */} + + {/* ) : ( + + )} */} +
- {seeds.length ? ( -
-
- -
- - -
-
- {get_related_seeds()} -
- ) : null} + {/* bin number input box */} +
+ + + + { + changebinNunber(value); + }} + />
- + {/* tip in foot */}
- - {/*
- -
- -
-
-
- -
- -
-
*/} - {/* {isSignedIn ? ( - - <>{getButtonText()}} - /> - - ) : ( - - )} */}
); } -function AddLiquidityButton({ +function Slider({ + min, + max, + step, + values, + setValues, + invert, +}: { + min: number; + max: number; + step: number; + values: any; + setValues: any; + invert: boolean; +}) { + return ( + + ); +} + +function CreatePoolComponent({ currentSelectedPool, tokenX, tokenY, tokenPriceList, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - pointChange, - onlyAddXToken, - onlyAddYToken, - invalidRange, - seeds, - priceRangeMode, - setPriceRangeMode, - binNumber, - setBinNumber, - curPointInBinBoundry, - setCurPointInBinBoundry, - getTokenYAmountByCondition, - getTokenXAmountByCondition, - liquidityShape, - setLiquidityShape, + buttonSort, }: { currentSelectedPool: PoolInfo; tokenX: TokenMetadata; tokenY: TokenMetadata; tokenPriceList: Record; - tokenXAmount: string; - tokenYAmount: string; - tokenXBalanceFromNear: string; - tokenYBalanceFromNear: string; - pointChange: any; - onlyAddXToken: boolean; - onlyAddYToken: boolean; - invalidRange: boolean; - seeds: Seed[]; - priceRangeMode: PriceRangeModeType; - setPriceRangeMode: (mode: PriceRangeModeType) => void; - binNumber: number; - setBinNumber: (binNumber: number) => void; - curPointInBinBoundry: boolean; - setCurPointInBinBoundry: (curPointInBinBoundry: boolean) => void; - getTokenYAmountByCondition: any; - getTokenXAmountByCondition: any; - liquidityShape: LiquidityShape; - setLiquidityShape: (liquidityShape: LiquidityShape) => void; + buttonSort: boolean; }) { - let [leftPoint, setLeftPoint] = useState(0); - let [rightPoint, setRightPoint] = useState(0); - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [currentPoint, setCurrentPoint] = useState(); - const [chartLoading, setChartLoading] = useState(false); - const [noDataForChart, setNoDataForChart] = useState(false); - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< - number | string - >(); - const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); - const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< - Record - >({}); + const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); + const [createPoolRate, setCreatePoolRate] = useState(''); + const [rateStatus, setRateStatus] = useState(true); const { globalState } = useContext(WalletContext); - const [timer, setTimer] = useState(null); - const [depthData, setDepthData] = useState(null); const isSignedIn = globalState.isSignedIn; - const intl = useIntl(); - const chartDom = useRef(null); - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - // init - useEffect(() => { - const { current_point, point_delta } = currentSelectedPool; - // 5, 10 20 50 - const optionsMapPoints_temp = {}; - quickOptions.forEach((p: number) => { - const { left_p, right_p } = getPointByCondition(p); - optionsMapPoints_temp[p] = { left_p, right_p }; - if (p == 10) { - leftPoint = left_p; - rightPoint = right_p; - setLeftPoint(left_p); - setRightPoint(right_p); - } - }); - // full - const l_p_temp = _.max([current_point - 400000, -800000]); - const r_p_temp = _.min([current_point + 400000, 800000]); - let l_p = Math.floor(l_p_temp / point_delta) * point_delta; - let r_p = Math.floor(r_p_temp / point_delta) * point_delta; - if (r_p - l_p >= POINTRIGHTRANGE) { - l_p = l_p + point_delta; - } - optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; - // set - setCurrentPoint(current_point); - setQuickOptionsMapPoint(optionsMapPoints_temp); - setCurrentCheckedQuickOption(10); - // show chart data - setChartLoading(true); - setNoDataForChart(false); - clearTimeout(timer); - const timer_latest = setTimeout(() => { - getChartData(); - }, 1000); - setTimer(timer_latest); - }, [currentSelectedPool]); useEffect(() => { - pointChange({ leftPoint, rightPoint, currentPoint }); - const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { - const { left_p, right_p } = quickOptionsMapPoint[key]; - if (left_p == leftPoint && right_p == rightPoint) { - return key; - } - }); - if (targetKey) { - setCurrentCheckedQuickOption(targetKey); - } else { - setCurrentCheckedQuickOption(undefined); - } - if (depthData) { - drawChartData({ - depthData, - left_point: leftPoint, - right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, - }); - } - }, [leftPoint, rightPoint]); - async function getChartData() { - const depthData = await get_pool_marketdepth(currentSelectedPool.pool_id); - const length = drawChartData({ - depthData, - left_point: leftPoint, - right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, - }); - if (length == 0) { - setNoDataForChart(true); - } else { - setDepthData(depthData); - } - setChartLoading(false); - } - function getPointByCondition(p: number) { - const { point_delta, token_x } = currentSelectedPool; - const c_price = getCurrentPrice_real_decimal(); - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const tokenSort = tokenX.id == token_x ? true : false; - const reduce_p = 1 - p / 100; - const add_p = 1 + p / 100; - if (tokenSort) { - // -10% example - const l_price = new BigNumber(c_price).multipliedBy(reduce_p).toFixed(); - const point_l = getPointByPrice( - point_delta, - l_price.toString(), - decimalRate - ); - // +10% example - const r_price = new BigNumber(c_price).multipliedBy(add_p).toFixed(); - const point_r = getPointByPrice( - point_delta, - r_price.toString(), - decimalRate - ); - return { left_p: point_l, right_p: point_r }; - } else { - const c_price2 = new BigNumber(1).dividedBy(c_price).toFixed(); - // +10% example - const priceAdd = new BigNumber(c_price2).multipliedBy(add_p); - const l_price_2 = new BigNumber(1).dividedBy(priceAdd).toFixed(); - const point_l_2 = getPointByPrice( - point_delta, - l_price_2.toString(), - decimalRate - ); - // -10% example - const priceDivide = new BigNumber(c_price2).multipliedBy(reduce_p); - const r_price_2 = new BigNumber(1).dividedBy(priceDivide).toFixed(); - const point_r_2 = getPointByPrice( - point_delta, - r_price_2.toString(), - decimalRate - ); - return { left_p: point_l_2, right_p: point_r_2 }; - } - } - - function getCurrentPrice_real_decimal() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { current_point, token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(current_point, decimalRate); - return price; - } - return 0; - } - - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - if (tokenSort) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } else { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - } else if (onlyAddYToken) { - if (tokenSort) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } else { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - return !(condition1 && condition2); - } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - } - - function addLiquidityCurve() { - const { pool_id, point_delta } = currentSelectedPool; - - const binSize = point_delta * SLOT_NUMBER; - - let liquidityInfo: AddLiquidityInfo[]; - - if (onlyAddYToken) { - // left side - const Y = tokenYAmount; - - const D = new Big(Y) - .mul(2) - .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) - .div(binSize); - - liquidityInfo = new Array(binNumber).map((item, index) => { - const height = new Big(index + 1).mul(D).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y, - }; - }); - } - - if (onlyAddXToken) { - const X = tokenSort ? tokenYAmount : tokenXAmount; - - const D = new Big(X) - .mul(2) - .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) - .div(binSize); - - liquidityInfo = new Array(binNumber).map((item, index) => { - const height = new Big(binNumber - index).mul(D).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }); - } - - if (!onlyAddXToken || !onlyAddYToken) { - if (curPointInBinBoundry) { - const leftBinSize = Math.floor(binNumber / 2); - const rightBinSize = binNumber - leftBinSize; - - const X_left = tokenYAmount; - - const Y_right = tokenXAmount; - - const D_left = new Big(X_left) - .mul(2) - .div(new Big(leftBinSize).mul(new Big(leftBinSize).plus(1))) - .div(binSize); - - const D_right = new Big(Y_right) - .mul(2) - .div(new Big(rightBinSize).mul(new Big(rightBinSize).plus(1))) - .div(binSize); - - liquidityInfo = new Array(leftBinSize).map((item, index) => { - const height = new Big(index + 1).mul(D_left).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y, - }; - }); - - liquidityInfo = liquidityInfo.concat( - new Array(rightBinSize).map((item, index) => { - const height = new Big(rightBinSize - index) - .mul(D_right) - .toFixed(0); - const left_p = leftPoint + binSize * (leftBinSize + index); - const right_p = leftPoint + binSize * (leftBinSize + index + 1); - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }) - ); - } else { - const leftBinSize = Math.floor(binNumber / 2) + 1; - const rightBinSize = binNumber - leftBinSize - 1; - - const D_left = new Big(tokenYAmount).div( - new Big( - new Big(((leftBinSize - 1) * leftBinSize) / 2).mul(binSize) - ).plus(new Big(leftBinSize).mul(currentPoint - leftPoint)) - ); - - if (leftBinSize - 1 > 0) { - const addLpList = new Array(leftBinSize - 1).map((item, index) => { - const height = new Big(index + 1).mul(D_left).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y: amount_y, - }; - }); - liquidityInfo = (liquidityInfo || []).concat(addLpList); - } - - // middle bin - - const tokenYMiddleAmount = new Big(currentPoint) - .minus(leftPoint + Number(leftBinSize - 1) * binSize) - .mul(D_left) - .mul(leftBinSize) - .toFixed(0); - - const tokenXMiddleAmount = getTokenXAmountByCondition({ - amount: tokenYMiddleAmount, - leftPoint: leftPoint + Number(leftBinSize - 1) * binSize, - rightPoint: leftPoint + leftBinSize * binSize, - currentPoint, - }); - - liquidityInfo = (liquidityInfo || []).concat([ - { - pool_id, - left_point: leftPoint + Number(leftBinSize - 1) * binSize, - right_point: leftPoint + leftBinSize * binSize, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXMiddleAmount), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYMiddleAmount), - }, - ]); - - // right side - - if (rightBinSize > 1) { - const newtokenXAmount = new Big(tokenXAmount) - .minus(tokenXMiddleAmount) - .toFixed(0); - - const D_right = new Big(newtokenXAmount) - .mul(2) - .div(new Big(((rightBinSize - 1) * rightBinSize) / 2).mul(binSize)); - - const addLpList = new Array(rightBinSize).map((item, index) => { - const height = new Big(rightBinSize - index) - .mul(D_right) - .toFixed(0); - const left_p = leftPoint + leftBinSize + index * binSize; - const right_p = left_p + binSize; - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }); - liquidityInfo = (liquidityInfo || []).concat(addLpList); - } - - // - } - } - - return batch_add_liquidity({ - liquidityInfo, - token_x: tokenX, - token_y: tokenY, - }); - } - - function addLiquidityBidAsk() { - const { pool_id, point_delta } = currentSelectedPool; - - const binSize = point_delta * SLOT_NUMBER; - - let liquidityInfo: AddLiquidityInfo[]; - - if (onlyAddYToken) { - // left side - const Y = tokenYAmount; - - const D = new Big(Y) - .mul(2) - .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) - .div(binSize); - - liquidityInfo = new Array(binNumber).map((item, index) => { - const height = new Big(binNumber - index).mul(D).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y, - }; - }); - } - - if (onlyAddXToken) { - const X = tokenSort ? tokenYAmount : tokenXAmount; - - const D = new Big(X) - .mul(2) - .div(new Big(binNumber).mul(new Big(binNumber).plus(1))) - .div(binSize); - - liquidityInfo = new Array(binNumber).map((item, index) => { - const height = new Big(index + 1).mul(D).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }); - } - - if (!onlyAddXToken || !onlyAddYToken) { - if (curPointInBinBoundry) { - const leftBinSize = Math.floor(binNumber / 2); - const rightBinSize = binNumber - leftBinSize; - - const X_left = tokenYAmount; - - const Y_right = tokenXAmount; - - const D_left = new Big(X_left) - .mul(2) - .div(new Big(leftBinSize).mul(new Big(leftBinSize).plus(1))) - .div(binSize); - - const D_right = new Big(Y_right) - .mul(2) - .div(new Big(rightBinSize).mul(new Big(rightBinSize).plus(1))) - .div(binSize); - - liquidityInfo = new Array(leftBinSize).map((item, index) => { - const height = new Big(leftBinSize - index).mul(D_left).toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y, - }; - }); - - liquidityInfo = liquidityInfo.concat( - new Array(rightBinSize).map((item, index) => { - const height = new Big(index + 1).mul(D_right).toFixed(0); - const left_p = leftPoint + binSize * (leftBinSize + index); - const right_p = leftPoint + binSize * (leftBinSize + index + 1); - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }) - ); - } else { - const leftBinSize = Math.floor(binNumber / 2) + 1; - const rightBinSize = binNumber - leftBinSize - 1; - - const D_left = new Big(tokenYAmount).div( - new Big( - new Big(((leftBinSize - 1) * leftBinSize) / 2).mul(binSize) - ).plus(new Big(leftBinSize).mul(currentPoint - leftPoint)) - ); - - if (leftBinSize - 1 > 0) { - const addLpList = new Array(leftBinSize - 1).map((item, index) => { - const height = new Big(leftBinSize - 1 - index) - .mul(D_left) - .toFixed(0); - const left_p = leftPoint + binSize * index; - const right_p = leftPoint + binSize * (index + 1); - - const amount_y = toNonDivisibleNumber( - tokenY.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x: '0', - amount_y: amount_y, - }; - }); - liquidityInfo = (liquidityInfo || []).concat(addLpList); - } - - // middle bin - - const tokenYMiddleAmount = new Big(currentPoint) - .minus(leftPoint + Number(leftBinSize - 1) * binSize) - .mul(D_left) - .toFixed(0); - - const tokenXMiddleAmount = getTokenXAmountByCondition({ - amount: tokenYMiddleAmount, - leftPoint: leftPoint + Number(leftBinSize - 1) * binSize, - rightPoint: leftPoint + leftBinSize * binSize, - currentPoint, - }); - - liquidityInfo = (liquidityInfo || []).concat([ - { - pool_id, - left_point: leftPoint + Number(leftBinSize - 1) * binSize, - right_point: leftPoint + leftBinSize * binSize, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXMiddleAmount), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYMiddleAmount), - }, - ]); - - // right side - - if (rightBinSize > 1) { - const newtokenXAmount = new Big(tokenXAmount) - .minus(tokenXMiddleAmount) - .toFixed(0); - - const D_right = new Big(newtokenXAmount) - .mul(2) - .div(new Big(((rightBinSize - 1) * rightBinSize) / 2).mul(binSize)); - - const addLpList = new Array(rightBinSize).map((item, index) => { - const height = new Big(index + 1).mul(D_right).toFixed(0); - const left_p = leftPoint + leftBinSize + index * binSize; - const right_p = left_p + binSize; - - const amount_x = toNonDivisibleNumber( - tokenX.decimals, - new Big(height).mul(binSize).toFixed() - ); - - return { - pool_id, - left_point: left_p, - right_point: right_p, - amount_x, - amount_y: '0', - }; - }); - liquidityInfo = (liquidityInfo || []).concat(addLpList); - } - - // - } + if (createPoolRate) { + const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); + setCreatePoolRate(toPrecision(rateString, 6)); + } + }, [buttonSort]); + function getCurrentPriceValue(token: TokenMetadata) { + if (token) { + const price = tokenPriceList[token.id]?.price; + return price ? `${'$' + price}` : '$-'; + } else { + return '$-'; } - - return batch_add_liquidity({ - liquidityInfo, - token_x: tokenX, - token_y: tokenY, - }); - } - - function addLiquidity() { - setAddLiquidityButtonLoading(true); - const { pool_id } = currentSelectedPool; - add_liquidity({ - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: tokenSort - ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - amount_y: tokenSort - ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - token_x: tokenSort ? tokenX : tokenY, - token_y: tokenSort ? tokenY : tokenX, - }); } - - function getButtonText() { - let txt: any = ( - + function createPool() { + setCreatePoolButtonLoading(true); + const { fee } = currentSelectedPool; + const pointDelta = POINTDELTAMAP[fee]; + let decimalRate = + Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); + let init_point = getPointByPrice( + pointDelta, + createPoolRate, + decimalRate, + true ); - if (invalidRange) { - txt = ( - - ); - } else if ( - (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || - (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || - (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - !onlyAddXToken && - !onlyAddYToken && - (+tokenXAmount == 0 || +tokenYAmount == 0) - ) { - txt = ( - - ); - } else if ( - +tokenXAmount > 0 && - new BigNumber(tokenXAmount).isGreaterThan( - getMax(tokenX, tokenXBalanceFromNear) - ) - ) { - txt = ( - - ); - } else if ( - +tokenYAmount > 0 && - new BigNumber(tokenYAmount).isGreaterThan( - getMax(tokenY, tokenYBalanceFromNear) - ) - ) { - txt = ( - + const arr = [tokenX.id, tokenY.id]; + arr.sort(); + if (arr[0] !== tokenX.id) { + decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + init_point = getPointByPrice( + pointDelta, + new BigNumber(1).dividedBy(createPoolRate).toFixed(), + decimalRate, + true ); + create_pool({ + token_a: tokenY.id, + token_b: tokenX.id, + fee: currentSelectedPool.fee, + init_point, + }); + } else { + create_pool({ + token_a: tokenX.id, + token_b: tokenY.id, + fee: currentSelectedPool.fee, + init_point, + }); } - return txt; } - - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const isAddLiquidityDisabled = getButtonStatus(); - - const add_lp_func = - liquidityShape === 'Spot' - ? addLiquidity - : liquidityShape === 'Curve' - ? addLiquidityCurve - : () => {}; - + function switchRate() { + setRateStatus(!rateStatus); + } + function getPoolRate() { + if (createPoolRate) { + const rate = 1 / +createPoolRate; + return toPrecision(rate.toString(), 6); + } + return ''; + } + const mobileDevice = isMobile(); return (
+
+ + : +
+
+ +
+
+

+ +

+
+
+

+ +

+
+ + 1 {toRealSymbol(tokenX?.symbol)} = + +
+ { + setCreatePoolRate(target.value); + }} + /> + + {toRealSymbol(tokenY?.symbol)} + +
+
+
+ + + +
+ {rateStatus ? ( +
+ 1 {toRealSymbol(tokenX?.symbol)} + + ({getCurrentPriceValue(tokenX)}) + + + + {createPoolRate} {toRealSymbol(tokenY?.symbol)} + +
+ ) : ( +
+ 1 {toRealSymbol(tokenY?.symbol)} + + ({getCurrentPriceValue(tokenY)}) + + + + {getPoolRate()} {toRealSymbol(tokenX?.symbol)} + +
+ )} + + +
+
+
+
+
{isSignedIn ? ( <>{getButtonText()}} + loading={createPoolButtonLoading} + Text={() => ( + <> + + + )} /> ) : ( - + )}
); @@ -3351,26 +2575,19 @@ function NoDataComponent(props: any) { ); } function PointInputComponent({ - reduceOneSlot, - addOneSlot, handlePriceToAppropriatePoint, customPrice, - getPrice, setCustomPrice, + + getPrice, + setPoint, + inputStatus, setInputStatus, disbaled, }: any) { return ( -
- {/*
{ - reduceOneSlot('r'); - }} - > - -
*/} +
{ - handlePriceToAppropriatePoint(); - setInputStatus(false); + if (customPrice) { + debugger; + const appropriate_point_temp = + handlePriceToAppropriatePoint(customPrice); + setInputStatus(false); + setPoint(appropriate_point_temp); + } }} disabled={disbaled} value={inputStatus ? customPrice : getPrice()} @@ -3390,14 +2612,6 @@ function PointInputComponent({ setCustomPrice(inputPrice); }} /> - {/*
{ - addOneSlot('r'); - }} - > - -
*/}
); } diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx new file mode 100644 index 000000000..e1a09d3f7 --- /dev/null +++ b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx @@ -0,0 +1,2483 @@ +import React, { useState, useContext, useEffect, useRef } from 'react'; +import { + ReturnIcon, + AddIcon, + SelectIcon, + BgIcon, + SwitchButton, + AddButton, + ReduceButton, + SwitchIcon, + BoxDarkBg, + SideIcon, + InvalidIcon, + WarningIcon, + EmptyIcon, + WarningMark, + SwitchLRButton, + SwitchArrowL, + SwitchArrowR, +} from '~components/icon/V3'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { useHistory } from 'react-router-dom'; +import { + GradientButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from '~components/button/Button'; +import SelectToken from '~components/forms/SelectToken'; +import { useTriTokens, useWhitelistTokens } from '../../state/token'; +import { useTriTokenIdsOnRef } from '../../services/aurora/aurora'; +import { + TokenMetadata, + ftGetBalance, + ftGetTokenMetadata, +} from '../../services/ft-contract'; +import { getTokenPriceList } from '../../services/indexer'; +import { getBoostTokenPrices, Seed, FarmBoost } from '../../services/farm'; +import { useTokenBalances, useDepositableBalance } from '../../state/token'; +import Loading from '~components/layout/Loading'; +import { + list_pools, + add_liquidity, + create_pool, + PoolInfo, + get_pool_marketdepth, +} from '../../services/swapV3'; +import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; +import { + getPriceByPoint, + getPointByPrice, + CONSTANT_D, + FEELIST, + POINTDELTAMAP, + DEFAULTSELECTEDFEE, + POINTLEFTRANGE, + POINTRIGHTRANGE, + useAddAndRemoveUrlHandle, + drawChartData, + TOKEN_LIST_FOR_RATE, + get_matched_seeds_for_dcl_pool, + get_all_seeds, + displayNumberToAppropriateDecimals, + get_pool_id, + get_pool_name, + openUrl, +} from '../../services/commonV3'; +import { + formatWithCommas, + toPrecision, + toReadableNumber, + toNonDivisibleNumber, + checkAllocations, + scientificNotationToString, + getAllocationsLeastOne, + toInternationalCurrencySystem, +} from '~utils/numbers'; +import { WalletContext } from '../../utils/wallets-integration'; +import _ from 'lodash'; +import BigNumber from 'bignumber.js'; +import { toRealSymbol } from '../../utils/token'; +import ReactTooltip from 'react-tooltip'; +import { getURLInfo } from '../../components/layout/transactionTipPopUp'; +import { BlueCircleLoading } from '../../components/layout/Loading'; +import { isMobile } from '../../utils/device'; +import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; +import { OutLinkIcon } from '../../components/icon/Common'; +import { REF_FI_POOL_ACTIVE_TAB } from '../pools/LiquidityPage'; +import getConfig from '../../services/config'; +import QuestionMark from '../../components/farm/QuestionMark'; +const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); + +import Big from 'big.js'; +import { SelectTokenDCL } from '../../components/forms/SelectToken'; + +export default function AddYourLiquidityPageV3() { + const [tokenX, setTokenX] = useState(null); + const [tokenY, setTokenY] = useState(null); + const [tokenXAmount, setTokenXAmount] = useState(''); + const [tokenYAmount, setTokenYAmount] = useState(''); + const [listPool, setListPool] = useState([]); + const [buttonHover, setButtonHover] = useState(false); + const [tokenPriceList, setTokenPriceList] = useState>({}); + const [currentPools, setCurrentPools] = + useState>(null); + const [onlyAddYToken, setOnlyAddYToken] = useState(false); // real + const [onlyAddXToken, setOnlyAddXToken] = useState(false); // real + const [invalidRange, setInvalidRange] = useState(false); // real + const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); + const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); + const [leftPoint, setLeftPoint] = useState(0); // real + const [rightPoint, setRightPoint] = useState(0); // real + const [currentPoint, setCurrentPoint] = useState(); // real + const [currentSelectedPool, setCurrentSelectedPool] = + useState(null); // real + const [feeBoxStatus, setFeeBoxStatus] = useState(true); + const [buttonSort, setButtonSort] = useState(false); + const [selectHover, setSelectHover] = useState(false); + const [viewPoolHover, setViewPoolHover] = useState(false); + const [topPairs, setTopPairs] = useState([]); + const [seed_list, set_seed_list] = useState(); + const [related_seeds, set_related_seeds] = useState([]); + // callBack handle + useAddAndRemoveUrlHandle(); + const history = useHistory(); + const triTokenIds = useTriTokenIdsOnRef(); + const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); + const triTokens = useTriTokens(); + const balances = useTokenBalances(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const nearBalance = useDepositableBalance('NEAR'); + const intl = useIntl(); + const intl_select = intl.formatMessage({ id: 'select_s' }); + const OPEN_CREATE_POOL_ENTRY = false; + useEffect(() => { + getBoostTokenPrices().then(setTokenPriceList); + get_list_pools(); + get_seeds(); + }, []); + useEffect(() => { + if (tokenX) { + const tokenXId = tokenX.id; + if (tokenXId) { + if (isSignedIn) { + ftGetBalance(tokenXId).then((available: string) => + setTokenXBalanceFromNear( + toReadableNumber( + tokenX.decimals, + tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + if (tokenY) { + const tokenYId = tokenY.id; + if (tokenYId) { + if (isSignedIn) { + ftGetBalance(tokenYId).then((available: string) => + setTokenYBalanceFromNear( + toReadableNumber( + tokenY.decimals, + tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + }, [tokenX, tokenY, isSignedIn, nearBalance]); + useEffect(() => { + if (listPool.length > 0) { + get_init_pool(); + } + }, [listPool]); + useEffect(() => { + if (currentSelectedPool && tokenX && tokenY) { + const { fee } = currentSelectedPool; + const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); + history.replace(`#${link}`); + if (seed_list && currentSelectedPool.pool_id) { + get_optional_seeds(); + } + } + }, [currentSelectedPool, tokenX, tokenY, seed_list]); + useEffect(() => { + if (tokenX && tokenY) { + searchPools(); + } + }, [tokenX, tokenY, tokenPriceList, listPool]); + useEffect(() => { + if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { + getTopPairs(); + } + }, [listPool, tokenPriceList]); + async function getTopPairs() { + const listPromise = listPool.map(async (p: PoolInfo) => { + const token_x = p.token_x; + const token_y = p.token_y; + + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged) + .toFixed(); + const tvlx = + Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * + Number(pricex); + const tvly = + Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * + Number(pricey); + + p.tvl = tvlx + tvly; + + return p; + }); + const list: PoolInfo[] = await Promise.all(listPromise); + list.sort((b: PoolInfo, a: PoolInfo) => { + return a.tvl - b.tvl; + }); + const top3 = list.slice(0, 3); + setTopPairs(top3); + } + + if (!refTokens || !triTokens || !triTokenIds) return ; + const allTokens = getAllTokens(refTokens, triTokens); + const nearSwapTokens = allTokens.filter((token) => token.onRef); + async function get_seeds() { + const seeds = await get_all_seeds(); + set_seed_list(seeds); + } + function get_optional_seeds() { + const optional_seeds = get_matched_seeds_for_dcl_pool({ + seeds: seed_list, + pool_id: currentSelectedPool.pool_id, + }); + if (optional_seeds.length) { + set_related_seeds(optional_seeds); + } else { + set_related_seeds([]); + } + } + async function get_init_pool() { + let tokenx_id, tokeny_id, pool_fee; + const hash = decodeURIComponent(location.hash); + if (hash.indexOf('<>') > -1) { + // new link + [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); + } else { + // old link + [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); + } + if (tokenx_id && tokeny_id && pool_fee) { + const tokenx = await ftGetTokenMetadata(tokenx_id); + const tokeny = await ftGetTokenMetadata(tokeny_id); + setTokenX(tokenx); + setTokenY(tokeny); + } + } + function goYourLiquidityPage() { + if (history.length == 2) { + history.push('/yourliquidity'); + } else { + history.goBack(); + } + } + async function get_list_pools() { + const list: PoolInfo[] = await list_pools(); + if (list.length > 0) { + setListPool(list); + } + } + function searchPools() { + const hash = decodeURIComponent(location.hash); + let url_fee; + if (hash.indexOf('<>') > -1) { + url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; + } else { + url_fee = +hash.slice(1).split('|')[2]; + } + const currentPoolsMap = {}; + if (listPool.length > 0 && tokenX && tokenY) { + const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { + // TODO 增加pool 状态的判断 + const { token_x, token_y, state } = pool; + if ( + (token_x == tokenX.id && token_y == tokenY.id) || + (token_x == tokenY.id && token_y == tokenX.id) + ) + return true; + }); + if (availablePools.length > 0) { + /*** percent start */ + const tvlList: number[] = []; + availablePools.map((p: PoolInfo) => { + const { + total_x, + total_y, + token_x, + token_y, + total_fee_x_charged, + total_fee_y_charged, + } = p; + const firstToken = tokenX.id == token_x ? tokenX : tokenY; + const secondToken = tokenY.id == token_y ? tokenY : tokenX; + const firstTokenPrice = + (tokenPriceList && + tokenPriceList[firstToken.id] && + tokenPriceList[firstToken.id].price) || + '0'; + const secondTokenPrice = + (tokenPriceList && + tokenPriceList[secondToken.id] && + tokenPriceList[secondToken.id].price) || + '0'; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged || 0) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged || 0) + .toFixed(); + const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) + .times(firstTokenPrice) + .toNumber(); + const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) + .times(secondTokenPrice) + .toNumber(); + const totalTvl = tvlx + tvly; + p.tvl = totalTvl; + tvlList.push(totalTvl); + return p; + }); + const sumOfTvlList = _.sum(tvlList); + const tvlPercents = + sumOfTvlList === 0 + ? ['0', '0', '0', '0'] + : availablePools.map((p: PoolInfo) => + scientificNotationToString( + ((p.tvl / sumOfTvlList) * 100).toString() + ) + ); + const nonZeroIndexes: number[] = []; + tvlPercents.forEach((p, index) => { + if (Number(p) > 0) { + nonZeroIndexes.push(index); + } + }); + const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); + const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); + const finalPercents = tvlPercents.map((p, index) => { + if (nonZeroIndexes.includes(index)) { + const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; + return newP; + } + return p; + }); + const maxPercent = _.max(finalPercents); + let maxPercentPool; + availablePools.forEach((pool: PoolInfo, index) => { + const f = pool.fee; + const temp: PoolInfo = { + ...pool, + percent: finalPercents[index], + tvl: tvlList[index], + }; + currentPoolsMap[f] = temp; + if (finalPercents[index] == maxPercent) { + maxPercentPool = temp; + } + }); + // url-fee-pool + const urlFeePool = url_fee + ? currentPoolsMap[url_fee] || { fee: url_fee } + : null; + setCurrentPools(currentPoolsMap); + setCurrentSelectedPool(urlFeePool || maxPercentPool); + } else { + setCurrentPools({}); + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } else { + setCurrentPools({}); + if (tokenX && tokenY) { + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } + } + function switchSelectedFee(fee: number) { + if (tokenX && tokenY && currentPools) { + const pool = currentPools[fee]; + setCurrentSelectedPool(pool || { fee }); + if (!pool) { + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setInvalidRange(false); + } + } + } + function changeTokenXAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenXAmount(amount); + if (sort) { + if (!onlyAddXToken) { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenYAmount(amount_result); + } + } else { + if (!onlyAddYToken) { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } + } + } + function changeTokenYAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenYAmount(amount); + if (sort) { + if (!onlyAddYToken) { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } else { + if (!onlyAddXToken) { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenXAmount(amount_result); + } + } + } + function getTokenYAmountByCondition({ + // real + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + // X-->L + const L = + +amount * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); + // L-->current Y + const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); + // L--> Y + const Y = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1)); + const decimalsRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + + const Y_result = (Y + Yc) * decimalsRate; + return Y_result.toString(); + } + } + function getTokenXAmountByCondition({ + // real + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + let L; + // Yc-->L + if (leftPoint == currentPoint) { + L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); + } else { + // Y-->L + L = + +amount * + ((Math.sqrt(CONSTANT_D) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); + } + const X = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); + const decimalsRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const X_result = X * decimalsRate; + return X_result.toString(); + } + } + function switchFeeBoxStatus() { + setFeeBoxStatus(!feeBoxStatus); + } + function pointChange({ + leftPoint, + rightPoint, + currentPoint, + }: { + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setLeftPoint(leftPoint); + setRightPoint(rightPoint); + setCurrentPoint(currentPoint); + setInvalidRange(false); + setOnlyAddXToken(false); + setOnlyAddYToken(false); + // invalid point + if (leftPoint >= rightPoint) { + setInvalidRange(true); + setTokenXAmount(''); + setTokenYAmount(''); + return; + } + // can only add x token + if (leftPoint > currentPoint) { + setOnlyAddXToken(true); + if (sort) { + setTokenYAmount(''); + } else { + setTokenXAmount(''); + } + return; + } + // can only add y token + if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { + setOnlyAddYToken(true); + if (sort) { + setTokenXAmount(''); + } else { + setTokenYAmount(''); + } + return; + } + if (sort) { + if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } else { + if (tokenXAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + } + function switchButtonSort() { + if (tokenX || tokenY) { + setTokenX(tokenY); + setTokenY(tokenX); + setTokenXAmount(tokenYAmount); + setTokenYAmount(tokenXAmount); + setTokenXBalanceFromNear(tokenYBalanceFromNear); + setTokenYBalanceFromNear(tokenXBalanceFromNear); + } + setButtonSort(!buttonSort); + } + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; + } + } + function goPoolsPage() { + const poolId = currentSelectedPool?.pool_id; + if (poolId) { + const newPoolId = get_pool_name(poolId); + openUrl(`/poolV2/${newPoolId}`); + } else { + localStorage.setItem(REF_FI_POOL_ACTIVE_TAB, 'v2'); + openUrl('/pools'); + } + } + const tokenSort = tokenX?.id == currentSelectedPool?.token_x; + const mobileDevice = isMobile(); + return ( + <> +
+
+ +
+ + + +
+
+
+
+
+
+
+ +
+ + + +
+
+ {/* left area */} +
+
+ { + if (tokenY && tokenY.id == token.id) return; + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + if (tokenX && tokenX.id == token.id) return; + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + className="pt-0 absolute top-8 outline-none left-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + + +
+ } + /> +
{ + setViewPoolHover(true); + }} + onMouseLeave={() => { + setViewPoolHover(false); + }} + onClick={goPoolsPage} + className={`flex items-center justify-center bg-viewPoolBgColor rounded-md px-3.5 py-1 mb-3 cursor-pointer ${ + viewPoolHover ? 'text-white' : 'text-primaryText' + }`} + > + + + + +
+
+
+
+ + {tokenX ? ( +
+ + + {toRealSymbol(tokenX.symbol)} + +
+ ) : ( + <> + {} + + + )} + +
+ } + onSelect={(token) => { + if (tokenY && tokenY.id == token.id) return; + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + balances={balances} + /> +
+
{ + if (!mobileDevice) { + setButtonHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setButtonHover(false); + } + }} + onTouchStart={() => { + if (mobileDevice) { + setButtonHover(true); + } + }} + onTouchEnd={() => { + if (mobileDevice) { + setButtonHover(false); + } + }} + onClick={switchButtonSort} + className="flex flex-col items-center justify-center border-2 border-switchIconBorderColor w-6 h-6 rounded-lg mx-2 cursor-pointer box-content bg-switchIconBgColor" + > + + +
+
+ + {tokenY ? ( +
+ + + {toRealSymbol(tokenY.symbol)} + +
+ ) : ( + <> + + + )} + +
+ } + onSelect={(token: TokenMetadata) => { + if (tokenX && tokenX.id == token.id) return; + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + balances={balances} + /> +
+
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > 0 ? ( + {displayTvl(currentPools[fee].tvl)} + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ + + + + + + +
+
+ {/* right area */} + {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} +
+
+
+
+ + ); +} +function CreatePoolComponent({ + currentSelectedPool, + tokenX, + tokenY, + tokenPriceList, + buttonSort, +}: { + currentSelectedPool: PoolInfo; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + tokenPriceList: Record; + buttonSort: boolean; +}) { + const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); + const [createPoolRate, setCreatePoolRate] = useState(''); + const [rateStatus, setRateStatus] = useState(true); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + if (createPoolRate) { + const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); + setCreatePoolRate(toPrecision(rateString, 6)); + } + }, [buttonSort]); + function getCurrentPriceValue(token: TokenMetadata) { + if (token) { + const price = tokenPriceList[token.id]?.price; + return price ? `${'$' + price}` : '$-'; + } else { + return '$-'; + } + } + function createPool() { + setCreatePoolButtonLoading(true); + const { fee } = currentSelectedPool; + const pointDelta = POINTDELTAMAP[fee]; + let decimalRate = + Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); + let init_point = getPointByPrice( + pointDelta, + createPoolRate, + decimalRate, + true + ); + const arr = [tokenX.id, tokenY.id]; + arr.sort(); + if (arr[0] !== tokenX.id) { + decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + init_point = getPointByPrice( + pointDelta, + new BigNumber(1).dividedBy(createPoolRate).toFixed(), + decimalRate, + true + ); + create_pool({ + token_a: tokenY.id, + token_b: tokenX.id, + fee: currentSelectedPool.fee, + init_point, + }); + } else { + create_pool({ + token_a: tokenX.id, + token_b: tokenY.id, + fee: currentSelectedPool.fee, + init_point, + }); + } + } + function switchRate() { + setRateStatus(!rateStatus); + } + function getPoolRate() { + if (createPoolRate) { + const rate = 1 / +createPoolRate; + return toPrecision(rate.toString(), 6); + } + return ''; + } + const mobileDevice = isMobile(); + return ( +
+
+ + : +
+
+ +
+
+

+ +

+
+
+

+ +

+
+ + 1 {toRealSymbol(tokenX?.symbol)} = + +
+ { + setCreatePoolRate(target.value); + }} + /> + + {toRealSymbol(tokenY?.symbol)} + +
+
+
+ + + +
+ {rateStatus ? ( +
+ 1 {toRealSymbol(tokenX?.symbol)} + + ({getCurrentPriceValue(tokenX)}) + + + + {createPoolRate} {toRealSymbol(tokenY?.symbol)} + +
+ ) : ( +
+ 1 {toRealSymbol(tokenY?.symbol)} + + ({getCurrentPriceValue(tokenY)}) + + + + {getPoolRate()} {toRealSymbol(tokenX?.symbol)} + +
+ )} + + +
+
+
+
+
+ {isSignedIn ? ( + + ( + <> + + + )} + /> + + ) : ( + + )} +
+ ); +} +function AddLiquidityComponent({ + currentSelectedPool, + tokenX, + tokenY, + tokenPriceList, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + pointChange, + onlyAddXToken, + onlyAddYToken, + invalidRange, + seeds, +}: { + currentSelectedPool: PoolInfo; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + tokenPriceList: Record; + tokenXAmount: string; + tokenYAmount: string; + tokenXBalanceFromNear: string; + tokenYBalanceFromNear: string; + pointChange: any; + onlyAddXToken: boolean; + onlyAddYToken: boolean; + invalidRange: boolean; + seeds: Seed[]; +}) { + let [leftCustomPrice, setLeftCustomPrice] = useState(''); + let [rightCustomPrice, setRightCustomPrice] = useState(''); + let [leftPoint, setLeftPoint] = useState(0); + let [rightPoint, setRightPoint] = useState(0); + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [currentPoint, setCurrentPoint] = useState(); + const [chartLoading, setChartLoading] = useState(false); + const [noDataForChart, setNoDataForChart] = useState(false); + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< + number | string + >(); + const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); + const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< + Record + >({}); + const { globalState } = useContext(WalletContext); + const [timer, setTimer] = useState(null); + const [depthData, setDepthData] = useState(null); + const [displayedSeedIndex, setDisplayedSeedIndex] = useState(0); + const isSignedIn = globalState.isSignedIn; + const intl = useIntl(); + const chartDom = useRef(null); + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + // init + useEffect(() => { + const { current_point, point_delta } = currentSelectedPool; + // 5, 10 20 50 + const optionsMapPoints_temp = {}; + quickOptions.forEach((p: number) => { + const { left_p, right_p } = getPointByCondition(p); + optionsMapPoints_temp[p] = { left_p, right_p }; + if (p == 10) { + leftPoint = left_p; + rightPoint = right_p; + setLeftPoint(left_p); + setRightPoint(right_p); + } + }); + // full + const l_p_temp = _.max([current_point - 400000, -800000]); + const r_p_temp = _.min([current_point + 400000, 800000]); + let l_p = Math.floor(l_p_temp / point_delta) * point_delta; + let r_p = Math.floor(r_p_temp / point_delta) * point_delta; + if (r_p - l_p >= POINTRIGHTRANGE) { + l_p = l_p + point_delta; + } + optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; + // set + setCurrentPoint(current_point); + setQuickOptionsMapPoint(optionsMapPoints_temp); + setCurrentCheckedQuickOption(10); + // show chart data + setChartLoading(true); + setNoDataForChart(false); + clearTimeout(timer); + const timer_latest = setTimeout(() => { + getChartData(); + }, 1000); + setTimer(timer_latest); + }, [currentSelectedPool]); + useEffect(() => { + pointChange({ leftPoint, rightPoint, currentPoint }); + const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { + const { left_p, right_p } = quickOptionsMapPoint[key]; + if (left_p == leftPoint && right_p == rightPoint) { + return key; + } + }); + if (targetKey) { + setCurrentCheckedQuickOption(targetKey); + } else { + setCurrentCheckedQuickOption(undefined); + } + if (depthData) { + drawChartData({ + depthData, + left_point: leftPoint, + right_point: rightPoint, + token_x_decimals, + token_y_decimals, + chartDom, + sort: tokenX.id == token_x, + space_x: 5, + }); + } + }, [leftPoint, rightPoint]); + async function getChartData() { + const depthData = await get_pool_marketdepth(currentSelectedPool.pool_id); + const length = drawChartData({ + depthData, + left_point: leftPoint, + right_point: rightPoint, + token_x_decimals, + token_y_decimals, + chartDom, + sort: tokenX.id == token_x, + space_x: 5, + }); + if (length == 0) { + setNoDataForChart(true); + } else { + setDepthData(depthData); + } + setChartLoading(false); + } + function getPointByCondition(p: number) { + const { point_delta, token_x } = currentSelectedPool; + const c_price = getCurrentPrice_real_decimal(); + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const tokenSort = tokenX.id == token_x ? true : false; + const reduce_p = 1 - p / 100; + const add_p = 1 + p / 100; + if (tokenSort) { + // -10% example + const l_price = new BigNumber(c_price).multipliedBy(reduce_p).toFixed(); + const point_l = getPointByPrice( + point_delta, + l_price.toString(), + decimalRate + ); + // +10% example + const r_price = new BigNumber(c_price).multipliedBy(add_p).toFixed(); + const point_r = getPointByPrice( + point_delta, + r_price.toString(), + decimalRate + ); + return { left_p: point_l, right_p: point_r }; + } else { + const c_price2 = new BigNumber(1).dividedBy(c_price).toFixed(); + // +10% example + const priceAdd = new BigNumber(c_price2).multipliedBy(add_p); + const l_price_2 = new BigNumber(1).dividedBy(priceAdd).toFixed(); + const point_l_2 = getPointByPrice( + point_delta, + l_price_2.toString(), + decimalRate + ); + // -10% example + const priceDivide = new BigNumber(c_price2).multipliedBy(reduce_p); + const r_price_2 = new BigNumber(1).dividedBy(priceDivide).toFixed(); + const point_r_2 = getPointByPrice( + point_delta, + r_price_2.toString(), + decimalRate + ); + return { left_p: point_l_2, right_p: point_r_2 }; + } + } + function getCurrentPrice() { + let price = getCurrentPrice_real_decimal(); + if (tokenX.id == token_y) { + price = new BigNumber(1).dividedBy(price).toFixed(); + } + if (price) { + return toPrecision(price.toString(), 8); + } else { + return '-'; + } + } + function getCurrentPriceValue() { + if (tokenX) { + const price = tokenPriceList[tokenX.id]?.price; + return price ? `${'$' + price}` : '$-'; + } else { + return '$-'; + } + } + function getCurrentPrice_real_decimal() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { current_point, token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(current_point, decimalRate); + return price; + } + return 0; + } + function addOneSlot(direction: string) { + const { point_delta } = currentSelectedPool; + const l_p = leftPoint + point_delta; + const r_p = rightPoint + point_delta; + if (direction == 'l') { + setLeftPoint(Math.max(Math.min(POINTRIGHTRANGE, l_p), POINTLEFTRANGE)); + } else if (direction == 'r') { + const target_slot_r = Math.max( + Math.min(POINTRIGHTRANGE, r_p), + POINTLEFTRANGE + ); + if (target_slot_r - leftPoint >= POINTRIGHTRANGE) return; + setRightPoint(target_slot_r); + } + } + function reduceOneSlot(direction: string) { + const { point_delta } = currentSelectedPool; + const l_p = leftPoint - point_delta; + const r_p = rightPoint - point_delta; + if (direction == 'l') { + const target_slot_l = Math.max( + Math.min(POINTRIGHTRANGE, l_p), + POINTLEFTRANGE + ); + if (rightPoint - target_slot_l >= POINTRIGHTRANGE) return; + setLeftPoint(target_slot_l); + } else if (direction == 'r') { + setRightPoint(Math.max(Math.min(POINTRIGHTRANGE, r_p), POINTLEFTRANGE)); + } + } + function handlePriceToAppropriatePoint() { + const { point_delta, token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + if (leftCustomPrice) { + if (!tokenSort) { + leftCustomPrice = new BigNumber(1).dividedBy(leftCustomPrice).toFixed(); + } + const c_point = getPointByPrice( + point_delta, + leftCustomPrice, + decimalRate + ); + setLeftCustomPrice(''); + setLeftPoint(c_point); + if (rightPoint - c_point >= POINTRIGHTRANGE) { + const appropriate_r_point = POINTRIGHTRANGE + c_point - point_delta; + setRightPoint(appropriate_r_point); + } + } + if (rightCustomPrice) { + if (!tokenSort) { + rightCustomPrice = new BigNumber(1) + .dividedBy(rightCustomPrice) + .toFixed(); + } + const c_point = getPointByPrice( + point_delta, + rightCustomPrice, + decimalRate + ); + setRightCustomPrice(''); + setRightPoint(c_point); + if (c_point - leftPoint >= POINTRIGHTRANGE) { + const appropriate_l_point = c_point - POINTRIGHTRANGE + point_delta; + setLeftPoint(appropriate_l_point); + } + } + } + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + if (tokenSort) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + } else if (onlyAddYToken) { + if (tokenSort) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + return !(condition1 && condition2); + } + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + function getLeftPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(leftPoint, decimalRate); + if (tokenX.id == token_y) { + price = new BigNumber(1).dividedBy(price).toFixed(); + } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getRightPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(rightPoint, decimalRate); + if (tokenX.id == token_y) { + price = new BigNumber(1).dividedBy(price).toFixed(); + } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function addLiquidity() { + setAddLiquidityButtonLoading(true); + const { pool_id } = currentSelectedPool; + add_liquidity({ + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: tokenSort + ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + amount_y: tokenSort + ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + token_x: tokenSort ? tokenX : tokenY, + token_y: tokenSort ? tokenY : tokenX, + }); + } + function quickChangePoint(item: string | number) { + if (currentCheckedQuickOption == item) return; + const { point_delta, current_point } = currentSelectedPool; + const decimalRateTurn = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + if (item == 'full') { + const l_p_temp = _.max([current_point - 400000, -800000]); + const r_p_temp = _.min([current_point + 400000, 800000]); + let l_p = Math.floor(l_p_temp / point_delta) * point_delta; + let r_p = Math.floor(r_p_temp / point_delta) * point_delta; + if (r_p - l_p >= POINTRIGHTRANGE) { + l_p = l_p + point_delta; + } + setLeftPoint(l_p); + setRightPoint(r_p); + } else { + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price_c = getPriceByPoint(currentPoint, decimalRate); + if (tokenSort) { + const price_l_new = new BigNumber(1 - +item / 100) + .multipliedBy(price_c) + .toFixed(); + const price_r_new = new BigNumber(1 + +item / 100) + .multipliedBy(price_c) + .toFixed(); + const l_point = getPointByPrice( + point_delta, + price_l_new, + decimalRateTurn + ); + const r_point = getPointByPrice( + point_delta, + price_r_new, + decimalRateTurn + ); + setLeftPoint(l_point); + setRightPoint(r_point); + } else { + const price_c_2 = new BigNumber(1).dividedBy(price_c).toFixed(); + const price_l_new_2 = new BigNumber(1 - +item / 100) + .multipliedBy(price_c_2) + .toFixed(); + const price_r_new_2 = new BigNumber(1 + +item / 100) + .multipliedBy(price_c_2) + .toFixed(); + const price_l_new = new BigNumber(1).dividedBy(price_r_new_2).toFixed(); + const price_r_new = new BigNumber(1).dividedBy(price_l_new_2).toFixed(); + const l_point = getPointByPrice( + point_delta, + price_l_new, + decimalRateTurn + ); + const r_point = getPointByPrice( + point_delta, + price_r_new, + decimalRateTurn + ); + setLeftPoint(l_point); + setRightPoint(r_point); + } + } + } + function getButtonText() { + let txt: any = ( + + ); + if (invalidRange) { + txt = ( + + ); + } else if ( + (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || + (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || + (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + !onlyAddXToken && + !onlyAddYToken && + (+tokenXAmount == 0 || +tokenYAmount == 0) + ) { + txt = ( + + ); + } else if ( + +tokenXAmount > 0 && + new BigNumber(tokenXAmount).isGreaterThan( + getMax(tokenX, tokenXBalanceFromNear) + ) + ) { + txt = ( + + ); + } else if ( + +tokenYAmount > 0 && + new BigNumber(tokenYAmount).isGreaterThan( + getMax(tokenY, tokenYBalanceFromNear) + ) + ) { + txt = ( + + ); + } + return txt; + } + function getPriceTip() { + const tip = intl.formatMessage({ id: 'price_on_slot_tip' }); + let result: string = `
${tip}
`; + return result; + } + function get_related_seeds() { + const temp_seeds = seeds.map((seed: Seed) => { + const [contractId, temp_mft_id] = seed.seed_id.split('@'); + const [fixRange, pool_id, left_point, right_point] = + temp_mft_id.split('&'); + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price_left = getPriceByPoint(+left_point, decimalRate); + const price_right = getPriceByPoint(+right_point, decimalRate); + return { + seed, + price_left, + price_right, + }; + }); + const tokenSort = tokenX.id == token_x ? true : false; + const targetSeed = temp_seeds[displayedSeedIndex]; + const { price_left, price_right } = targetSeed; + let price_left_final = price_left; + let price_right_final = price_right; + + if (!tokenSort) { + price_left_final = new BigNumber(1).dividedBy(price_right).toFixed(); + price_right_final = new BigNumber(1).dividedBy(price_left).toFixed(); + } + const display_price_left = displayNumberToAppropriateDecimals( + price_left_final.toString() + ); + const display_price_right = displayNumberToAppropriateDecimals( + price_right_final.toString() + ); + return ( +
+ + 1 {tokenX.symbol} ={' '} + +
+ { + setDisplayedSeedIndex(displayedSeedIndex - 1); + }} + className={`transform rotate-90 mr-1.5 text-primaryText cursor-pointer ${ + seeds.length > 1 && displayedSeedIndex > 0 ? '' : 'hidden' + }`} + > + { + if (!tokenSort) { + leftCustomPrice = price_right_final; + rightCustomPrice = price_left_final; + } else { + leftCustomPrice = price_left_final; + rightCustomPrice = price_right_final; + } + setLeftCustomPrice(leftCustomPrice); + setRightCustomPrice(rightCustomPrice); + handlePriceToAppropriatePoint(); + }} + > + {display_price_left} ~ {display_price_right} + + {tokenY.symbol} + { + setDisplayedSeedIndex(displayedSeedIndex + 1); + }} + className={`transform -rotate-90 ml-1.5 text-primaryText cursor-pointer ${ + seeds.length > 1 && displayedSeedIndex < seeds.length - 1 + ? '' + : 'hidden' + }`} + > +
+
+ ); + } + function rewardRangeTip() { + const tip = intl.formatMessage({ id: 'reward_range_tip' }); + let result: string = `
${tip}
`; + return result; + } + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const isAddLiquidityDisabled = getButtonStatus(); + const mobileDevice = isMobile(); + return ( +
+
+ +
+
+
+ + + +
+ 1 {toRealSymbol(tokenX?.symbol)} + + ({getCurrentPriceValue()}) + + + + {getCurrentPrice()} {toRealSymbol(tokenY?.symbol)} + +
+
+ {/* range chart area */} +
+ + + + + + + + {chartLoading ? ( + + ) : null} + {noDataForChart ? : null} +
+ {/* input range area */} +
+
+
+ + + + {tokenSort ? ( + { + reduceOneSlot('l'); + }} + addOneSlot={() => { + addOneSlot('l'); + }} + handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} + customPrice={leftCustomPrice} + getPrice={getLeftPrice} + setCustomPrice={setLeftCustomPrice} + inputStatus={leftInputStatus} + setInputStatus={setLeftInputStatus} + > + ) : ( + { + addOneSlot('r'); + }} + addOneSlot={() => { + reduceOneSlot('r'); + }} + handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + > + )} +
+
+ + + + {tokenSort ? ( + { + reduceOneSlot('r'); + }} + addOneSlot={() => { + addOneSlot('r'); + }} + handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + > + ) : ( + { + addOneSlot('l'); + }} + addOneSlot={() => { + reduceOneSlot('l'); + }} + handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} + customPrice={leftCustomPrice} + getPrice={getLeftPrice} + setCustomPrice={setLeftCustomPrice} + inputStatus={leftInputStatus} + setInputStatus={setLeftInputStatus} + > + )} +
+
+
+
+ + +
+ {quickOptions.map((item: number, index) => { + return ( +
{ + quickChangePoint(item); + }} + key={index} + className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ + currentCheckedQuickOption == item + ? 'bg-v3PurpleColor border-v3PurpleColor text-white' + : 'border-v3GreyColor text-v3LightGreyColor' + }`} + > + ± {item}% +
+ ); + })} +
{ + quickChangePoint('full'); + }} + className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ + currentCheckedQuickOption == 'full' + ? 'bg-v3PurpleColor border-v3PurpleColor text-white' + : 'border-v3GreyColor text-v3LightGreyColor' + }`} + > + +
+
+ {seeds.length ? ( +
+
+ +
+ + +
+
+ {get_related_seeds()} +
+ ) : null} +
+
+
+ +
+ +
+
+
+ +
+ +
+
+ {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} +
+ ); +} + +function NoDataComponent(props: any) { + const { isNoPool } = props; + const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); + const mobileDevice = isMobile(); + return ( +
+
+ +
+ {isNoPool ? ( +
+ +
+ ) : null} +
+ {/* range chart area */} +
+ +
+ {/* input range area */} +
+
+
+ + + +
+
+ +
+ +
+ +
+
+
+
+ + + +
+
+ +
+ +
+ +
+
+
+
+
+ + {quickOptions.map((item: number, index) => { + return ( +
+ ± {item}% +
+ ); + })} +
+ +
+
+
+
+ + ( + <> + {isNoPool ? ( + + ) : ( + + )} + + )} + /> + +
+ ); +} +function PointInputComponent({ + reduceOneSlot, + addOneSlot, + handlePriceToAppropriatePoint, + customPrice, + getPrice, + setCustomPrice, + inputStatus, + setInputStatus, +}: any) { + return ( +
+
{ + reduceOneSlot('r'); + }} + > + +
+ { + handlePriceToAppropriatePoint(); + setInputStatus(false); + }} + value={inputStatus ? customPrice : getPrice()} + onChange={({ target }) => { + setInputStatus(true); + const inputPrice = target.value; + setCustomPrice(inputPrice); + }} + /> +
{ + addOneSlot('r'); + }} + > + +
+
+ ); +} +function OneSide({ show }: { show: boolean }) { + return ( +
+ + +
+ +
+
+ ); +} +function InvalidRange({ show }: { show: boolean }) { + return ( +
+ + +
+ +
+
+ ); +} +function InputAmount({ + token, + balance, + tokenPriceList, + changeAmount, + amount, + currentSelectedPool, + hidden, +}: { + token: TokenMetadata; + balance: string; + tokenPriceList: Record; + changeAmount: any; + amount: string; + currentSelectedPool: PoolInfo; + hidden: Boolean; +}) { + const [inputPrice, setInputPrice] = useState(''); + const [showNearTip, setShowNearTip] = useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + const price = token ? tokenPriceList[token.id]?.price : ''; + if (price && amount) { + setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); + } else { + setInputPrice(''); + } + if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { + const difference = new BigNumber(maxBalance).minus(amount); + const b = difference.toFixed(); + const r = difference.isLessThan(0); + if (r) { + setShowNearTip(true); + } else { + setShowNearTip(false); + } + } else { + setShowNearTip(false); + } + }, [amount, token, tokenPriceList.length]); + function getBalance() { + let r = '0'; + if (token && balance) { + r = formatWithCommas(toPrecision(balance.toString(), 3)); + } + return isSignedIn ? r : '-'; + } + function showCurrentPrice() { + if (isNoPool) { + return '$-'; + } else if (inputPrice) { + return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); + } + return '$-'; + } + const maxBalance = + token?.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + const isNoPool = !currentSelectedPool?.pool_id; + return ( +
+ + {showNearTip && !isNoPool ? ( +
+ + +
+ ) : null} +
+ ); +} +function getAllTokens(refTokens: TokenMetadata[], triTokens: TokenMetadata[]) { + triTokens.forEach((tk) => { + const tokenInRef = refTokens.find((token) => token.id === tk.id); + if (tokenInRef) { + tokenInRef.onTri = true; + } else { + refTokens.push(tk); + } + }); + + return refTokens; +} diff --git a/src/pages/poolsV3/interfaces.tsx b/src/pages/poolsV3/interfaces.tsx new file mode 100644 index 000000000..45f8c8529 --- /dev/null +++ b/src/pages/poolsV3/interfaces.tsx @@ -0,0 +1,19 @@ +export interface IAddLiquidityInfo { + pool_id: string; + left_point: number; + right_point: number; + amount_x: string; + amount_y: string; + min_amount_x: string; + min_amount_y: string; +} +export interface IaddLiquidityInfoHelp { + [key: number]: { + left_point: number; + right_point: number; + const_value: string; + }; +} + +export type LiquidityShape = 'Spot' | 'Curve' | 'BidAsk'; +export type PriceRangeModeType = 'by_range' | 'by_radius'; diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 0e5b1c9a4..7d3521a91 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1115,6 +1115,56 @@ export function get_liquidity_value({ }); return v; } +/** + * caculate bin point by price + * @param pointDelta + * @param price + * @param decimalRate tokenY/tokenX + * @returns + */ +export function getBinPointByPrice( + pointDelta: number, + price: string, + decimalRate: number, + slotNumber: number +) { + const point = Math.log(+price * decimalRate) / Math.log(CONSTANT_D); + const point_int = Math.round(point); + const point_int_bin = getBinPointByPoint(pointDelta, slotNumber, point_int); + return point_int_bin; +} + +/** + * caculate bin point by point + * @param pointDelta + * @param point + * @param slotNumber + * @returns + */ +export function getBinPointByPoint( + pointDelta: number, + slotNumber: number, + point: number, + type?: 'round' | 'floor' | 'ceil' +) { + const binWidth = pointDelta * slotNumber; + let int; + if (type == 'floor') { + int = Math.floor(point / binWidth); + } else if (type == 'ceil') { + int = Math.ceil(point / binWidth); + } else { + int = Math.round(point / binWidth); + } + const point_in_bin = int * binWidth; + if (point_in_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_in_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_in_bin; +} + // processing of pool id and farm id const FEE_TIER = [100, 400, 2000, 10000]; const TOKENS = getTokens(); diff --git a/src/services/near.ts b/src/services/near.ts index bc7a8ea97..1aff9ea76 100644 --- a/src/services/near.ts +++ b/src/services/near.ts @@ -199,9 +199,6 @@ export const REF_AIRDRAOP_CONTRACT_ID = config.REF_AIRDROP_CONTRACT_ID; export const REF_TOKEN_ID = config.REF_TOKEN_ID; const XREF_TOKEN_ID = getConfig().XREF_TOKEN_ID; - -export const SLOT_NUMBER = 100; - export const LP_STORAGE_AMOUNT = '0.01'; export const ONE_YOCTO_NEAR = '0.000000000000000000000001'; diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 9355c4395..f6c3bc075 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -38,6 +38,7 @@ import { getPointByPrice } from './commonV3'; import { toPrecision } from '../utils/numbers'; import { REF_DCL_POOL_CACHE_KEY } from '../state/swap'; import { REF_UNI_SWAP_CONTRACT_ID } from './near'; +import { IAddLiquidityInfo } from '../pages/poolsV3/interfaces'; import getConfig from './config'; const LOG_BASE = 1.0001; @@ -807,7 +808,7 @@ export const add_liquidity = async ({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ { - methodName: 'batch_add_liquidity', + methodName: 'add_liquidity', args: { pool_id, left_point, @@ -921,23 +922,18 @@ export const add_liquidity = async ({ return executeMultipleTransactions(transactions); }; -export interface AddLiquidityInfo { - pool_id: string; - left_point: number; - right_point: number; - amount_x: string; - amount_y: string; -} - export const batch_add_liquidity = async ({ - liquidityInfo, + liquidityInfos, token_x, token_y, + amount_x, + amount_y, }: { - liquidityInfo: AddLiquidityInfo[]; -} & { + liquidityInfos: IAddLiquidityInfo[]; token_x: TokenMetadata; token_y: TokenMetadata; + amount_x: string; + amount_y: string; }) => { const transactions: Transaction[] = [ { @@ -946,11 +942,7 @@ export const batch_add_liquidity = async ({ { methodName: 'batch_add_liquidity', args: { - add_liquidity_infos: liquidityInfo.map((info) => ({ - ...info, - min_amount_x: '0', - min_amount_y: '0', - })), + add_liquidity_infos: liquidityInfos, }, gas: '150000000000000', }, @@ -958,23 +950,6 @@ export const batch_add_liquidity = async ({ }, ]; - let amount_x; - let amount_y; - - amount_x = liquidityInfo.reduce( - (acc, info) => acc.plus(info.amount_x), - new Big(0) - ); - - amount_x = amount_x.toFixed(0); - - amount_y = liquidityInfo.reduce( - (acc, info) => acc.plus(info.amount_y), - new Big(0) - ); - - amount_y = amount_y.toFixed(0); - if (+amount_x > 0) { transactions.unshift({ receiverId: token_x.id, @@ -1059,9 +1034,7 @@ export const batch_add_liquidity = async ({ ], }); } - const neededStorage = await get_user_storage_detail({ - size: liquidityInfo.length, - }); + const neededStorage = await get_user_storage_detail({ size: 1 }); if (!ONLY_ZEROS.test(neededStorage)) { transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index 2299532b4..e25bd81f1 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -82,23 +82,24 @@ export const useAllPoolsV2 = () => { p.tvl = tvlx + tvly; - const topBinFee = await getDCLTopBinFee({ - pool_id: p.pool_id, - slot_number: 100, - }); - - if (!topBinFee || ONLY_ZEROS.test(topBinFee.total_liquidity)) { - p.top_bin_apr = '0'; - p.top_bin_apr_display = '-'; - } else { - const apr = new Big(topBinFee.total_fee) - .div(topBinFee.total_liquidity) - .mul(365) - .toFixed(2); - p.top_bin_apr = apr; - p.top_bin_apr_display = apr + '%'; - } - + try { + const topBinFee = await getDCLTopBinFee({ + pool_id: p.pool_id, + slot_number: 100, + }); + + if (!topBinFee || ONLY_ZEROS.test(topBinFee.total_liquidity)) { + p.top_bin_apr = '0'; + p.top_bin_apr_display = '-'; + } else { + const apr = new Big(topBinFee.total_fee) + .div(topBinFee.total_liquidity) + .mul(365) + .toFixed(2); + p.top_bin_apr = apr; + p.top_bin_apr_display = apr + '%'; + } + } catch (error) {} return p; }) ); diff --git a/tailwind.config.js b/tailwind.config.js index 8ac96339f..12c86688d 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -408,6 +408,7 @@ module.exports = { chartHoverBoxBg: 'rgba(26, 39, 48, 0.9)', chartBorderColor: '#344451', proTabBgColor: '#324451', + dclTabBorderColor:'#3F4A52' }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From 2a1b6a77d33e30b7698a32d5cf68517182f54e19 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 26 Jun 2023 11:46:17 +0800 Subject: [PATCH 037/204] add your liquidity chart --- src/components/d3Chart/DclChart.tsx | 12 ++-- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 68 ++++++++++++++------ 2 files changed, 53 insertions(+), 27 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 09fac6a86..6ee5ec361 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -201,14 +201,14 @@ export default function DclChart({ if (config?.radiusMode && config?.targetPoint) { // hide drag bar and show target price bar draw_radius_mode_bar(); - d3.select('.leftBar').attr('style', 'display:none'); - d3.select('.rightBar').attr('style', 'display:none'); + d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); + d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); } else { - d3.select('.leftBar').attr('style', ''); - d3.select('.rightBar').attr('style', ''); - d3.select('.radiusBar').attr('style', 'display:none'); + d3.select(`${randomId} .leftBar`).attr('style', ''); + d3.select(`${randomId} .rightBar`).attr('style', ''); + d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); } - }, [config?.radiusMode, config?.targetPoint]); + }, [config?.radiusMode, config?.targetPoint, pool_id]); async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 4b207c6df..9663ea179 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -748,6 +748,7 @@ export default function AddYourLiquidityPageV3() { onlyAddXToken, onlyAddYToken, invalidRange, + isSignedIn, }} >
@@ -1099,13 +1100,13 @@ export default function AddYourLiquidityPageV3() { } )}
- -
+ {/* todo */} + {/*
-
+
*/} {currentSelectedPool && currentSelectedPool.pool_id && ( )} @@ -1864,6 +1865,8 @@ function SetPointsComponent() { SLOT_NUMBER, BIN_WIDTH, + + isSignedIn, } = useContext(LiquidityProviderData); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' @@ -1878,6 +1881,7 @@ function SetPointsComponent() { const [leftInputStatus, setLeftInputStatus] = useState(false); const [rightInputStatus, setRightInputStatus] = useState(false); const [targetInputStatus, setTargetInputStatus] = useState(false); + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); const { token_x, token_y } = currentSelectedPool; const token_x_decimals = tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; @@ -1890,6 +1894,7 @@ function SetPointsComponent() { setTargetPoint(currentSelectedPool.current_point); setRadius(3); setPriceRangeMode('by_range'); + setChartTab('liquidity'); } }, [currentSelectedPool, tokenX, tokenY]); // change event in radius mode @@ -1998,8 +2003,6 @@ function SetPointsComponent() { setBinNumber(v); } } - console.log('leftPoint', leftPoint); - console.log('rightPoint', rightPoint); return (
{ + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' }`} > Liquidity { + setChartTab('yours'); + }} > Yours
- {leftPoint && rightPoint && ( - +
+ {leftPoint && rightPoint && ( + + )} +
+ {isSignedIn && ( +
+ +
)}
{/* set price range area */} From 8ae69172221cf886ca28a6fcbbef2dfff1001e17 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 27 Jun 2023 00:02:57 +0800 Subject: [PATCH 038/204] add liquidity --- src/components/pool/RemovePoolV3.tsx | 3 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 815 +++++++++++++++++-- 2 files changed, 746 insertions(+), 72 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index a0b40c661..89235489a 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -43,12 +43,11 @@ import _ from 'lodash'; import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; import { useWalletSelector } from '~context/WalletSelectorContext'; import { getDclUserPoints } from '../../services/indexer'; -import { SLOT_NUMBER } from '../../services/near'; import Big from 'big.js'; import { IntegerInputComponent } from '../../pages/poolsV3/AddYourLiquidityPageV3'; export type RemoveType = 'left' | 'right' | 'all'; - +const SLOT_NUMBER = 2; // todo export const RemovePoolV3 = (props: any) => { const { tokenMetadata_x_y, diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 9663ea179..6c5062b69 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -166,6 +166,7 @@ export default function AddYourLiquidityPageV3() { const [topPairs, setTopPairs] = useState([]); const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); + const [token_amount_tip, set_token_amount_tip] = useState(); // callBack handle useAddAndRemoveUrlHandle(); @@ -749,6 +750,8 @@ export default function AddYourLiquidityPageV3() { onlyAddYToken, invalidRange, isSignedIn, + token_amount_tip, + set_token_amount_tip, }} >
@@ -929,6 +932,12 @@ export default function AddYourLiquidityPageV3() { : false } > + {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null}
@@ -1128,7 +1137,12 @@ export default function AddYourLiquidityPageV3() { ); } - +/** + * 如果只选择了一个bin且是双边的话,那只能选择spot模式 todo 待处理 + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 提示 todo + * @returns + */ function AddLiquidityButton() { const { currentSelectedPool, @@ -1147,9 +1161,8 @@ function AddLiquidityButton() { onlyAddXToken, onlyAddYToken, invalidRange, + set_token_amount_tip, } = useContext(LiquidityProviderData); - console.log('leftPoint', leftPoint); - console.log('rightPoint', rightPoint); const tokenSort = tokenX.id == currentSelectedPool.token_x; const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = useState(false); @@ -1231,59 +1244,150 @@ function AddLiquidityButton() { } if (!onlyAddXToken && !onlyAddYToken) { /** - * 把包含当前点位的那个bin单独拿出来作为一个nft处理,不参与做等差,因为参与做等差会导致 不成立。 - * 除去参与做等差的 x,y,剩余 x,y 作为一个nft,由于这个nft的liquidity太小了,不能添加,可以忽略。 - * */ + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; const current_l_point = getBinPointByPoint( point_delta, SLOT_NUMBER, - currentPoint, + current_point, 'floor' ); const current_r_point = getBinPointByPoint( point_delta, SLOT_NUMBER, - currentPoint, + current_point, 'ceil' ); - const nftList_y = get_y_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - const nftList_x = get_x_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - /** 包含当前点位的bin start */ - const used_x_amount = nftList_x.reduce((acc, cur) => { - return acc.plus(cur.amount_x || '0'); - }, Big(0)); - const used_y_amount = nftList_y.reduce((acc, cur) => { - return acc.plus(cur.amount_y || '0'); - }, Big(0)); - const current_bin_nft: IAddLiquidityInfo = { - pool_id, - left_point: current_l_point, - right_point: current_r_point, - amount_x: Big(tokenXAmount_nonDivisible) - .minus(used_x_amount) - .toFixed(0), - amount_y: Big(tokenYAmount_nonDivisible) - .minus(used_y_amount) - .toFixed(0), - min_amount_x: '0', - min_amount_y: '0', - }; - /** 包含当前点位的bin end */ - nftList = nftList_x.concat(nftList_y); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(''); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } } batch_add_liquidity({ liquidityInfos: nftList, @@ -1293,7 +1397,7 @@ function AddLiquidityButton() { amount_y: tokenYAmount_nonDivisible, }); } - // 待删除项 + // 待删除项目 function addLiquidityCurve() { /** * 已知条件: @@ -1396,6 +1500,7 @@ function AddLiquidityButton() { amount_y: tokenYAmount_nonDivisible, }); } + // 待删除项目 function addLiquidityBidAsk() { /** * 已知条件: @@ -1498,8 +1603,575 @@ function AddLiquidityButton() { amount_y: tokenYAmount_nonDivisible, }); } + /** + * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ - function get_decline_pattern_nfts({ + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == exclude_cur_total_nft_number) { + amount_x = dis + .mul(exclude_cur_total_nft_number + 1) + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 1).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == exclude_cur_total_nft_number) { + amount_y = dis + .mul(exclude_cur_total_nft_number + 1) + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 2).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == 0) { + amount_y = dis + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + // 不同点1 + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + // 不同点2 + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 2).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == 0) { + amount_x = dis + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 和 bid ask 上升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_rise_pattern_nfts({ left_point, right_point, token, @@ -1517,13 +2189,12 @@ function AddLiquidityButton() { is_token_y?: boolean; }) { /** - * 从左往右逐渐下降模式 - * nft 从右往左计算 + * 从左往右逐渐上升模式 + * 从左往右计算 * e.g. - * 公式推导: - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) * ===>求出dis后,就可以知道每个nft的amount * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) * */ @@ -1554,14 +2225,13 @@ function AddLiquidityButton() { const addLiquidityInfoList: IAddLiquidityInfo[] = []; const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; for (let i = 0; i < total_nft_number; i++) { - let left_i_point; + const left_i_point = left_point + nftWidth * i; let right_i_point; if (i == total_nft_number - 1) { - left_i_point = left_point; + right_i_point = right_point; } else { - left_i_point = right_point - nftWidth * (i + 1); + right_i_point = left_point + nftWidth * (i + 1); } - right_i_point = right_point - nftWidth * i; const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); total_const = total_const.plus(const_i); addLiquidityInfoHelp[i] = { @@ -1591,8 +2261,13 @@ function AddLiquidityButton() { } return addLiquidityInfoList; } - - function get_rise_pattern_nfts({ + /** + * curve 和 bid ask 下降升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_decline_pattern_nfts({ left_point, right_point, token, @@ -1610,12 +2285,13 @@ function AddLiquidityButton() { is_token_y?: boolean; }) { /** - * 从左往右逐渐上升模式 - * 从左往右计算 + * 从左往右逐渐下降模式 + * nft 从右往左计算 * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) * ===>求出dis后,就可以知道每个nft的amount * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) * */ @@ -1646,13 +2322,14 @@ function AddLiquidityButton() { const addLiquidityInfoList: IAddLiquidityInfo[] = []; const addLiquidityInfoHelp: IaddLiquidityInfoHelp = {}; for (let i = 0; i < total_nft_number; i++) { - const left_i_point = left_point + nftWidth * i; + let left_i_point; let right_i_point; if (i == total_nft_number - 1) { - right_i_point = right_point; + left_i_point = left_point; } else { - right_i_point = left_point + nftWidth * (i + 1); + left_i_point = right_point - nftWidth * (i + 1); } + right_i_point = right_point - nftWidth * i; const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); total_const = total_const.plus(const_i); addLiquidityInfoHelp[i] = { @@ -1682,7 +2359,6 @@ function AddLiquidityButton() { } return addLiquidityInfoList; } - function formula_of_token_x(leftPoint: number, rightPoint: number) { return ( (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / @@ -2623,7 +3299,6 @@ function PointInputComponent({ }`} onBlur={() => { if (customPrice) { - debugger; const appropriate_point_temp = handlePriceToAppropriatePoint(customPrice); setInputStatus(false); From 9d1dfca37e5096321d0f0f2fd04a70ac9394f588 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 27 Jun 2023 23:54:48 +0800 Subject: [PATCH 039/204] add remove liquidity function --- src/components/pool/RemovePoolV3.tsx | 1079 ++++++++---------- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 18 +- src/pages/poolsV3/interfaces.tsx | 13 +- src/services/swapV3.ts | 130 ++- 4 files changed, 635 insertions(+), 605 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 89235489a..f5df62100 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -1,13 +1,10 @@ -import { path } from 'animejs'; import React, { useEffect, useMemo, useState, useContext, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { WalletContext } from '../../utils/wallets-integration'; -import { useHistory } from 'react-router-dom'; import { Card } from '~components/card/Card'; -import { isMobile } from '~utils/device'; import { ModalClose } from '~components/icon'; import { TokenMetadata } from '../../services/ft-contract'; -import { SwitchButton, Slider } from '~components/icon/V3'; +import { Slider } from '~components/icon/V3'; import { GradientButton, ButtonTextWrapper, @@ -20,8 +17,6 @@ import { toPrecision, toReadableNumber, toNonDivisibleNumber, - formatWithCommas, - ONLY_ZEROS, } from '~utils/numbers'; import { getPriceByPoint, @@ -30,29 +25,44 @@ import { getXAmount_per_point_by_Lx, getYAmount_per_point_by_Ly, sort_tokens_by_base, - POINTRIGHTRANGE, - getPointByPrice, + getBinPointByPrice, + getBinPointByPoint, } from '../../services/commonV3'; import { PoolInfo, - pointToPrice, - priceToPoint, - remove_liquidity, + batch_remove_liquidity_contract, } from '../../services/swapV3'; -import _ from 'lodash'; import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; -import { useWalletSelector } from '~context/WalletSelectorContext'; -import { getDclUserPoints } from '../../services/indexer'; import Big from 'big.js'; import { IntegerInputComponent } from '../../pages/poolsV3/AddYourLiquidityPageV3'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, +} from '../../components/d3Chart/config'; +import { + IChartItemConfig, + IChartConfig, +} from '../../components/d3Chart/interfaces'; +import { + formatNumber, + formatWithCommas_usd, +} from '../../components/d3Chart/utils'; +import { + IAddLiquidityInfo, + IRemoveLiquidityInfo, + IBatchUpdateiquidityInfo, +} from '~pages/poolsV3/interfaces'; export type RemoveType = 'left' | 'right' | 'all'; -const SLOT_NUMBER = 2; // todo +/** + * 遗产nft 的处理,采用老的弹窗ui?todo + * @param props + * @returns + */ export const RemovePoolV3 = (props: any) => { const { tokenMetadata_x_y, poolDetail, - userLiquidity, tokenPriceList, isLegacy, listLiquidities, @@ -60,411 +70,244 @@ export const RemovePoolV3 = (props: any) => { }: { tokenMetadata_x_y: TokenMetadata[]; poolDetail: PoolInfo; - userLiquidity: UserLiquidityInfo; tokenPriceList: any; isLegacy?: boolean; restProps: any; listLiquidities: UserLiquidityInfo[]; } = props; - + const SLOT_NUMBER = get_slot_number_in_a_bin(); const [slippageTolerance, setSlippageTolerance] = useState(0.5); - const [tokenXAmount, setTokenXAmount] = useState(''); - const [tokenYAmount, setTokenYAmount] = useState(''); - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - const { decimals: token_y_decimals } = tokens[1]; + // const tokens = sort_tokens_by_base(tokenMetadata_x_y);// todo + const tokens = tokenMetadata_x_y; const { decimals: token_x_decimals } = tokens[0]; - console.log('poolDetail: ', poolDetail); - - console.log('tokenMetadata_x_y: ', tokenMetadata_x_y); - - // all your liquidity amount - const [liquidityAmount, setLiquidityAmount] = useState(''); - console.log('liquidityAmount: ', liquidityAmount); - - const [removeAmount, setRemoveAmount] = useState(''); - const [removeTokenXAmount, setRemoveTokenXAmount] = useState(''); - const [removeTokenYAmount, setRemoveTokenYAmount] = useState(''); - const [isInrange, setIsInrange] = useState(true); + const { decimals: token_y_decimals } = tokens[1]; const [removeLoading, setRemoveLoading] = useState(false); - const [rateDirection, setRateDirection] = useState(true); - const [removePercentAmount, setRemovePercentAmount] = useState('100'); - const [removeType, setRemoveType] = useState('left'); - - const { accountId } = useWalletSelector(); - - const BIN_SIZE = poolDetail.point_delta * SLOT_NUMBER; + const [removeType, setRemoveType] = useState('all'); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + // new + const [minPoint, setMinPoint] = useState(); + const [maxPoint, setMaxPoint] = useState(); const [minPrice, setMinPrice] = useState(''); - const [maxPrice, setMaxPrice] = useState(''); - - // left boundry point - const [leftPoint, setLeftPoint] = useState(); - - // right boundry point - const [rightPoint, setRightPoint] = useState(); - - const [binAmount, setBinAmount] = useState(''); - const [maxBinAmount, setMaxBinAmount] = useState(); - const [dclUserPoints, setDclUserPoints] = useState(); - - const [changeType, setChangeType] = useState<'min' | 'max'>(); - - useEffect(() => { - if (!dclUserPoints) return; - - const { point_delta } = poolDetail; - - const BIN_SIZE = point_delta * SLOT_NUMBER; - - const len = dclUserPoints.length; - - const left_point = dclUserPoints[0].point; - console.log('left_point: ', left_point); - - const right_point = dclUserPoints[len - 1].point; - console.log('right_point: ', right_point); - - setLeftPoint(left_point); - - setRightPoint(right_point + BIN_SIZE); - - const calcBinAmount = new Big(right_point) - .plus(new Big(BIN_SIZE)) - .minus(left_point) - .div(new Big(BIN_SIZE)) - .toFixed(0); - - console.log('calcBinAmount: ', calcBinAmount); - - setBinAmount(calcBinAmount); - - setMaxBinAmount(calcBinAmount); - - const { decimals: token_y_decimals } = tokens[1]; - const { decimals: token_x_decimals } = tokens[0]; - - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - - const minPrice = getPriceByPoint(left_point, decimalRate); - - const maxPrice = getPriceByPoint(right_point + BIN_SIZE, decimalRate); - - const displayMinPrice = toPrecision(minPrice, 8); - - const displayMaxPrice = toPrecision(maxPrice, 8); - - setMinPrice(displayMinPrice); - setMaxPrice(displayMaxPrice); - }, [dclUserPoints]); - - useEffect(() => { - if (!poolDetail || !accountId) return; - getDclUserPoints(poolDetail.pool_id, SLOT_NUMBER, accountId).then((res) => { - setDclUserPoints(res); - }); - }, [accountId]); - - useEffect(() => { - if (removeType === 'all') { - setRemovePercentAmount('100'); - setBinAmount(maxBinAmount); - } - }, [removeType]); - - useEffect(() => { - if (removeType === 'all' || !poolDetail || !leftPoint || !rightPoint) - return; - - const { point_delta, token_x, token_y } = poolDetail; - - if (removeType === 'left') { - // set left price to left poin - - let decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - console.log('decimalRate11: ', decimalRate); - - const raw_left_price = getPriceByPoint(leftPoint, decimalRate); - console.log('raw_left_price: ', raw_left_price); - - const left_price = toPrecision(raw_left_price, 8); - console.log('left_price: ', left_price); - - const c_right_point = leftPoint + BIN_SIZE * Number(binAmount); + const [minBoxPrice, setMinBoxPrice] = useState(''); + const [maxBoxPrice, setMaxBoxPrice] = useState(''); + const [minBoxPoint, setMinBoxPoint] = useState(); + const [maxBoxPoint, setMaxBoxPoint] = useState(); + const [binBoxAmount, setBinBoxAmount] = useState(''); - console.log('c_right_point: ', c_right_point); - - const raw_right_price = getPriceByPoint(c_right_point, decimalRate); - console.log('raw_right_price: ', raw_right_price); - - const text_right_point = getPointByPrice( - point_delta, - raw_right_price, - 1 / decimalRate - ); - console.log('text_right_point: ', text_right_point); - - const right_price = toPrecision(raw_right_price, 8); - - setMaxPrice(right_price); - - const removePercent = new Big(c_right_point) - .minus(leftPoint) - .div(new Big(rightPoint).minus(leftPoint)) - .mul(100) - .toFixed(0); - console.log('removePercent: ', removePercent); - - setRemovePercentAmount(removePercent); - - setMinPrice(left_price); - } - }, [binAmount, poolDetail]); - - // const [removePercentList] = useState([0, 25, 50, 75, 100]); - const { globalState } = useContext(WalletContext); - const v3PoolRemoveRef = useRef(null); - const sliderRef = useRef(null); - const isSignedIn = globalState.isSignedIn; useEffect(() => { + // init if (tokens && poolDetail && listLiquidities) { - // const { current_point } = poolDetail; - // const { left_point, right_point } = userLiquidity; - // if (current_point >= left_point && right_point > current_point) { - // setIsInrange(true); - // } else { - // setIsInrange(false); - // } - get_liquidity_x_y(); - getLiquidityAmount(); + get_user_points_range(); } - }, [tokens, userLiquidity, poolDetail, listLiquidities]); + }, [tokens, poolDetail, listLiquidities]); useEffect(() => { - if (liquidityAmount) { - changeRemoveAmount('100'); + if (minBoxPoint && maxBoxPoint) { + const bin_amount = get_bin_amount_by_points(minBoxPoint, maxBoxPoint); + setBinBoxAmount(bin_amount); } - }, [liquidityAmount]); - useEffect(() => { - if (v3PoolRemoveRef.current) { - v3PoolRemoveRef.current.style.backgroundSize = `${removePercentAmount}% 100%`; - } - if (sliderRef.current) { - sliderRef.current.style.left = `${+removePercentAmount}%`; - const marginLeft = -13 - (20 * +removePercentAmount) / 100; - sliderRef.current.style.marginLeft = `${marginLeft}px`; + }, [minBoxPoint, maxBoxPoint]); + const [ + min_received_x_amount, + min_received_y_amount, + min_received_total_value, + ] = useMemo(() => { + if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { + const { total_token_x_amount, total_token_y_amount, total_value } = + get_minimum_received_data(); + return [ + formatNumber(total_token_x_amount), + formatNumber(total_token_y_amount), + formatWithCommas_usd(total_value), + ]; } - }, [removePercentAmount]); - - const step = - !leftPoint || !rightPoint - ? 'any' - : new Big(100) - .div(new Big(rightPoint).minus(new Big(leftPoint)).div(BIN_SIZE)) - .toFixed(); - - function get_liquidity_x_y() { - const [tokenX, tokenY] = tokenMetadata_x_y; - - const { tokenXAmount, tokenYAmount } = listLiquidities.reduce( - (acc, userLiquidity) => { - const { left_point, right_point, amount: L } = userLiquidity; - const { current_point } = poolDetail; - - let curTokenXAmount = '0'; - let curTokenYAmount = '0'; - - // in range - if (current_point >= left_point && right_point > current_point) { - curTokenYAmount = getY(left_point, current_point, L, tokenY); - curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); - const { amountx, amounty } = get_X_Y_In_CurrentPoint( - tokenX, - tokenY, - L - ); - - return { - tokenXAmount: new Big(acc.tokenXAmount) - .plus(curTokenXAmount) - .plus(amountx) - .toFixed(), - tokenYAmount: new Big(acc.tokenYAmount) - .plus(curTokenYAmount) - .plus(amounty) - .toFixed(), - }; - - // setTokenXAmount(new BigNumber(tokenXAmount).plus(amountx).toFixed()); - // setTokenYAmount(new BigNumber(tokenYAmount).plus(amounty).toFixed()); - } - // only y token - if (current_point >= right_point) { - curTokenYAmount = getY(left_point, right_point, L, tokenY); - // setTokenYAmount(tokenYAmount); - return { - ...acc, - tokenYAmount: new Big(acc.tokenYAmount) - .plus(curTokenYAmount) - .toFixed(), - }; + return ['0', '0', '$0']; + }, [ + tokenPriceList, + tokenMetadata_x_y, + minBoxPoint, + maxBoxPoint, + slippageTolerance, + ]); + /** + * NOTE 删除一个点的场景暂时不考虑 + * @returns + */ + function get_will_deleted_nfts() { + let whole_deleted_nfts: UserLiquidityInfo[] = []; + let broken_deleted_nfts: UserLiquidityInfo[] = []; + if (removeType == 'all') { + whole_deleted_nfts = [].concat(listLiquidities); + } else if (removeType == 'left') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (left_point < maxBoxPoint) { + // 之前:left_point <= maxBoxPoint + if (right_point <= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } } - // only x token - if (left_point > current_point) { - curTokenXAmount = getX(left_point, right_point, L, tokenX); - // setTokenXAmount(tokenXAmount); - - return { - ...acc, - tokenXAmount: new Big(acc.tokenXAmount) - .plus(curTokenXAmount) - .toFixed(), - }; + }); + } else if (removeType == 'right') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (right_point > minBoxPoint) { + if (left_point >= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } } - }, - { - tokenXAmount: '0', - tokenYAmount: '0', - } - ); - - setTokenXAmount(tokenXAmount); - setTokenYAmount(tokenYAmount); - } - - // no change - function getLiquidityPrice() { - if (tokenPriceList && tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const priceX = tokenPriceList[tokenX.id]?.price || 0; - const priceY = tokenPriceList[tokenY.id]?.price || 0; - const tokenYTotalPrice = new BigNumber(tokenYAmount || 0).multipliedBy( - priceY - ); - const tokenXTotalPrice = new BigNumber(tokenXAmount || 0).multipliedBy( - priceX - ); - let total_price = tokenYTotalPrice.plus(tokenXTotalPrice); - total_price = new BigNumber(removePercentAmount) - .multipliedBy(total_price) - .dividedBy(100); - if (total_price.isEqualTo(0)) { - return '$0'; - } else if (total_price.isLessThan('0.01')) { - return '$<0.01'; - } else { - return `$` + formatWithCommas(toPrecision(total_price.toFixed(), 2)); - } + }); } + return { + whole_deleted_nfts, + broken_deleted_nfts, + }; } - function getLiquidityAmount() { - const liquidityAmountRaw = listLiquidities - .reduce( - (pre, cur) => { - const { amount } = cur; - - return pre.plus(new Big(amount)); - }, - - new Big(0) - ) + function get_slot_number_in_a_bin() { + const pool_id = poolDetail?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + function get_user_points_range() { + const user_points: number[] = []; + listLiquidities.forEach((l: UserLiquidityInfo) => { + user_points.push(l.left_point, l.right_point); + }); + user_points.sort((b, a) => b - a); + const min_point = get_bin_point_by_point(user_points[0], 'floor'); + const max_point = get_bin_point_by_point( + user_points[user_points.length - 1], + 'ceil' + ); + const min_price = get_bin_price_by_point(min_point); + const max_price = get_bin_price_by_point(max_point); + + const max_bin_amount = get_bin_amount_by_points(min_point, max_point); + + setMinPoint(min_point); + setMaxPoint(max_point); + setMinPrice(min_price); + setMaxPrice(max_price); + setMaxBinAmount(max_bin_amount); + + setMinBoxPrice(min_price); + setMaxBoxPrice(max_price); + setMinBoxPoint(min_point); + setMaxBoxPoint(max_point); + setBinBoxAmount(max_bin_amount); + } + function get_bin_amount_by_points(left_point: number, right_point: number) { + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + const bin_amount = Big(right_point - left_point) + .div(binWidth) .toFixed(); - - console.log('liquidityAmountRaw: ', liquidityAmountRaw); - - setLiquidityAmount(liquidityAmountRaw); + return bin_amount; } - - function handlePriceToAppropriatePoint() { - const { point_delta, token_x, token_y } = poolDetail; - - const { decimals: token_y_decimals } = tokens[1]; - const { decimals: token_x_decimals } = tokens[0]; - - let decimalRate = + function get_bin_price_by_point(point: number) { + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(point, decimalRate); + return price; + } + function get_bin_point_by_price(price: string) { + const point_delta = poolDetail.point_delta; + const decimalRate = Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - console.log('decimalRate: ', decimalRate); - - if (minPrice && changeType == 'min') { - console.log('minPrice: ', minPrice); - - let c_point = getPointByPrice(point_delta, minPrice, 1 / decimalRate); - console.log('c_point: ', c_point); - console.log('leftPoint: ', leftPoint); - - if (c_point >= rightPoint) { - c_point = rightPoint - BIN_SIZE; - } - if (c_point < leftPoint) { - c_point = leftPoint; - } - - if ( - !new Big(c_point) - .minus(new Big(leftPoint)) - .div(BIN_SIZE || 1) - .eq(0) - ) { - const new_point = - Math.floor((c_point - leftPoint) / BIN_SIZE) * BIN_SIZE + leftPoint; - - console.log('new_point_min: ', new_point); - - const new_min_price = getPriceByPoint(new_point, decimalRate); - console.log('new_min_price: ', new_min_price); - - setMinPrice(new_min_price); - } - - // formatted point + const point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return point; + } + function get_bin_point_by_point( + point: number, + type: 'round' | 'floor' | 'ceil' + ) { + const point_delta = poolDetail.point_delta; + const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); + return bin_point; + } + function handleMinBoxPriceToAppropriatePoint() { + /** + * min price <= price < max box price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(minBoxPrice || 0); + if (big_price.lt(minPrice)) { + appropriate_price = minPrice; + appropriate_point = minPoint; + } else if (big_price.gt(maxBoxPrice)) { + appropriate_price = maxBoxPrice; + appropriate_point = maxBoxPoint; + } else { + appropriate_point = get_bin_point_by_price(minBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); } - console.log('maxPrice: ', maxPrice); - - if (maxPrice && changeType == 'max') { - console.log('maxPrice: ', maxPrice); - - let c_point = getPointByPrice(point_delta, maxPrice, 1 / decimalRate); - console.log('right_point', rightPoint); - console.log('c_point_max: ', c_point); - if (c_point > rightPoint) { - c_point = rightPoint; - } - if (c_point <= leftPoint) { - c_point = leftPoint + BIN_SIZE; - } - - if ( - new Big(c_point) - .minus(new Big(leftPoint)) - .div(BIN_SIZE || 1) - .eq(0) - ) { - return; - } else { - const new_point = - Math.floor((c_point - leftPoint) / BIN_SIZE) * BIN_SIZE + leftPoint; - - console.log('new_point_max: ', new_point); - - const new_max_price = getPriceByPoint(new_point, decimalRate); - console.log('new_max_price: ', new_max_price); - setMaxPrice(new_max_price); - } - - // formatted point + setMinBoxPrice(appropriate_price); + setMinBoxPoint(appropriate_point); + } + function handleMaxBoxPriceToAppropriatePoint() { + /** + * min box price <= price <= max price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(maxBoxPrice || 0); + if (big_price.lt(minBoxPrice)) { + appropriate_price = minBoxPrice; + appropriate_point = minBoxPoint; + } else if (big_price.gt(maxPrice)) { + appropriate_price = maxPrice; + appropriate_point = maxPoint; + } else { + appropriate_point = get_bin_point_by_price(maxBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); } + setMaxBoxPrice(appropriate_price); + setMaxBoxPoint(appropriate_point); } - - function priceToBinBoundry(amount: string) {} - - function displayLiquidityAmount() { - if (liquidityAmount) { - return toPrecision(liquidityAmount, 3); + /** + * 左右点位改变会触发bin amount随之更改 + * bin amount 修改会改变可以修改的点位 + * 0 < bin amount < max bin amount + */ + function handleBinAmountToAppropriateAmount() { + const amount_int = +binBoxAmount; + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + let appropriate_amount = amount_int; + if (amount_int < 1) { + appropriate_amount = 1; + } else if (amount_int > +maxBinAmount) { + appropriate_amount = +maxBinAmount; } + if (removeType == 'left') { + const right_box_point = minPoint + binWidth * appropriate_amount; + const right_box_price = get_bin_price_by_point(right_box_point); + setMaxBoxPoint(right_box_point); + setMaxBoxPrice(right_box_price); + } else if (removeType == 'right') { + const left_box_point = maxPoint - binWidth * appropriate_amount; + const left_box_price = get_bin_price_by_point(left_box_point); + setMinBoxPoint(left_box_point); + setMinBoxPrice(left_box_price); + } + setBinBoxAmount(appropriate_amount.toString()); } function getY( leftPoint: number, @@ -524,168 +367,244 @@ export const RemovePoolV3 = (props: any) => { ); return { amountx: amountX_read, amounty: amountY_read }; } + function batch_remove_nfts() { + setRemoveLoading(true); + const [tokenX, tokenY] = tokenMetadata_x_y; + sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); + let batch_remove_liquidity: IRemoveLiquidityInfo[]; + let batch_update_liquidity: IBatchUpdateiquidityInfo; + let mint_liquidities: UserLiquidityInfo[] = []; + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + const { pool_id } = poolDetail; + /** + * step1 找到被截断的nft的 未截断的区间 + * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 + * step3 未截断的区间 和 token 数量作为 添加nft的参数 + */ + if (broken_deleted_nfts.length) { + const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + addLiquidityInfoList.push({ + pool_id, + left_point: new_left_point, + right_point: new_right_point, + amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), + amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), + min_amount_x: '0', + min_amount_y: '0', + }); + removeLiquidityInfos.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); - function getPoolFee() { - if (poolDetail) { - return poolDetail.fee / 10000; + batch_update_liquidity = { + remove_liquidity_infos: removeLiquidityInfos, + add_liquidity_infos: addLiquidityInfoList, + }; } - return ''; - } - function changeRemoveAmount(value: string) { - setRemovePercentAmount(value); - const amount = new BigNumber(liquidityAmount) - .multipliedBy(value) - .dividedBy(100) - .toFixed(0, 1); - - setRemoveAmount(amount); - getMinimumInfo(amount); - - if (!value || !maxBinAmount) { - return; + if (whole_deleted_nfts.length) { + const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + batchRemoveLiquidity.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + batch_remove_liquidity = batchRemoveLiquidity; } - const newBinAmount = new Big(value).div(100).mul(maxBinAmount).toFixed(); - - setBinAmount(newBinAmount); - } - function getMinimumInfo(amount: string) { - if (liquidityAmount) { - const proportion = new BigNumber(amount || 0).dividedBy(liquidityAmount); - setRemoveTokenXAmount( - proportion.multipliedBy(tokenXAmount || '0').toFixed() - ); - setRemoveTokenYAmount( - proportion.multipliedBy(tokenYAmount || '0').toFixed() - ); - } + batch_remove_liquidity_contract({ + token_x: tokenX, + token_y: tokenY, + batch_remove_liquidity, + batch_update_liquidity, + mint_liquidities, + }); } - function getMinTokenAmount() { - const rate = 100 - slippageTolerance; - const result: any = {}; - if (removeTokenXAmount) { - const minX = new BigNumber(removeTokenXAmount || 0).multipliedBy( - rate / 100 - ); - let displayX = ''; - if (minX.isEqualTo(0)) { - displayX = '0'; - } else if (minX.isLessThan(0.001)) { - displayX = '<0.001'; - } else { - displayX = toPrecision(minX.toFixed(), 3); - } - result.minX = minX.toFixed(); - result.displayX = displayX; + function get_minimum_received_data() { + /** + * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 + * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 + * step3 把上述step1, step2 得到的x,y 累加起来即可 + */ + let total_token_x_amount = Big(0); + let total_token_y_amount = Big(0); + let total_value = Big(0); + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + if (whole_deleted_nfts.length) { + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + total_token_x_amount = total_token_x_amount.plus( + min_token_x_amount || 0 + ); + total_token_y_amount = total_token_y_amount.plus( + min_token_y_amount || 0 + ); + }); } - if (removeTokenYAmount) { - const minY = new BigNumber(removeTokenYAmount || 0).multipliedBy( - rate / 100 - ); - let displayY = ''; - if (minY.isEqualTo(0)) { - displayY = '0'; - } else if (minY.isLessThan(0.001)) { - displayY = '<0.001'; - } else { - displayY = toPrecision(minY.toFixed(), 3); - } - result.minY = minY.toFixed(); - result.displayY = displayY; + if (broken_deleted_nfts.length) { + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const [new_left_point, new_right_point] = get_deleted_range(l); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount: l.amount, + }); + total_token_x_amount = total_token_x_amount.plus( + min_token_x_amount || 0 + ); + total_token_y_amount = total_token_y_amount.plus( + min_token_y_amount || 0 + ); + }); } if (tokenPriceList && tokenMetadata_x_y) { const [tokenX, tokenY] = tokenMetadata_x_y; const priceX = tokenPriceList[tokenX.id]?.price || 0; const priceY = tokenPriceList[tokenY.id]?.price || 0; - const tokenXTotalPrice = new BigNumber(result.minX || 0).multipliedBy( - priceX - ); - const tokenYTotalPrice = new BigNumber(result.minY || 0).multipliedBy( - priceY - ); - const total_price = tokenYTotalPrice.plus(tokenXTotalPrice); - if (total_price.isEqualTo(0)) { - result.minPrice = '$0'; - } else if (total_price.isLessThan('0.001')) { - result.minPrice = '<$0.001'; - } else { - result.minPrice = `$` + toPrecision(total_price.toFixed(), 3); - } + const token_x_value = total_token_x_amount.mul(priceX); + const token_y_value = total_token_y_amount.mul(priceY); + total_value = token_x_value.plus(token_y_value); } - return result; + return { + total_token_x_amount: total_token_x_amount.toFixed(), + total_token_y_amount: total_token_y_amount.toFixed(), + total_value: total_value.toFixed(), + }; } - - function remove() { - setRemoveLoading(true); - const [tokenX, tokenY] = tokenMetadata_x_y; - const { lpt_id, mft_id } = userLiquidity; - - sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); - - // replace to batch remove lp - remove_liquidity({ - token_x: tokenX, - token_y: tokenY, - lpt_id, - mft_id, - amount: removeAmount, - min_amount_x: toNonDivisibleNumber(tokenX.decimals, MINDATA.minX), - min_amount_y: toNonDivisibleNumber(tokenY.decimals, MINDATA.minY), - isLegacy, - }); + function get_un_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.max(left_point, minBoxPoint); + const intersection_r = Math.min(right_point, maxBoxPoint); + // intersection part + let un_intersection_l; + let un_intersection_r; + if (removeType == 'left') { + un_intersection_l = intersection_r; + un_intersection_r = right_point; + } else if (removeType == 'right') { + un_intersection_l = left_point; + un_intersection_r = intersection_l; + } + return [un_intersection_l, un_intersection_r]; } - function switchRate() { - setRateDirection(!rateDirection); + function get_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.max(left_point, minBoxPoint); + const intersection_r = Math.min(right_point, maxBoxPoint); + return [intersection_l, intersection_r]; } - function getCurrentPrice(type: string) { - if (poolDetail && tokenMetadata_x_y) { - const { current_point } = poolDetail; - const [tokenX, tokenY] = tokenMetadata_x_y; - const rate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - let price = getPriceByPoint(current_point, rate); - if (type == 'l') { - price = new BigNumber('1').dividedBy(price).toFixed(); - } - - const price_big = new BigNumber(price); - if (price_big.isLessThan('0.001')) { - return '<0.001'; - } else { - return toPrecision(price, 6); - } + function get_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { left_point, right_point, amount: L } = liquidity; + const { current_point } = poolDetail; + let curTokenXAmount = '0'; + let curTokenYAmount = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); + curTokenYAmount = getY(left_point, current_point, L, tokenY); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + curTokenXAmount = Big(amountx || '0') + .plus(curTokenXAmount || '0') + .toFixed(); + curTokenYAmount = Big(amounty || '0') + .plus(curTokenYAmount || '0') + .toFixed(); } - } - function getTokenPrice(type: string) { - if (tokenPriceList && tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - if (type == 'l') { - return tokenPriceList[tokenX.id]?.price || '-'; - } - if (type == 'r') { - return tokenPriceList[tokenY.id]?.price || '-'; - } + // only y token + if (current_point >= right_point) { + curTokenYAmount = getY(left_point, right_point, L, tokenY); } - return '-'; + // only x token + if (left_point > current_point) { + curTokenXAmount = getX(left_point, right_point, L, tokenX); + } + return [curTokenXAmount, curTokenYAmount]; } - const MINDATA: { - minX: string; - displayX: string; - minY: string; - displayY: string; - minPrice: string; - } = getMinTokenAmount(); - const isRemoveLiquidityDisabled = !( - +removeAmount > 0 && - new BigNumber(removeAmount || 0).isLessThanOrEqualTo(liquidityAmount || 0) - ); - + function get_min_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const rate = (100 - slippageTolerance) / 100; + const [token_x_amount, token_y_amount] = + get_x_y_amount_of_liquidity(liquidity); + const min_token_x_amount = Big(token_x_amount || 0) + .mul(rate) + .toFixed(); + const min_token_y_amount = Big(token_y_amount || 0) + .mul(rate) + .toFixed(); + return [min_token_x_amount, min_token_y_amount]; + } + const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; return ( + {/* Title */}
@@ -694,6 +613,7 @@ export const RemovePoolV3 = (props: any) => {
+ {/* Symbol pairs */}
@@ -710,9 +630,11 @@ export const RemovePoolV3 = (props: any) => { {tokens[0]?.symbol}/{tokens[1]?.symbol}
- {getLiquidityPrice()} + + {min_received_total_value} +
- + {/* Removing way */}
{ e.preventDefault(); e.stopPropagation(); setRemoveType('left'); + setMinBoxPrice(minPrice); + setMinBoxPoint(minPoint); }} > { e.preventDefault(); e.stopPropagation(); setRemoveType('right'); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(maxPoint); }} > { e.preventDefault(); e.stopPropagation(); setRemoveType('all'); + setMinBoxPrice(minPrice); + setMinBoxPoint(minPoint); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(maxPoint); }} > {
- -
- {/*
- {removePercentList.map((p) => { - return ( -
{ - if (isLegacy) return; - changeRemoveAmount(p.toString()); - }} - > - - {p}% - - -
- ); - })} -
*/} + {/* remove slider todo */} + {/*
{ @@ -819,21 +721,10 @@ export const RemovePoolV3 = (props: any) => { max="100" step={step} /> - {/*
- - - {toPrecision(removePercentAmount.toString(), 0)}% - -
*/} -
- -
+
*/} + {/* Set points */} +
{/* min price */} -
{ className={`ml-2 font-gothamBold ${ removeType !== 'right' ? 'text-primaryText' : 'text-white' }`} - min={0} - max={maxPrice} - value={minPrice} + value={minBoxPrice} onChange={(e) => { const value = e.target.value; - setChangeType('min'); - setMinPrice(value); + setMinBoxPrice(value); }} inputMode="decimal" onBlur={() => { - handlePriceToAppropriatePoint(); + handleMinBoxPriceToAppropriatePoint(); }} disabled={removeType !== 'right'} >
- + {/* max price */}
{ }`} onChange={(e) => { const value = e.target.value; - setChangeType('max'); - setMaxPrice(value); + setMaxBoxPrice(value); }} - min={0} - value={maxPrice} + value={maxBoxPrice} inputMode="decimal" onBlur={() => { - handlePriceToAppropriatePoint(); + handleMaxBoxPriceToAppropriatePoint(); }} + disabled={removeType !== 'left'} >
- + {/* bin amount */}
{ + setBinBoxAmount(v); + }} + onBlur={handleBinAmountToAppropriateAmount} disabled={removeType === 'all'} />
- + {/* Slippage */}
{ textColor="text-white" />
+ {/* Minimum received */}
@@ -925,7 +816,7 @@ export const RemovePoolV3 = (props: any) => { > - {MINDATA.displayX || '-'} + {min_received_x_amount}
{ > - {MINDATA.displayY || '-'} + {min_received_y_amount}
- + {/* Button */} {isSignedIn ? ( { + onBlur && onBlur(); + }} onChange={({ target }) => { handleChange(target.value); }} diff --git a/src/pages/poolsV3/interfaces.tsx b/src/pages/poolsV3/interfaces.tsx index 45f8c8529..bed4904e7 100644 --- a/src/pages/poolsV3/interfaces.tsx +++ b/src/pages/poolsV3/interfaces.tsx @@ -7,7 +7,7 @@ export interface IAddLiquidityInfo { min_amount_x: string; min_amount_y: string; } -export interface IaddLiquidityInfoHelp { +export interface IAddLiquidityInfoHelp { [key: number]: { left_point: number; right_point: number; @@ -17,3 +17,14 @@ export interface IaddLiquidityInfoHelp { export type LiquidityShape = 'Spot' | 'Curve' | 'BidAsk'; export type PriceRangeModeType = 'by_range' | 'by_radius'; +export interface IRemoveLiquidityInfo { + lpt_id: string; + amount: string; + min_amount_x: string; + min_amount_y: string; +} + +export interface IBatchUpdateiquidityInfo { + remove_liquidity_infos: IRemoveLiquidityInfo[]; + add_liquidity_infos: IAddLiquidityInfo[]; +} diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index f6c3bc075..037584169 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -15,7 +15,7 @@ import { ONLY_ZEROS, } from '../utils/numbers'; import { getCurrentWallet } from '../utils/wallets-integration'; -import _ from 'lodash'; +import _, { forEach } from 'lodash'; import Big from 'big.js'; import { Transaction, @@ -34,11 +34,15 @@ import { import { WRAP_NEAR_CONTRACT_ID, nearMetadata } from '../services/wrap-near'; import { registerAccountOnToken } from './creators/token'; import { nearDepositTransaction, nearWithdrawTransaction } from './wrap-near'; -import { getPointByPrice } from './commonV3'; +import { UserLiquidityInfo, getPointByPrice } from './commonV3'; import { toPrecision } from '../utils/numbers'; import { REF_DCL_POOL_CACHE_KEY } from '../state/swap'; import { REF_UNI_SWAP_CONTRACT_ID } from './near'; -import { IAddLiquidityInfo } from '../pages/poolsV3/interfaces'; +import { + IAddLiquidityInfo, + IBatchUpdateiquidityInfo, + IRemoveLiquidityInfo, +} from '../pages/poolsV3/interfaces'; import getConfig from './config'; const LOG_BASE = 1.0001; @@ -1274,6 +1278,126 @@ export const remove_liquidity = async ({ } return executeMultipleTransactions(transactions); }; +export const batch_remove_liquidity_contract = async ({ + token_x, + token_y, + batch_remove_liquidity, + batch_update_liquidity, + mint_liquidities, +}: { + token_x: TokenMetadata; + token_y: TokenMetadata; + batch_remove_liquidity: IRemoveLiquidityInfo[]; + batch_update_liquidity: IBatchUpdateiquidityInfo; + mint_liquidities: UserLiquidityInfo[]; +}) => { + const max_number = 20; + const transactions: Transaction[] = []; + if (mint_liquidities.length) { + const functionCallsV3: any = []; + mint_liquidities.forEach((l: UserLiquidityInfo) => { + functionCallsV3.push({ + methodName: 'burn_v_liquidity', + args: { + lpt_id: l.lpt_id, + }, + gas: '20000000000000', + }); + }); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: functionCallsV3, + }); + } + if (batch_remove_liquidity) { + const length = batch_remove_liquidity.length; + const ts_length = Math.ceil(length / max_number); + for (let i = 0; i < ts_length; i++) { + let batch_remove_liquidity_i; + const startIndex = i * max_number; + const endIndex = startIndex + max_number; + batch_remove_liquidity_i = batch_remove_liquidity.slice( + startIndex, + endIndex + ); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_remove_liquidity', + args: { + remove_liquidity_infos: batch_remove_liquidity_i, + }, + gas: '250000000000000', + }, + ], + }); + } + } + if (batch_update_liquidity) { + const { add_liquidity_infos, remove_liquidity_infos } = + batch_update_liquidity; + const length = add_liquidity_infos.length; + const ts_length = Math.ceil(length / (max_number / 2)); + for (let i = 0; i < ts_length; i++) { + let batch_update_liquidity_i; + const startIndex = i * max_number; + const endIndex = startIndex + max_number; + batch_update_liquidity_i = { + add_liquidity_infos: add_liquidity_infos.slice(startIndex, endIndex), + remove_liquidity_infos: remove_liquidity_infos.slice( + startIndex, + endIndex + ), + }; + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_update_liquidity', + args: batch_update_liquidity_i, + gas: '250000000000000', + }, + ], + }); + } + } + + const ftBalance_x = await ftGetStorageBalance(token_x.id); + if (!ftBalance_x) { + transactions.unshift({ + receiverId: token_x.id, + functionCalls: [ + storageDepositAction({ + registrationOnly: true, + amount: STORAGE_TO_REGISTER_WITH_MFT, + }), + ], + }); + } + const ftBalance_y = await ftGetStorageBalance(token_y.id); + if (!ftBalance_y) { + transactions.unshift({ + receiverId: token_y.id, + functionCalls: [ + storageDepositAction({ + registrationOnly: true, + amount: STORAGE_TO_REGISTER_WITH_MFT, + }), + ], + }); + } + const neededStorage = await checkTokenNeedsStorageDeposit_v3(); + if (neededStorage) { + transactions.unshift({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + storageDepositAction({ amount: neededStorage, registrationOnly: true }), + ], + }); + } + return executeMultipleTransactions(transactions); +}; export const claim_all_liquidity_fee = async ({ token_x, From 4d25aba85127144f003cccb67228d55221c5b9ec Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 28 Jun 2023 09:07:16 +0800 Subject: [PATCH 040/204] update add liquidity page --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 91 ++++++++++++-------- 1 file changed, 54 insertions(+), 37 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 2795769e1..4570b2d33 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -167,6 +167,8 @@ export default function AddYourLiquidityPageV3() { const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); const [token_amount_tip, set_token_amount_tip] = useState(); + const [only_suppport_spot_shape, set_only_suppport_spot_shape] = + useState(false); // callBack handle useAddAndRemoveUrlHandle(); @@ -178,7 +180,6 @@ export default function AddYourLiquidityPageV3() { const isSignedIn = globalState.isSignedIn; const nearBalance = useDepositableBalance('NEAR'); const intl = useIntl(); - const intl_select = intl.formatMessage({ id: 'select_s' }); const OPEN_CREATE_POOL_ENTRY = false; useEffect(() => { @@ -243,17 +244,6 @@ export default function AddYourLiquidityPageV3() { getTopPairs(); } }, [listPool, tokenPriceList]); - // trigger - useEffect(() => { - if ( - liquidityShape === 'Spot' && - !ONLY_ZEROS.test(tokenXAmount) && - currentSelectedPool - ) { - changeTokenXAmount(tokenXAmount); - } - }, [liquidityShape]); - // new useEffect(() => { // init @@ -266,6 +256,33 @@ export default function AddYourLiquidityPageV3() { setCurrentPoint(current_point); } }, [currentSelectedPool, tokenX, tokenY]); + // 如果只有一个 bin 且 双边 则只允许设置成spot模式 + useEffect(() => { + set_only_suppport_spot_shape(false); + if (currentSelectedPool) { + const { point_delta } = currentSelectedPool; + if (leftPoint <= currentPoint && rightPoint > currentPoint) { + // inrange + const binWidth = SLOT_NUMBER * point_delta; + const binNumber = (rightPoint - leftPoint) / binWidth; + if (binNumber == 1) { + setLiquidityShape('Spot'); + set_only_suppport_spot_shape(true); + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount); + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount); + } + } + } + } + }, [ + leftPoint, + rightPoint, + currentPoint, + currentSelectedPool, + liquidityShape, + ]); async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -478,30 +495,24 @@ export default function AddYourLiquidityPageV3() { const sort = tokenX.id == token_x; setTokenXAmount(amount); /*if (sort) {*/ - if (!onlyAddXToken) { + if (!onlyAddXToken && liquidityShape === 'Spot') { const amount_result = getTokenYAmountByCondition({ amount, leftPoint: leftPoint, rightPoint: rightPoint, currentPoint: currentPoint, }); - - if (liquidityShape === 'Spot') { - setTokenYAmount(amount_result); - } + setTokenYAmount(amount_result); } /*} else { - if (!onlyAddYToken) { + if (!onlyAddYToken && liquidityShape === 'Spot') { const amount_result = getTokenXAmountByCondition({ amount, leftPoint, rightPoint, currentPoint, }); - - if (liquidityShape === 'Spot') { - setTokenYAmount(amount_result); - } + setTokenYAmount(amount_result); } }*/ } @@ -510,28 +521,24 @@ export default function AddYourLiquidityPageV3() { const sort = tokenX.id == token_x; setTokenYAmount(amount); /*if (sort) {*/ - if (!onlyAddYToken) { + if (!onlyAddYToken && liquidityShape === 'Spot') { const amount_result = getTokenXAmountByCondition({ amount, leftPoint, rightPoint, currentPoint, }); - if (liquidityShape === 'Spot') { - setTokenXAmount(amount_result); - } + setTokenXAmount(amount_result); } /*} else { - if (!onlyAddXToken) { + if (!onlyAddXToken && liquidityShape === 'Spot') { const amount_result = getTokenYAmountByCondition({ amount, leftPoint: leftPoint, rightPoint: rightPoint, currentPoint: currentPoint, }); - if (liquidityShape === 'Spot') { - setTokenXAmount(amount_result); - } + setTokenXAmount(amount_result); } }*/ } @@ -1060,9 +1067,18 @@ export default function AddYourLiquidityPageV3() {
{[SpotShape, CurveShape, BidAskShape].map( (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } return (
@@ -1138,7 +1156,6 @@ export default function AddYourLiquidityPageV3() { ); } /** - * 如果只选择了一个bin且是双边的话,那只能选择spot模式 todo 待处理 * 双边 最小token数量不满足 提示 * 双边 一侧token 数量太多 提示 todo * @returns @@ -2699,9 +2716,9 @@ function SetPointsComponent() { Liquidity Date: Wed, 28 Jun 2023 20:35:30 +0800 Subject: [PATCH 041/204] add slider for remove liquidity --- src/components/pool/RemovePoolV3.tsx | 48 +++++++++++++--------------- src/global.css | 44 ++++++++++--------------- 2 files changed, 40 insertions(+), 52 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index f5df62100..9960012cf 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -4,7 +4,7 @@ import { WalletContext } from '../../utils/wallets-integration'; import { Card } from '~components/card/Card'; import { ModalClose } from '~components/icon'; import { TokenMetadata } from '../../services/ft-contract'; -import { Slider } from '~components/icon/V3'; +import ReactSlider from 'react-slider'; import { GradientButton, ButtonTextWrapper, @@ -112,6 +112,11 @@ export const RemovePoolV3 = (props: any) => { setBinBoxAmount(bin_amount); } }, [minBoxPoint, maxBoxPoint]); + useEffect(() => { + if (binBoxAmount !== '') { + handleBinAmountToAppropriateAmount(+binBoxAmount); + } + }, [binBoxAmount]); const [ min_received_x_amount, min_received_y_amount, @@ -284,16 +289,14 @@ export const RemovePoolV3 = (props: any) => { /** * 左右点位改变会触发bin amount随之更改 * bin amount 修改会改变可以修改的点位 - * 0 < bin amount < max bin amount + * 0 <= bin amount < max bin amount */ - function handleBinAmountToAppropriateAmount() { - const amount_int = +binBoxAmount; + function handleBinAmountToAppropriateAmount(point: number) { + const amount_int = point || +binBoxAmount; const { point_delta } = poolDetail; const binWidth = SLOT_NUMBER * point_delta; let appropriate_amount = amount_int; - if (amount_int < 1) { - appropriate_amount = 1; - } else if (amount_int > +maxBinAmount) { + if (amount_int > +maxBinAmount) { appropriate_amount = +maxBinAmount; } if (removeType == 'left') { @@ -704,24 +707,19 @@ export const RemovePoolV3 = (props: any) => {
{/* remove slider todo */} - {/*
- { - changeRemoveAmount(e.target.value); - }} - disabled={removeType === 'all' || isLegacy ? true : false} - value={removePercentAmount} - type="range" - className={`w-full ${ - isLegacy ? 'pause cursor-not-allowed' : 'cursor-pointer' - }`} - style={{ backgroundSize: '100% 100%' }} - min="0" - max="100" - step={step} - /> -
*/} + {/* binBoxAmount 控制 */} + { + setBinBoxAmount(v.toString()); + }} + value={+binBoxAmount} + min={0} + max={+maxBinAmount} + step={1} + > {/* Set points */}
{/* min price */} diff --git a/src/global.css b/src/global.css index aa8db163f..06f7eef2c 100644 --- a/src/global.css +++ b/src/global.css @@ -1296,48 +1296,38 @@ input[type='range']::-webkit-slider-runnable-track { display: none; } +/* slider start*/ .multi-slider { - height: 10px; - border-radius: 6px; - background: #121e27; + height: 22px; } .multi-slider .thumb { - width: 21px; - height: 21px; + width: 22px; + height: 22px; cursor: pointer; - background: white; border-radius: 100%; border: 3px solid #1d2932; outline: none; background: #00d6af; } - -.thumb-0 { - top: -5px; - transform: translateX(-50%); -} - -.thumb-1 { - top: -5px; - transform: translateX(50%); -} - -.invert .thumb-0 { - top: -5px; - transform: translateX(50%); +.multi-slider.disabled .thumb { + cursor: not-allowed; } - -.invert .thumb-1 { - top: -5px; - transform: translateX(-50%); +.multi-slider .track-0 { + height: 10px; + top: 6px; + border-radius: 6px; + background-color: #00c6a2; } - -.track-1 { - background-color: #00d6af; +.multi-slider .track-1 { height: 10px; + top: 6px; + border-radius: 6px; + background-color: #121e27; } +/* slider start*/ + .refresh-loader { animation: spin 1s linear infinite; transform-origin: center; From d2f1afafbf76aae472de9768441920177197466b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 29 Jun 2023 09:32:06 +0800 Subject: [PATCH 042/204] add slider for add and remove function --- src/components/d3Chart/DclChart.tsx | 4 +- src/components/pool/RemovePoolV3.tsx | 4 +- src/global.css | 33 +++- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 176 ++++++++++++++----- 4 files changed, 163 insertions(+), 54 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 6ee5ec361..ca818e424 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -198,7 +198,7 @@ export default function DclChart({ } }, [rightPoint]); useEffect(() => { - if (config?.radiusMode && config?.targetPoint) { + if (config?.radiusMode && config?.targetPoint && drawChartDone) { // hide drag bar and show target price bar draw_radius_mode_bar(); d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); @@ -208,7 +208,7 @@ export default function DclChart({ d3.select(`${randomId} .rightBar`).attr('style', ''); d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); } - }, [config?.radiusMode, config?.targetPoint, pool_id]); + }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 9960012cf..6aad79d3f 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -779,10 +779,8 @@ export const RemovePoolV3 = (props: any) => { { - setBinBoxAmount(v); - }} onBlur={handleBinAmountToAppropriateAmount} disabled={removeType === 'all'} /> diff --git a/src/global.css b/src/global.css index 06f7eef2c..090ad4682 100644 --- a/src/global.css +++ b/src/global.css @@ -1326,7 +1326,38 @@ input[type='range']::-webkit-slider-runnable-track { background-color: #121e27; } -/* slider start*/ +.multi-slider-double { + height: 22px; +} +.multi-slider-double .thumb { + width: 22px; + height: 22px; + cursor: pointer; + border-radius: 100%; + border: 3px solid #1d2932; + outline: none; + background: #00d6af; +} + +.multi-slider-double .track-0 { + height: 10px; + top: 6px; + border-radius: 6px; + background-color: #121e27; +} +.multi-slider-double .track-1 { + height: 10px; + top: 6px; + border-radius: 6px; + background-color: #00c6a2; +} +.multi-slider-double .track-2 { + height: 10px; + top: 6px; + border-radius: 6px; + background-color: #121e27; +} +/* slider end*/ .refresh-loader { animation: spin 1s linear infinite; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 4570b2d33..d82505195 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2564,8 +2564,9 @@ function SetPointsComponent() { const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' >('by_range'); + const SLIDER_BIN_NUMBER = 20; + const RADIUS_DEFAULT_NUMBER = 3; const [radius, setRadius] = useState(); - const [targetCustomPrice, setTargetCustomPrice] = useState(''); const [leftCustomPrice, setLeftCustomPrice] = useState(''); const [rightCustomPrice, setRightCustomPrice] = useState(''); @@ -2575,6 +2576,14 @@ function SetPointsComponent() { const [rightInputStatus, setRightInputStatus] = useState(false); const [targetInputStatus, setTargetInputStatus] = useState(false); const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + const [initDone, setInitDone] = useState(false); + + const [slider_point_min, set_slider_point_min] = useState(); + const [slider_point_max, set_slider_point_max] = useState(); + + const [slider_left_value, set_slider_left_value] = useState(); + const [slider_right_value, set_slider_right_value] = useState(); + const { token_x, token_y } = currentSelectedPool; const token_x_decimals = tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; @@ -2583,34 +2592,88 @@ function SetPointsComponent() { const tokenSort = tokenX.id == currentSelectedPool.token_x; // init useEffect(() => { + setInitDone(false); if (currentSelectedPool && tokenX && tokenY) { setTargetPoint(currentSelectedPool.current_point); - setRadius(3); + setRadius(RADIUS_DEFAULT_NUMBER); setPriceRangeMode('by_range'); setChartTab('liquidity'); + setInitDone(true); } }, [currentSelectedPool, tokenX, tokenY]); - // change event in radius mode useEffect(() => { - if (radius && +radius > 0 && targetPoint && BIN_WIDTH) { - const left_point_temp = handlePointToAppropriatePoint( - targetPoint - (radius - 1) * BIN_WIDTH - ); + if (initDone) { + set_slider_point_range(); + } + }, [initDone]); + /** + * change event in radius mode + * radius && targetPoint ===> left point right point ===> binNumber + */ + useEffect(() => { + if (targetPoint && BIN_WIDTH) { const right_point_temp = handlePointToAppropriatePoint( - targetPoint + (radius + 1) * BIN_WIDTH + targetPoint + radius * BIN_WIDTH ); + const left_point_temp = right_point_temp - BIN_WIDTH * radius * 2; + setLeftPoint(left_point_temp); setRightPoint(right_point_temp); } }, [radius, targetPoint, BIN_WIDTH, priceRangeMode]); useEffect(() => { - const diff = rightPoint - leftPoint; - const bin_number_temp = diff / BIN_WIDTH; - setBinNumber(bin_number_temp); - // effect right area - pointChange({ leftPoint, rightPoint, currentPoint }); + if (leftPoint && rightPoint) { + const diff = rightPoint - leftPoint; + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect right area + pointChange({ leftPoint, rightPoint, currentPoint }); + // effect slider + const slider_left_value = get_slider_value_by_point(leftPoint); + const slider_right_value = get_slider_value_by_point(rightPoint); + set_slider_left_value(slider_left_value); + set_slider_right_value(slider_right_value); + } }, [leftPoint, rightPoint]); + useEffect(() => { + if (binNumber !== '') { + const right_point_temp = leftPoint + binNumber * BIN_WIDTH; + setRightPoint(right_point_temp); + } + }, [binNumber]); + + useEffect(() => { + if (slider_left_value !== undefined && slider_right_value !== undefined) { + const new_left_point = get_point_by_slider_value(slider_left_value); + const new_right_point = get_point_by_slider_value(slider_right_value); + setLeftPoint(new_left_point); + setRightPoint(new_right_point); + } + }, [slider_left_value, slider_right_value]); + + /** + * 设置slider可以操作的point 左右点位区间 + */ + function set_slider_point_range() { + const { current_point } = currentSelectedPool; + const max_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) + ); + const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; + set_slider_point_min(min_point); + set_slider_point_max(max_point); + } + function get_slider_value_by_point(point: number) { + // if (point < slider_point_min) return 0; + // if (point > slider_point_max) return SLIDER_BIN_NUMBER; + const value = (point - slider_point_min) / BIN_WIDTH; + return value; + } + function get_point_by_slider_value(v: number) { + const new_point = slider_point_min + v * BIN_WIDTH; + return new_point; + } function handlePointToAppropriatePoint(point: number) { const { point_delta } = currentSelectedPool; @@ -2689,13 +2752,6 @@ function SetPointsComponent() { return `(${tokenY.symbol}/${tokenX.symbol})`; } } - function changebinNunber(v: string) { - if (!isInvalid(v)) { - const right_point_temp = leftPoint + +v * BIN_WIDTH; - setRightPoint(right_point_temp); - setBinNumber(v); - } - } return (
{/* content */} +
+ +
{/* target price input box */}
@@ -2840,12 +2911,7 @@ function SetPointsComponent() { defaultMessage="Radius" > - { - setRadius(value); - }} - /> +
{/* min price input box */} @@ -2866,6 +2932,7 @@ function SetPointsComponent() { inputStatus={leftInputStatus} setInputStatus={setLeftInputStatus} setPoint={setLeftPoint} + point={leftPoint} > {/* ) : ( {/* ) : ( @@ -2923,10 +2991,8 @@ function SetPointsComponent() { { - changebinNunber(value); - }} />
@@ -2944,32 +3010,40 @@ function SetPointsComponent() { ); } +/** + * step1 slider设置数字区间[0, 20] + * step2 数字区间 和 point区间做一个 双向 映射 + * step step 为 一个bin,区间设定逻辑是 以当前价格为起点,向左向右各辐射30个point(可配置) + * @param param0 + * @returns + */ function Slider({ min, max, step, - values, - setValues, - invert, + value, + set_slider_left_value, + set_slider_right_value, }: { min: number; max: number; step: number; - values: any; - setValues: any; - invert: boolean; + value: any; + set_slider_left_value: Function; + set_slider_right_value: Function; }) { return ( { + set_slider_left_value(v[0]); + set_slider_right_value(v[1]); + }} + value={value} min={min} max={max} step={step} + pearling={true} /> ); } @@ -3299,6 +3373,7 @@ function PointInputComponent({ setCustomPrice, getPrice, + point, setPoint, inputStatus, @@ -3315,11 +3390,13 @@ function PointInputComponent({ disbaled ? 'text-primaryText' : 'text-white' }`} onBlur={() => { + setInputStatus(false); if (customPrice) { const appropriate_point_temp = handlePriceToAppropriatePoint(customPrice); - setInputStatus(false); setPoint(appropriate_point_temp); + } else { + setPoint(point); } }} disabled={disbaled} @@ -3336,11 +3413,11 @@ function PointInputComponent({ export function IntegerInputComponent({ value, - onChange, - onBlur, + setValue, disabled, className, max, + onBlur, }: any) { const removeLeadingZeros = (s: string) => { const oldLen = s.length; @@ -3360,8 +3437,7 @@ export function IntegerInputComponent({ const handleChange = (val: string) => { val = val.replace(/[^\d]/g, ''); val = removeLeadingZeros(val); - - onChange(val); + setValue(val); }; return ( @@ -3373,8 +3449,12 @@ export function IntegerInputComponent({ }`} disabled={disabled} value={value} - onBlur={() => { - onBlur && onBlur(); + onBlur={({ target }) => { + if (onBlur) { + onBlur(); + } else if (!target.value) { + setValue(1); + } }} onChange={({ target }) => { handleChange(target.value); From ca4fda2df5b9ebe2604faf2666610591a8cc6510 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 29 Jun 2023 12:22:41 +0800 Subject: [PATCH 043/204] fix bug --- src/components/forms/SelectToken.tsx | 10 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 241 +++---------------- 2 files changed, 37 insertions(+), 214 deletions(-) diff --git a/src/components/forms/SelectToken.tsx b/src/components/forms/SelectToken.tsx index c1d1298cb..9429f8c6f 100644 --- a/src/components/forms/SelectToken.tsx +++ b/src/components/forms/SelectToken.tsx @@ -771,6 +771,7 @@ export function SelectTokenDCL({ onSelect, selected, className, + notNeedSortToken, }: { selectTokenIn?: (token: TokenMetadata) => void; selectTokenOut?: (token: TokenMetadata) => void; @@ -778,6 +779,7 @@ export function SelectTokenDCL({ selectedToken?: TokenMetadata; selected?: JSX.Element; className?: string; + notNeedSortToken?: boolean; }) { const allPools = useAllPoolsV2(); @@ -800,7 +802,9 @@ export function SelectTokenDCL({ const handleSelect = (p: PoolInfo) => { // select token in const { token_x_metadata, token_y_metadata } = p; - const tokens = sort_tokens_by_base([token_x_metadata, token_y_metadata]); + const tokens = notNeedSortToken + ? [token_x_metadata, token_y_metadata] + : sort_tokens_by_base([token_x_metadata, token_y_metadata]); if (!selectedToken) { selectTokenIn(tokens[0]); @@ -849,7 +853,9 @@ export function SelectTokenDCL({ const renderList = renderPools?.map((p) => { const { token_x_metadata, token_y_metadata } = p; - const tokens = sort_tokens_by_base([token_x_metadata, token_y_metadata]); + const tokens = notNeedSortToken + ? [token_x_metadata, token_y_metadata] + : sort_tokens_by_base([token_x_metadata, token_y_metadata]); return (
{ - if (tokenY && tokenY.id == token.id) return; setTokenX(token); setTokenXBalanceFromNear(token?.near?.toString()); }} selectTokenOut={(token: TokenMetadata) => { - if (tokenX && tokenX.id == token.id) return; setTokenY(token); setTokenYBalanceFromNear(token?.near?.toString()); }} + notNeedSortToken={true} className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " selected={
{ - return acc.plus(cur.amount_x || '0'); - }, Big(0)); - const used_y_amount = nftList_y.reduce((acc, cur) => { - return acc.plus(cur.amount_y || '0'); - }, Big(0)); - const current_bin_nft: IAddLiquidityInfo = { - pool_id, - left_point: current_l_point, - right_point: current_r_point, - amount_x: Big(tokenXAmount_nonDivisible) - .minus(used_x_amount) - .toFixed(0), - amount_y: Big(tokenYAmount_nonDivisible) - .minus(used_y_amount) - .toFixed(0), - min_amount_x: '0', - min_amount_y: '0', - }; - /** 包含当前点位的bin end */ - nftList = nftList_x.concat(nftList_y); - } - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: tokenXAmount_nonDivisible, - amount_y: tokenYAmount_nonDivisible, - }); - } - // 待删除项目 - function addLiquidityBidAsk() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - if (onlyAddYToken) { - nftList = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * 把包含当前点位的那个bin单独拿出来作为一个nft处理,不参与做等差,因为参与做等差会导致 不成立。 - * 除去参与做等差的 x,y,剩余 x,y 作为一个nft,由于这个nft的liquidity太小了,不能添加,可以忽略。 - * */ - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - currentPoint, - 'floor' - ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - currentPoint, - 'ceil' - ); - const nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - const nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - /** 包含当前点位的bin start */ - const used_x_amount = nftList_x.reduce((acc, cur) => { - return acc.plus(cur.amount_x || '0'); - }, Big(0)); - const used_y_amount = nftList_y.reduce((acc, cur) => { - return acc.plus(cur.amount_y || '0'); - }, Big(0)); - const current_bin_nft: IAddLiquidityInfo = { - pool_id, - left_point: current_l_point, - right_point: current_r_point, - amount_x: Big(tokenXAmount_nonDivisible) - .minus(used_x_amount) - .toFixed(0), - amount_y: Big(tokenYAmount_nonDivisible) - .minus(used_y_amount) - .toFixed(0), - min_amount_x: '0', - min_amount_y: '0', - }; - /** 包含当前点位的bin end */ - nftList = nftList_x.concat(nftList_y); - } - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: tokenXAmount_nonDivisible, - amount_y: tokenYAmount_nonDivisible, - }); - } /** * curve 模式下,左侧(y)包含当前点位的 nfts划分 * 此区间bin的数量要求 > 1 @@ -2590,22 +2384,34 @@ function SetPointsComponent() { const token_y_decimals = tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; const tokenSort = tokenX.id == currentSelectedPool.token_x; + /** + * step1 切换池子,左右点位重新初始化 + * step2 切换池子,slider 左右边界值重新初始化 + * step3 左右点===>得到slider 的左右值 + * + * + * + */ // init useEffect(() => { - setInitDone(false); if (currentSelectedPool && tokenX && tokenY) { + debugger; setTargetPoint(currentSelectedPool.current_point); setRadius(RADIUS_DEFAULT_NUMBER); setPriceRangeMode('by_range'); setChartTab('liquidity'); setInitDone(true); + } else { + setInitDone(false); } }, [currentSelectedPool, tokenX, tokenY]); + // init useEffect(() => { if (initDone) { + debugger; set_slider_point_range(); } - }, [initDone]); + }, [initDone, currentSelectedPool, tokenX, tokenY]); /** * change event in radius mode * radius && targetPoint ===> left point right point ===> binNumber @@ -2624,6 +2430,7 @@ function SetPointsComponent() { useEffect(() => { if (leftPoint && rightPoint) { + debugger; const diff = rightPoint - leftPoint; const bin_number_temp = diff / BIN_WIDTH; setBinNumber(bin_number_temp); @@ -2637,14 +2444,24 @@ function SetPointsComponent() { } }, [leftPoint, rightPoint]); useEffect(() => { - if (binNumber !== '') { - const right_point_temp = leftPoint + binNumber * BIN_WIDTH; + if (!isInvalid(binNumber)) { + let right_point_temp = leftPoint + binNumber * BIN_WIDTH; + if (right_point_temp < POINTLEFTRANGE) { + right_point_temp = POINTLEFTRANGE; + } else if (right_point_temp > POINTRIGHTRANGE) { + right_point_temp = 800000; + } + const diff = right_point_temp - leftPoint; + const bin_number_temp = diff / BIN_WIDTH; + + setBinNumber(bin_number_temp); setRightPoint(right_point_temp); } }, [binNumber]); useEffect(() => { if (slider_left_value !== undefined && slider_right_value !== undefined) { + debugger; const new_left_point = get_point_by_slider_value(slider_left_value); const new_right_point = get_point_by_slider_value(slider_right_value); setLeftPoint(new_left_point); From 7d931aa2cf507ddfd8c3f4ff791e90da90e2daa7 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 30 Jun 2023 00:51:36 +0800 Subject: [PATCH 044/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 15 +- src/components/d3Chart/config.tsx | 3 + src/components/farm/FarmsDclDetail.tsx | 4 +- src/components/pool/RemovePoolV3.tsx | 54 +++- src/global.css | 5 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 290 ++++++++++--------- 6 files changed, 227 insertions(+), 144 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index ca818e424..aada704d1 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -26,6 +26,7 @@ import { import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; + export default function DclChart({ pool_id, leftPoint, @@ -144,11 +145,7 @@ export default function DclChart({ } }, [dragLeftPoint, price_range, drawChartDone]); useEffect(() => { - if ( - isValid(leftPoint) && - isValid(dragLeftPoint) && - leftPoint !== dragLeftPoint - ) { + if (isValid(leftPoint) && isValid(dragLeftPoint)) { setDragLeftPoint(leftPoint); } }, [leftPoint]); @@ -189,11 +186,7 @@ export default function DclChart({ } }, [dragRightPoint, price_range, drawChartDone]); useEffect(() => { - if ( - isValid(rightPoint) && - isValid(dragRightPoint) && - rightPoint !== dragRightPoint - ) { + if (isValid(rightPoint) && isValid(dragRightPoint)) { setDragRightPoint(rightPoint); } }, [rightPoint]); @@ -733,6 +726,7 @@ export default function DclChart({ const p = scale.invert(e.x); const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); setDragLeftPoint(newLeftPoint); + setLeftPoint(newLeftPoint); }); d3.select(`${randomId} .drag-left`).call(dragLeft); } @@ -775,6 +769,7 @@ export default function DclChart({ const p = scale.invert(e.x); const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); setDragRightPoint(newRightPoint); + setRightPoint(newRightPoint); }); d3.select(`${randomId} .drag-right`).call(dragRight); } diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index ea4e2f2d6..491a9b21f 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -74,3 +74,6 @@ export function get_default_config_for_chart(): IChartItemConfig { }; } } + +export const SLIDER_BIN_NUMBER = 20; +export const RADIUS_DEFAULT_NUMBER = 3; diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index cf4dfd7af..bbaa21a4f 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1399,9 +1399,9 @@ export default function FarmsDclDetail(props: { {yp_farming_value}
-
+ {/*
{yp_percent} of your liquidity that can be farmed -
+
*/}
diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 6aad79d3f..df9c96f77 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -34,7 +34,6 @@ import { } from '../../services/swapV3'; import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; import Big from 'big.js'; -import { IntegerInputComponent } from '../../pages/poolsV3/AddYourLiquidityPageV3'; import { get_custom_config_for_chart, get_default_config_for_chart, @@ -857,3 +856,56 @@ export const RemovePoolV3 = (props: any) => { ); }; + +export function IntegerInputComponent({ + value, + setValue, + disabled, + className, + max, + onBlur, +}: any) { + const removeLeadingZeros = (s: string) => { + const oldLen = s.length; + s = s.replace(/^0+/, ''); + + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + + if (max && Number(s) > max) { + return max; + } + + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + setValue(val); + }; + + return ( +
+ { + if (onBlur) { + onBlur(); + } else if (!target.value) { + setValue(1); + } + }} + onChange={({ target }) => { + handleChange(target.value); + }} + /> +
+ ); +} diff --git a/src/global.css b/src/global.css index 090ad4682..07a1136e9 100644 --- a/src/global.css +++ b/src/global.css @@ -1310,8 +1310,9 @@ input[type='range']::-webkit-slider-runnable-track { outline: none; background: #00d6af; } -.multi-slider.disabled .thumb { - cursor: not-allowed; +.multi-slider.disabled .thumb-0, +.multi-slider.disabled .track-1 { + visibility: hidden; } .multi-slider .track-0 { height: 10px; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index ec67f06b3..856aeac5d 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -122,6 +122,8 @@ import { import { get_custom_config_for_chart, get_default_config_for_chart, + SLIDER_BIN_NUMBER, + RADIUS_DEFAULT_NUMBER, } from '../../components/d3Chart/config'; import { IChartItemConfig, @@ -169,6 +171,8 @@ export default function AddYourLiquidityPageV3() { const [token_amount_tip, set_token_amount_tip] = useState(); const [only_suppport_spot_shape, set_only_suppport_spot_shape] = useState(false); + const [switch_pool_loading, set_switch_pool_loading] = + useState(true); // callBack handle useAddAndRemoveUrlHandle(); @@ -247,15 +251,16 @@ export default function AddYourLiquidityPageV3() { // new useEffect(() => { // init - if (currentSelectedPool && tokenX && tokenY) { + if (currentSelectedPool) { const { current_point, point_delta } = currentSelectedPool; const n = get_slot_number_in_a_bin(); const bin_width = n * point_delta; SET_SLOT_NUMBER(n); SET_BIN_WIDTH(bin_width); setCurrentPoint(current_point); + set_switch_pool_loading(false); } - }, [currentSelectedPool, tokenX, tokenY]); + }, [currentSelectedPool]); // 如果只有一个 bin 且 双边 则只允许设置成spot模式 useEffect(() => { set_only_suppport_spot_shape(false); @@ -374,6 +379,7 @@ export default function AddYourLiquidityPageV3() { } const currentPoolsMap = {}; if (listPool.length > 0 && tokenX && tokenY) { + set_switch_pool_loading(true); const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { // TODO 增加pool 状态的判断 const { token_x, token_y, state } = pool; @@ -635,8 +641,6 @@ export default function AddYourLiquidityPageV3() { }) { const { token_x, token_y } = currentSelectedPool; const sort = tokenX.id == token_x; - setLeftPoint(leftPoint); - setRightPoint(rightPoint); setInvalidRange(false); setOnlyAddXToken(false); setOnlyAddYToken(false); @@ -759,6 +763,7 @@ export default function AddYourLiquidityPageV3() { isSignedIn, token_amount_tip, set_token_amount_tip, + switch_pool_loading, }} >
@@ -1220,7 +1225,6 @@ function AddLiquidityButton() { * 当前点位为point,以bin为单位 下一跳是 point + bin * slots * 最小的bin的高度就是等差的值 为dis **/ - debugger; setAddLiquidityButtonLoading(true); const tokenXAmount_nonDivisible = toNonDivisibleNumber( tokenX.decimals, @@ -2353,13 +2357,13 @@ function SetPointsComponent() { SLOT_NUMBER, BIN_WIDTH, + switch_pool_loading, + isSignedIn, } = useContext(LiquidityProviderData); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' >('by_range'); - const SLIDER_BIN_NUMBER = 20; - const RADIUS_DEFAULT_NUMBER = 3; const [radius, setRadius] = useState(); const [targetCustomPrice, setTargetCustomPrice] = useState(''); const [leftCustomPrice, setLeftCustomPrice] = useState(''); @@ -2370,7 +2374,6 @@ function SetPointsComponent() { const [rightInputStatus, setRightInputStatus] = useState(false); const [targetInputStatus, setTargetInputStatus] = useState(false); const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - const [initDone, setInitDone] = useState(false); const [slider_point_min, set_slider_point_min] = useState(); const [slider_point_max, set_slider_point_max] = useState(); @@ -2384,94 +2387,83 @@ function SetPointsComponent() { const token_y_decimals = tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; const tokenSort = tokenX.id == currentSelectedPool.token_x; - /** - * step1 切换池子,左右点位重新初始化 - * step2 切换池子,slider 左右边界值重新初始化 - * step3 左右点===>得到slider 的左右值 - * - * - * - */ + // init useEffect(() => { - if (currentSelectedPool && tokenX && tokenY) { - debugger; - setTargetPoint(currentSelectedPool.current_point); + if (currentSelectedPool && !switch_pool_loading) { + const { current_point } = currentSelectedPool; + const right_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + setTargetPoint(current_point); setRadius(RADIUS_DEFAULT_NUMBER); + setLeftPoint(left_point); + setRightPoint(right_point); + set_slider_point_range(); setPriceRangeMode('by_range'); setChartTab('liquidity'); - setInitDone(true); - } else { - setInitDone(false); } - }, [currentSelectedPool, tokenX, tokenY]); - // init - useEffect(() => { - if (initDone) { - debugger; - set_slider_point_range(); - } - }, [initDone, currentSelectedPool, tokenX, tokenY]); - /** - * change event in radius mode - * radius && targetPoint ===> left point right point ===> binNumber - */ - useEffect(() => { - if (targetPoint && BIN_WIDTH) { - const right_point_temp = handlePointToAppropriatePoint( - targetPoint + radius * BIN_WIDTH - ); - const left_point_temp = right_point_temp - BIN_WIDTH * radius * 2; - - setLeftPoint(left_point_temp); - setRightPoint(right_point_temp); - } - }, [radius, targetPoint, BIN_WIDTH, priceRangeMode]); + }, [currentSelectedPool, switch_pool_loading]); useEffect(() => { - if (leftPoint && rightPoint) { - debugger; + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + // effect bin const diff = rightPoint - leftPoint; const bin_number_temp = diff / BIN_WIDTH; setBinNumber(bin_number_temp); - // effect right area - pointChange({ leftPoint, rightPoint, currentPoint }); // effect slider const slider_left_value = get_slider_value_by_point(leftPoint); const slider_right_value = get_slider_value_by_point(rightPoint); + set_slider_left_value(slider_left_value); set_slider_right_value(slider_right_value); + // effect right area + pointChange({ leftPoint, rightPoint, currentPoint }); } - }, [leftPoint, rightPoint]); - useEffect(() => { - if (!isInvalid(binNumber)) { - let right_point_temp = leftPoint + binNumber * BIN_WIDTH; - if (right_point_temp < POINTLEFTRANGE) { - right_point_temp = POINTLEFTRANGE; - } else if (right_point_temp > POINTRIGHTRANGE) { - right_point_temp = 800000; - } - const diff = right_point_temp - leftPoint; - const bin_number_temp = diff / BIN_WIDTH; - - setBinNumber(bin_number_temp); - setRightPoint(right_point_temp); - } - }, [binNumber]); + }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); - useEffect(() => { - if (slider_left_value !== undefined && slider_right_value !== undefined) { - debugger; - const new_left_point = get_point_by_slider_value(slider_left_value); - const new_right_point = get_point_by_slider_value(slider_right_value); - setLeftPoint(new_left_point); - setRightPoint(new_right_point); + // 修改bin --> 合适的右点位 --->合适的bin + function changeBin(bin: number) { + let appropriate_right_point = leftPoint + BIN_WIDTH * bin; + if (appropriate_right_point > POINTRIGHTRANGE) { + appropriate_right_point = POINTRIGHTRANGE; } - }, [slider_left_value, slider_right_value]); + const appropriate_bin_number = + (appropriate_right_point - leftPoint) / BIN_WIDTH; + setRightPoint(appropriate_right_point); + setBinNumber(appropriate_bin_number); + } - /** - * 设置slider可以操作的point 左右点位区间 - */ + // 修改radius-->合适的左右点位 --->合适的radius + function changeRadius(radius: number) { + let appropriate_right_point = handlePointToAppropriatePoint( + targetPoint + BIN_WIDTH * radius + ); + let appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + const appropriate_radius = + (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + setRadius(appropriate_radius); + } + // 修改 targetPrice-->合适的左右点位--->合适的targetPrice + function handleTargetPriceToAppropriatePoint(price: string) { + let appropriate_target_point = handlePriceToAppropriatePoint(price); + const appropriate_right_point = handlePointToAppropriatePoint( + appropriate_target_point + BIN_WIDTH * radius + ); + const appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + return appropriate_target_point; + } + // 设置slider可以操作的point 左右点位区间 function set_slider_point_range() { const { current_point } = currentSelectedPool; const max_point = handlePointToAppropriatePoint( @@ -2482,8 +2474,6 @@ function SetPointsComponent() { set_slider_point_max(max_point); } function get_slider_value_by_point(point: number) { - // if (point < slider_point_min) return 0; - // if (point > slider_point_max) return SLIDER_BIN_NUMBER; const value = (point - slider_point_min) / BIN_WIDTH; return value; } @@ -2491,7 +2481,6 @@ function SetPointsComponent() { const new_point = slider_point_min + v * BIN_WIDTH; return new_point; } - function handlePointToAppropriatePoint(point: number) { const { point_delta } = currentSelectedPool; return getBinPointByPoint(point_delta, SLOT_NUMBER, point); @@ -2574,7 +2563,7 @@ function SetPointsComponent() { className={`w-full xs:w-full md:w-full flex mr-6 flex-col justify-between self-stretch xs:mt-5 md:mt-5`} > {/* chart area */} -
+
{ @@ -2604,19 +2593,21 @@ function SetPointsComponent() {
- {leftPoint && rightPoint && ( - - )} + {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )}
{isSignedIn && (
@@ -2667,6 +2658,7 @@ function SetPointsComponent() { }`} onClick={() => { setPriceRangeMode('by_radius'); + changeRadius(radius); }} >
@@ -2705,7 +2697,9 @@ function SetPointsComponent() { > - +
{/* min price input box */} @@ -2809,6 +2807,7 @@ function SetPointsComponent() {
@@ -2835,31 +2834,36 @@ function SetPointsComponent() { * @returns */ function Slider({ - min, - max, - step, value, set_slider_left_value, set_slider_right_value, + set_left_point, + set_right_point, + get_point_by_slider_value, }: { - min: number; - max: number; - step: number; value: any; set_slider_left_value: Function; set_slider_right_value: Function; + set_left_point: Function; + set_right_point: Function; + get_point_by_slider_value: Function; }) { return ( { - set_slider_left_value(v[0]); - set_slider_right_value(v[1]); + const [num1, num2] = v; + set_slider_left_value(num1); + set_slider_right_value(num2); + const left_point = get_point_by_slider_value(num1); + const right_point = get_point_by_slider_value(num2); + set_left_point(left_point); + set_right_point(right_point); }} value={value} - min={min} - max={max} - step={step} + min={0} + max={SLIDER_BIN_NUMBER} + step={1} pearling={true} /> ); @@ -3138,26 +3142,6 @@ function NoDataComponent(props: any) {
- {/*
- - {quickOptions.map((item: number, index) => { - return ( -
- ± {item}% -
- ); - })} -
- -
-
*/}
{ + const oldLen = s.length; + s = s.replace(/^0+/, ''); + + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + setValue(val); + if (val) { + triggerByValue(val); + } + }; + + return ( +
+ { + if (!target.value) { + setValue(1); + triggerByValue(1); + } + }} + onChange={({ target }) => { + handleChange(target.value); + }} + /> +
+ ); +} +export function IntegerInputComponentCopy({ value, setValue, disabled, From 77dd577111ab481254776a65bc9b3949dba84e20 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 30 Jun 2023 08:11:50 +0800 Subject: [PATCH 045/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index a99109750..e4d64f4f4 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -135,10 +135,16 @@ export default function SwapLimitOrderChart() { const list_x = list.filter((item: IOrderPointItem) => Big(item.amount_x).gt(0) ); + list_x.sort((b: IOrderPointItem, a: IOrderPointItem) => { + return b.point - a.point; + }); const list_y = list .filter((item: IOrderPointItem) => Big(item.amount_y).gt(0)) .reverse(); const { token_x_metadata, token_y_metadata } = pool; + list_y.sort((b: IOrderPointItem, a: IOrderPointItem) => { + return a.point - b.point; + }); // accumulate list_x.forEach((item: IOrderPointItem) => { const { point, amount_x } = item; From 7d78b4787456c5588a8042f9ddd03b7f132db914 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 1 Jul 2023 02:27:13 +0800 Subject: [PATCH 046/204] add personal chart data --- src/components/d3Chart/DclChart.tsx | 159 +++++++---- src/components/d3Chart/config.tsx | 2 +- src/components/d3Chart/interfaces.tsx | 20 +- src/components/pool/RemovePoolV3.tsx | 23 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 21 +- src/services/commonV3.ts | 268 ++++++++++++++++++- 6 files changed, 422 insertions(+), 71 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index aada704d1..df2d9c3ed 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -2,12 +2,15 @@ import React, { useState, useRef, useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import { isMobile } from '../../utils/device'; import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; -import { get_pool, PoolInfo } from '../../services/swapV3'; +import { get_pool, PoolInfo, list_liquidities } from '../../services/swapV3'; import { getPriceByPoint, getPointByPrice, POINTLEFTRANGE, POINTRIGHTRANGE, + divide_liquidities_into_bins, + UserLiquidityInfo, + getBinPointByPoint, } from '../../services/commonV3'; import { getDclPoolPoints, getDclUserPoints } from '../../services/indexer'; import { sortBy } from 'lodash'; @@ -26,7 +29,6 @@ import { import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; - export default function DclChart({ pool_id, leftPoint, @@ -202,6 +204,23 @@ export default function DclChart({ d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); } }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); + /** + * 中文 + * remove 流动性,当参数改变,重新绘制删除区域的北京框 + */ + useEffect(() => { + if (removeParams && drawChartDone) { + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + draw_background_bars_for_select_area({ scale, scaleBar }); + } + }, [ + removeParams?.all, + removeParams?.fromLeft, + removeParams?.fromRight, + removeParams?.point, + drawChartDone, + ]); async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; @@ -229,7 +248,17 @@ export default function DclChart({ const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); let list = []; if (chartType == 'USER' && accountId) { - list = await getDclUserPoints(pool_id, bin_final, accountId); + const liquidities = await list_liquidities(); + const nfts = liquidities.filter((l: UserLiquidityInfo) => { + return l.pool_id == pool_id; + }); + list = divide_liquidities_into_bins({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); } else { const result = await getDclPoolPoints( pool_id, @@ -339,32 +368,32 @@ export default function DclChart({ const decimalRate_price = Math.pow(10, token_x_metadata.decimals) / Math.pow(10, token_y_metadata.decimals); - const data: IChartData[] = getChartDataListInRange().map( - (o: IChartData) => { - const { point } = o; - const price_l = getPriceByPoint(+point, decimalRate_price); - const point_r = +point + point_delta * bin_final; - const point_r_close = +point + point_delta * bin_final + 1; - const price_r = getPriceByPoint(point_r, decimalRate_price); - const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); + const list = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const data: IChartData[] = list.map((o: IChartData) => { + const { point } = o; + const price_l = getPriceByPoint(+point, decimalRate_price); + const point_r = +point + point_delta * bin_final; + const point_r_close = +point + point_delta * bin_final + 1; + const price_r = getPriceByPoint(point_r, decimalRate_price); + const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); - return { - ...o, - liquidity: Big(o.liquidity || 0).toFixed(), - order_liquidity: Big(o.order_liquidity || 0).toFixed(), - token_x: Big(o.token_x || 0).toFixed(), - token_y: Big(o.token_y || 0).toFixed(), - order_x: Big(o.order_x || 0).toFixed(), - order_y: Big(o.order_y || 0).toFixed(), - total_liquidity: Big(o.total_liquidity || 0).toFixed(), - fee: Big(o.fee || 0).toFixed(), - price: price_l.toString(), - price_r: price_r.toString(), - point_r: point_r.toString(), - price_r_close: price_r_close.toString(), - }; - } - ); + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + price: price_l.toString(), + price_r: price_r.toString(), + point_r: point_r.toString(), + price_r_close: price_r_close.toString(), + }; + }); return data; } function hoverBox(e: any, d: IChartData) { @@ -638,19 +667,18 @@ export default function DclChart({ scale: Function; scaleBar: Function; }) { - // todo const { sortP, sortY } = getChartDataListRange_x_y(); + const min_bin_price = sortP[0]; + const max_bin_price = sortP[sortP.length - 1]; const { fromLeft, fromRight, all, point } = removeParams; d3.select(`${randomId} .remove_bars_background`) .attr('width', function () { if (all) { - return scale(sortP[sortP.length - 1]) - scale(sortP[0]); + return scale(max_bin_price) - scale(min_bin_price); } else if (fromLeft) { - return scale(get_price_by_point(point)) - scale(sortP[0]); + return scale(get_price_by_point(point)) - scale(min_bin_price); } else if (fromRight) { - return ( - scale(sortP[sortP.length - 1]) - scale(get_price_by_point(point)) - ); + return scale(max_bin_price) - scale(get_price_by_point(point)); } }) .attr('height', function () { @@ -662,7 +690,7 @@ export default function DclChart({ if (fromRight) { return scale(get_price_by_point(point)); } else { - return scale(sortP[0]); + return scale(min_bin_price); } }) .attr('y', function () { @@ -833,11 +861,37 @@ export default function DclChart({ setDragRightPoint(get_nearby_bin_left_point(newPoint)); } function scaleAxis() { + if (chartType == 'USER') { + return scaleAxis_User(); + } else { + return scaleAxis_Pool(); + } + } + function scaleAxis_Pool() { return d3 .scaleLinear() .domain(price_range) .range([0, svgWidth - svgPaddingX * 2]); } + function scaleAxis_User() { + chartDataList; + const binWidth = getConfig().bin * pool.point_delta; + const min_point = Math.max( + chartDataList[0].point - binWidth * 2, + POINTLEFTRANGE + ); + const max_point = Math.min( + chartDataList[chartDataList.length - 1].point + binWidth * 3, + POINTRIGHTRANGE + ); + const min_price = get_price_by_point(min_point); + const max_price = get_price_by_point(max_point); + const range = [+min_price, +max_price]; + return d3 + .scaleLinear() + .domain(range) + .range([0, svgWidth - svgPaddingX * 2]); + } function scaleAxisY() { if (chartType == 'USER') { return scaleAxisY_User(); @@ -856,32 +910,41 @@ export default function DclChart({ ); }); const sortL = sortBy(L); - return d3 - .scaleLinear() - .domain([+sortL[0], +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]); + return ( + d3 + .scaleLinear() + // .domain([+sortL[0], +sortL[sortL.length - 1]]) + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]) + ); } function scaleAxisY_User() { const { sortY: sortL } = getChartDataListRange_x_y(); - return d3 - .scaleLinear() - .domain([+sortL[0], +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight - whole_bars_background_padding]) - .clamp(true); + return ( + d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + // .domain([+sortL[0], +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true) + ); } function getChartDataListRange_x_y() { const Y: number[] = []; const X: number[] = []; - const chartDataListInrange = getChartDataListInRange(); - chartDataListInrange.forEach((o: IChartData) => { + const chartDataListInrange = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const binWidth = getConfig().bin * pool.point_delta; + chartDataListInrange.forEach((o: IChartData, index) => { const { liquidity, point } = o; Y.push(+liquidity); X.push(+point); + if (index == chartDataListInrange.length - 1) { + X.push(+point + binWidth); + } }); const sortY = sortBy(Y); const sortX = sortBy(X); - // const max_x = sortX[X.length - 1] + getConfig().bin * pool.point_delta; - // sortX.push(max_x); const sortX_map_P = sortX.map((x) => { return get_price_by_point(x); }); diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index 491a9b21f..be18ec853 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -60,7 +60,7 @@ export function get_default_config_for_chart(): IChartItemConfig { }; } else if (env == 'testnet') { return { - bin: 20, + bin: 10, range: 10, rangeGear: [100, 80, 60, 40, 20, 10], colors: ['#707C84', '#2775CA'], diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 1bf05ff6c..e8ad925bb 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -1,19 +1,19 @@ export interface IChartData { pool_id: string; - point: string; + point: number; + token_x: string; + token_y: string; + liquidity: string; price?: string; price_r?: string; point_r?: string; price_r_close?: string; - liquidity: string; - token_x: string; - token_y: string; - order_x: string; - order_y: string; - order_liquidity: string; - fee: string; - total_liquidity: string; - sort_number: number; + order_x?: string; + order_y?: string; + order_liquidity?: string; + fee?: string; + total_liquidity?: string; + sort_number?: number; } export interface IChartItemConfig { diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index df9c96f77..489b3a5ba 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -51,6 +51,7 @@ import { IRemoveLiquidityInfo, IBatchUpdateiquidityInfo, } from '~pages/poolsV3/interfaces'; +import DclChart from '../../components/d3Chart/DclChart'; export type RemoveType = 'left' | 'right' | 'all'; /** @@ -198,7 +199,6 @@ export const RemovePoolV3 = (props: any) => { const max_price = get_bin_price_by_point(max_point); const max_bin_amount = get_bin_amount_by_points(min_point, max_point); - setMinPoint(min_point); setMaxPoint(max_point); setMinPrice(min_price); @@ -636,6 +636,27 @@ export const RemovePoolV3 = (props: any) => { {min_received_total_value}
+
+ {maxPoint && ( + + )} +
{/* Removing way */}
diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 856aeac5d..216bd49f3 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2609,15 +2609,18 @@ function SetPointsComponent() { > )}
- {isSignedIn && ( -
- -
- )} + {isSignedIn && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )}
{/* set price range area */}
diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 7d3521a91..b7ca9c4d2 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -5,8 +5,12 @@ import { WalletContext } from '../utils/wallets-integration'; import { useHistory } from 'react-router'; import BigNumber from 'bignumber.js'; import * as d3 from 'd3'; -import _ from 'lodash'; -import { toReadableNumber, toPrecision } from '../utils/numbers'; +import _, { forEach } from 'lodash'; +import { + toReadableNumber, + toPrecision, + toNonDivisibleNumber, +} from '../utils/numbers'; import { TokenMetadata } from '../services/ft-contract'; import { PoolInfo } from './swapV3'; import { @@ -31,6 +35,8 @@ import { useIntl } from 'react-intl'; import { scientificNotationToString } from '../utils/numbers'; import { getTokens } from './tokens_static'; +import Big from 'big.js'; +import { IChartData } from '~components/d3Chart/interfaces'; const { REF_UNI_V3_SWAP_CONTRACT_ID, boostBlackList } = getConfig(); /** @@ -549,6 +555,47 @@ function getX( ); return x.shiftedBy(-token.decimals).toFixed(); } +/** + * + * @param leftPoint + * @param rightPoint + * @param token_x_amount NonDivisible + */ +function getLByTokenX( + leftPoint: number, + rightPoint: number, + token_x_amount: string +) { + const L = Big(token_x_amount) + .div( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(0); + return L; +} +/** + * + * @param leftPoint + * @param rightPoint + * @param token_y_amount NonDivisible + * @returns + */ +function getLByTokenY( + leftPoint: number, + rightPoint: number, + token_y_amount: string +) { + const L = Big(token_y_amount) + .div( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ) + .toFixed(0); + return L; +} function get_X_Y_In_CurrentPoint( tokenX: TokenMetadata, tokenY: TokenMetadata, @@ -1228,3 +1275,220 @@ export function openUrl(url: string) { newTab.opener = null; newTab.location = url; } + +/** + * 中文 + * @param nfts + * @param slot_number_in_a_bin + * step1 遍历每个nft,按照slot拆分,把每个slot的 liquidity 和 tokenx amount token y amount 放入 map集合里 + * step2 根据step1 得到的 几个,再按照bin划分,每个bin的宽度根据参数拿到,高度 根据 这个bin 里token x token y的数量 应用公式拿到 + */ +export function divide_liquidities_into_bins({ + liquidities, + slot_number_in_a_bin, + tokenX, + tokenY, + poolDetail, +}: { + liquidities: UserLiquidityInfo[]; + slot_number_in_a_bin: number; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + poolDetail: PoolInfo; +}) { + // split data to slots + const liquidities_in_slot_unit: { [point: number]: IChartData } = {}; + const { point_delta, pool_id } = poolDetail; + liquidities.forEach((liquidity: UserLiquidityInfo) => { + const { left_point, right_point, amount } = liquidity; + const slots_in_a_nft = (right_point - left_point) / point_delta; + for (let i = 0; i < slots_in_a_nft; i++) { + const left_point_i = left_point + i * point_delta; + const right_point_i = left_point_i + point_delta; + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point: left_point_i, + right_point: right_point_i, + amount, + tokenX, + tokenY, + poolDetail, + }); + const { + token_x: token_x_amount, + token_y: token_y_amount, + liquidity: L, + } = liquidities_in_slot_unit[left_point_i] || {}; + liquidities_in_slot_unit[left_point_i] = { + pool_id, + point: left_point_i, + liquidity: Big(amount || 0) + .plus(L || 0) + .toFixed(), + token_x: Big(total_x || 0) + .plus(token_x_amount || 0) + .toFixed(), + token_y: Big(total_y || 0) + .plus(token_y_amount || 0) + .toFixed(), + }; + } + }); + // split slots to bin + const liquidities_in_bin_unit: IChartData[] = []; + const slots: IChartData[] = Object.values(liquidities_in_slot_unit); + slots.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + const min_point = slots[0].point; + const max_point = slots[slots.length - 1].point + point_delta; + + const min_bin_point = getBinPointByPoint( + point_delta, + slot_number_in_a_bin, + min_point, + 'floor' + ); + const max_bin_point = getBinPointByPoint( + point_delta, + slot_number_in_a_bin, + max_point, + 'ceil' + ); + const binWidth = slot_number_in_a_bin * point_delta; + const bins_number = (max_bin_point - min_bin_point) / binWidth; + for (let i = 0; i < bins_number; i++) { + // search slots in this bin + const bin_i_point_start = min_bin_point + i * binWidth; + const bin_i_point_end = min_bin_point + (i + 1) * binWidth; + const slots_in_bin_i = slots.filter((slot: IChartData) => { + const { point } = slot; + const point_start = point; + const point_end = point + point_delta; + return point_start >= bin_i_point_start && point_end <= bin_i_point_end; + }); + // get tokenx tokeny amount in this bin + let total_x_amount_in_bin_i = Big(0); + let total_y_amount_in_bin_i = Big(0); + slots_in_bin_i.forEach((slot: IChartData) => { + const { token_x, token_y } = slot; + total_x_amount_in_bin_i = total_x_amount_in_bin_i.plus(token_x); + total_y_amount_in_bin_i = total_y_amount_in_bin_i.plus(token_y); + }); + + // get L in this bin + const bin_i_L = get_l_amount_by_condition({ + left_point: bin_i_point_start, + right_point: bin_i_point_end, + token_x_amount: toNonDivisibleNumber( + tokenX.decimals, + total_x_amount_in_bin_i.toFixed() + ), + token_y_amount: toNonDivisibleNumber( + tokenY.decimals, + total_y_amount_in_bin_i.toFixed() + ), + poolDetail, + }); + + // + liquidities_in_bin_unit.push({ + pool_id, + point: bin_i_point_start, + liquidity: bin_i_L, + token_x: total_x_amount_in_bin_i.toFixed(), + token_y: total_y_amount_in_bin_i.toFixed(), + }); + } + // filter empty bin + const bins_final = liquidities_in_bin_unit.filter((bin: IChartData) => { + const { token_x, token_y } = bin; + return Big(token_x || 0).gt(0) || Big(token_y || 0).gt(0); + }); + bins_final.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + // last bins + return bins_final; +} +// 根据 point 区间和 L 高度,获得这段区间里的token x 和 token y的数量 +function get_x_y_amount_by_condition({ + left_point, + right_point, + amount, + tokenX, + tokenY, + poolDetail, +}: { + left_point: number; + right_point: number; + amount: string; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + poolDetail: PoolInfo; +}) { + const { current_point } = poolDetail; + let total_x = '0'; + let total_y = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + const tokenYAmount = getY(left_point, current_point, amount, tokenY); + const tokenXAmount = getX(current_point + 1, right_point, amount, tokenX); + const { amountx, amounty } = get_X_Y_In_CurrentPoint( + tokenX, + tokenY, + amount, + poolDetail + ); + total_x = new BigNumber(tokenXAmount).plus(amountx).toFixed(); + total_y = new BigNumber(tokenYAmount).plus(amounty).toFixed(); + } + // only y token + if (current_point >= right_point) { + const tokenYAmount = getY(left_point, right_point, amount, tokenY); + total_y = tokenYAmount; + } + // only x token + if (left_point > current_point) { + const tokenXAmount = getX(left_point, right_point, amount, tokenX); + total_x = tokenXAmount; + } + return { + total_x, + total_y, + }; +} + +// 根据 区间和这段区间里的token x tokeny的数量,获得这段区间的高度 +function get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount, + token_y_amount, + poolDetail, +}: { + left_point: number; + right_point: number; + token_x_amount: string; + token_y_amount: string; + poolDetail: PoolInfo; +}) { + let L; + const { current_point } = poolDetail; + // in range + if (current_point >= left_point && right_point > current_point) { + if (Big(token_y_amount).gt(0)) { + L = getLByTokenY(left_point, current_point, token_y_amount); + } else { + L = getLByTokenX(current_point + 1, right_point, token_x_amount); + } + } + // only y token + if (current_point >= right_point) { + L = getLByTokenY(left_point, right_point, token_y_amount); + } + // only x token + if (left_point > current_point) { + L = getLByTokenX(left_point, right_point, token_x_amount); + } + return L; +} From 160f7bfa30293705b92964317e6c493c04828b40 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 1 Jul 2023 13:01:20 +0800 Subject: [PATCH 047/204] add user chart --- src/components/d3Chart/DclChart.tsx | 10 +- src/components/d3Chart/config.tsx | 1 + src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 2022 ++++++++++-------- src/services/commonV3.ts | 20 +- 4 files changed, 1198 insertions(+), 855 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index df2d9c3ed..9fc76c584 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -38,6 +38,7 @@ export default function DclChart({ config, chartType, removeParams, + newlyAddedLiquidities, }: { pool_id: string; leftPoint?: number; @@ -52,6 +53,7 @@ export default function DclChart({ point?: number; all?: boolean; }; + newlyAddedLiquidities?:UserLiquidityInfo[] }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -99,7 +101,7 @@ export default function DclChart({ if (pool) { get_chart_data(); } - }, [pool, accountId]); + }, [pool, accountId, newlyAddedLiquidities]); useEffect(() => { if (price_range && chartDataList) { drawChart(); @@ -221,6 +223,9 @@ export default function DclChart({ removeParams?.point, drawChartDone, ]); + /** + * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 + */ async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; @@ -249,9 +254,10 @@ export default function DclChart({ let list = []; if (chartType == 'USER' && accountId) { const liquidities = await list_liquidities(); - const nfts = liquidities.filter((l: UserLiquidityInfo) => { + let nfts = liquidities.filter((l: UserLiquidityInfo) => { return l.pool_id == pool_id; }); + nfts = nfts.concat(newlyAddedLiquidities || []); list = divide_liquidities_into_bins({ liquidities: nfts, slot_number_in_a_bin: bin_final, diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index be18ec853..149dd2b72 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -77,3 +77,4 @@ export function get_default_config_for_chart(): IChartItemConfig { export const SLIDER_BIN_NUMBER = 20; export const RADIUS_DEFAULT_NUMBER = 3; +export const max_nft_divisional_per_side = 3; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 216bd49f3..b99d5c660 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -76,6 +76,8 @@ import { openUrl, getBinPointByPrice, getBinPointByPoint, + get_l_amount_by_condition, + UserLiquidityInfo, } from '../../services/commonV3'; import { formatWithCommas, @@ -97,22 +99,17 @@ import { getURLInfo } from '../../components/layout/transactionTipPopUp'; import { BlueCircleLoading } from '../../components/layout/Loading'; import { isMobile } from '../../utils/device'; import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; -import { OutLinkIcon } from '../../components/icon/Common'; -import { REF_FI_POOL_ACTIVE_TAB } from '../pools/LiquidityPage'; -import getConfig from '../../services/config'; -import QuestionMark from '../../components/farm/QuestionMark'; import ReactSlider from 'react-slider'; import Big from 'big.js'; import { SelectTokenDCL } from '../../components/forms/SelectToken'; import { SliderCurColor } from '~components/icon/Info'; -import { values } from 'lodash'; -import { PipValues } from '../../../public/charting_library/charting_library'; import { CurveShape, SpotShape, BidAskShape, } from '../Orderly/components/Common/Icons'; import DclChart from '../../components/d3Chart/DclChart'; +import { IChartData } from '../../components/d3Chart/interfaces'; import { IAddLiquidityInfo, IAddLiquidityInfoHelp, @@ -124,6 +121,7 @@ import { get_default_config_for_chart, SLIDER_BIN_NUMBER, RADIUS_DEFAULT_NUMBER, + max_nft_divisional_per_side } from '../../components/d3Chart/config'; import { IChartItemConfig, @@ -164,7 +162,6 @@ export default function AddYourLiquidityPageV3() { // new const [binNumber, setBinNumber] = useState(); const [liquidityShape, setLiquidityShape] = useState('Spot'); - const [curPointInBinBoundry, setCurPointInBinBoundry] = useState(false); const [topPairs, setTopPairs] = useState([]); const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); @@ -173,6 +170,7 @@ export default function AddYourLiquidityPageV3() { useState(false); const [switch_pool_loading, set_switch_pool_loading] = useState(true); + const [new_user_liquidities, set_new_user_liquidities] = useState([]); // callBack handle useAddAndRemoveUrlHandle(); @@ -261,7 +259,7 @@ export default function AddYourLiquidityPageV3() { set_switch_pool_loading(false); } }, [currentSelectedPool]); - // 如果只有一个 bin 且 双边 则只允许设置成spot模式 + // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 useEffect(() => { set_only_suppport_spot_shape(false); if (currentSelectedPool) { @@ -323,7 +321,6 @@ export default function AddYourLiquidityPageV3() { setTopPairs(top3); } if (!refTokens || !triTokens || !triTokenIds) return ; - const allTokens = getAllTokens(refTokens, triTokens); async function get_seeds() { const seeds = await get_all_seeds(); set_seed_list(seeds); @@ -730,702 +727,380 @@ export default function AddYourLiquidityPageV3() { const slots = custom_config[pool_id]?.bin || bin; return slots; } - const tokenSort = tokenX?.id == currentSelectedPool?.token_x; - const mobileDevice = isMobile(); - - return ( - 可以触发 + * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 + * step3 把新增的liquidity传递给Chart组件, + * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 + * step5 疑问;?实时修改图表 会导致效率什么问题吗? + */ + function generate_new_user_chart() { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint) && (+tokenXAmount > 0 || +tokenYAmount > 0)) { + let new_nfts:any; + if (liquidityShape == 'Spot') { + const new_nft = getLiquiditySpot(); + new_nfts = [new_nft] + } else { + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; + } + const processed_new_nfts = process_liquidities(new_nfts); + set_new_user_liquidities(processed_new_nfts); + } else { + set_new_user_liquidities([]); + } + } + function getLiquiditySpot() { + const { pool_id } = currentSelectedPool; + return { + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + // amount_x: tokenSort + // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + // amount_y: tokenSort + // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + // token_x: tokenSort ? tokenX : tokenY, + // token_y: tokenSort ? tokenY : tokenX, + } + } + function getLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; + const current_l_point = getBinPointByPoint( + point_delta, SLOT_NUMBER, - BIN_WIDTH, - liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - isSignedIn, - token_amount_tip, - set_token_amount_tip, - switch_pool_loading, - }} - > -
- {/* 缩略图 */} - {/* */} - {/* 详情页图 */} - {/* */} - {/* 添加页图 */} - {/* */} - {/* 用户流动性图表*/} - {/* */} - {/* 删除流动性图表 从右侧部分删除 */} - {/* */} - {/* 删除流动性图表 从左侧部分删除 */} - {/* */} - {/* 删除流动性图表 全部删除 */} - {/* */} -
- - {/* mobile head */} -
-
- -
- - - -
-
- {/* pc head */} -
{ - history.goBack(); - }} - > -
- -
- - - -
- - {/* content */} -
-
- {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} - {/* right area */} -
-
-
- -
+ current_point, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'ceil' + ); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(''); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; - { - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
+ const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; - - - {token_amount_tip ? ( -
- - {token_amount_tip} -
- ) : null} + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } + } + return nftList; + } + /** + * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ -
-
- -
+ /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; -
{ - setHoverFeeBox(false); - }} - onMouseEnter={() => { - setHoverFeeBox(true); - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
+ const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; -
- -
+ const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; -
- {[SpotShape, CurveShape, BidAskShape].map( - (Shape, index: number) => { - let disabled = false; - if ( - (index == 1 || index == 2) && - only_suppport_spot_shape - ) { - disabled = true; - } - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - if (index === 0) setLiquidityShape('Spot'); - else if (index === 1 && !only_suppport_spot_shape) - setLiquidityShape('Curve'); - else if (index == 2 && !only_suppport_spot_shape) - setLiquidityShape('BidAsk'); - }} - > - + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } - - {index === 0 && ( - - )} - {index === 1 && ( - - )} + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); - {index === 2 && ( - - )} - -
- ); - } - )} -
- {/* todo */} - {/*
- -
*/} - {currentSelectedPool && currentSelectedPool.pool_id && ( - - )} -
- - -
-
-
-
-
-
- ); -} -/** - * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 提示 todo - * @returns - */ -function AddLiquidityButton() { - const { - currentSelectedPool, - tokenX, - tokenY, - binNumber, - SLOT_NUMBER, - leftPoint, - rightPoint, - currentPoint, - liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - set_token_amount_tip, - } = useContext(LiquidityProviderData); - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; - const max_nft_divisional_per_side = 3; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; - function addLiquiditySpot() { - setAddLiquidityButtonLoading(true); - const { pool_id } = currentSelectedPool; - add_liquidity({ - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - token_x: tokenX, - token_y: tokenY, - // amount_x: tokenSort - // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - // amount_y: tokenSort - // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - // token_x: tokenSort ? tokenX : tokenY, - // token_y: tokenSort ? tokenY : tokenX, - }); - } - function addLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_x_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 - * step2 分配好后,获得对右侧的最小token数量要求 - * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 - */ - const { point_delta, current_point } = currentSelectedPool; - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'floor' - ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'ceil' - ); - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(''); - if (liquidityShape == 'Curve') { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_curve({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_x = get_decline_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_curve({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_y = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } else { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_bid_ask({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_bid_ask({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == exclude_cur_total_nft_number) { + amount_x = dis + .mul(exclude_cur_total_nft_number + 1) + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); } } - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: tokenXAmount_nonDivisible, - amount_y: tokenYAmount_nonDivisible, - }); + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; } /** - * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * curve 模式下,右侧(x)包含当前点位的 nfts划分 * 此区间bin的数量要求 > 1 * 双边 * @param param0 * @returns */ - function get_y_nfts_contain_current_curve({ + function get_x_nfts_contain_current_curve({ left_point, right_point, }: { @@ -1443,25 +1118,26 @@ function AddLiquidityButton() { */ /** - * 从左往右逐渐上升模式 - * 从左往右计算 + * 从左往右逐渐下降模式 + * 从右往左计算 * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) * ===>求出dis后,就可以知道每个nft的amount * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; const slot_number_in_a_bin = SLOT_NUMBER; const binWidth = slot_number_in_a_bin * point_delta; - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; const exclude_cur_total_bin_number = (exclude_cur_right_point - exclude_cur_left_point) / binWidth; @@ -1487,150 +1163,11 @@ function AddLiquidityButton() { const addLiquidityInfoList: IAddLiquidityInfo[] = []; const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; for (let i = 0; i < exclude_cur_total_nft_number; i++) { - const left_i_point = exclude_cur_left_point + nftWidth * i; + // 不同点3 + let left_i_point; let right_i_point; if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == exclude_cur_total_nft_number) { - amount_x = dis - .mul(exclude_cur_total_nft_number + 1) - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; + left_i_point = exclude_cur_left_point; } else { left_i_point = exclude_cur_right_point - nftWidth * (i + 1); } @@ -2174,19 +1711,800 @@ function AddLiquidityButton() { } return addLiquidityInfoList; } - function formula_of_token_x(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + /** + * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 + */ + function process_liquidities(liquidities:IAddLiquidityInfo[]) { + const { pool_id } = currentSelectedPool; + const new_liquidities:UserLiquidityInfo[] = []; + liquidities.forEach((l:IAddLiquidityInfo) => { + const { left_point, right_point, amount_x, amount_y } = l; + const L = get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount:amount_x, + token_y_amount:amount_y, + poolDetail:currentSelectedPool + }) + new_liquidities.push({ + pool_id, + left_point, + right_point, + amount: L + }) + }) + return new_liquidities; + } + function pointAndshapeAndAmountChange() { + set_token_amount_tip(''); + if (liquidityShape == 'Spot') { + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount) + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount) + } + } + } + const tokenSort = tokenX?.id == currentSelectedPool?.token_x; + const mobileDevice = isMobile(); + + return ( + +
+ {/* 缩略图 */} + {/* */} + {/* 详情页图 */} + {/* */} + {/* 添加页图 */} + {/* */} + {/* 用户流动性图表*/} + {/* */} + {/* 删除流动性图表 从右侧部分删除 */} + {/* */} + {/* 删除流动性图表 从左侧部分删除 */} + {/* */} + {/* 删除流动性图表 全部删除 */} + {/* */} +
+ + {/* mobile head */} +
+
+ +
+ + + +
+
+ {/* pc head */} +
{ + history.goBack(); + }} + > +
+ +
+ + + +
+ + {/* content */} +
+
+ {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + {/* right area */} +
+
+
+ +
+ + { + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ + + + {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null} + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + setHoverFeeBox(false); + }} + onMouseEnter={() => { + setHoverFeeBox(true); + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ +
+ +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1 && !only_suppport_spot_shape) + setLiquidityShape('Curve'); + else if (index == 2 && !only_suppport_spot_shape) + setLiquidityShape('BidAsk'); + }} + > + + + + {index === 0 && ( + + )} + {index === 1 && ( + + )} + + {index === 2 && ( + + )} + +
+ ); + } + )} +
+ {/* new user chart part */} + { + isSignedIn && currentSelectedPool ?
+
+
+ +
+
Generate
+
+ { + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
:null + } + + {currentSelectedPool && currentSelectedPool.pool_id && ( + + )} +
+ + +
+
+
+
+
+
+ ); +} +/** + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 提示 todo + * @returns + */ +function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + binNumber, + SLOT_NUMBER, + leftPoint, + rightPoint, + currentPoint, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + set_token_amount_tip, + get_y_nfts_contain_current_curve, + get_x_nfts_contain_current_curve, + get_x_nfts_contain_current_bid_ask, + get_y_nfts_contain_current_bid_ask, + get_rise_pattern_nfts, + get_decline_pattern_nfts, + formula_of_token_x, + formula_of_token_y, + + getLiquiditySpot, + getLiquidityForCurveAndBidAskMode, + } = useContext(LiquidityProviderData); + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const new_liquidity = getLiquiditySpot(); + add_liquidity(new_liquidity); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' ); + let nftList: IAddLiquidityInfo[] = []; + nftList = getLiquidityForCurveAndBidAskMode(); + if (!nftList) { + setAddLiquidityButtonLoading(false); + return; + } + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: tokenXAmount_nonDivisible, + amount_y: tokenYAmount_nonDivisible, + }); } - function formula_of_token_y(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) + function addLiquidityForCurveAndBidAskModeCopy() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' ); + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'ceil' + ); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(''); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 + set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 + set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + setAddLiquidityButtonLoading(false); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } + } + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: tokenXAmount_nonDivisible, + amount_y: tokenYAmount_nonDivisible, + }); } function getMax(token: TokenMetadata, balance: string) { return token.id !== WRAP_NEAR_CONTRACT_ID @@ -2345,9 +2663,13 @@ function SetPointsComponent() { currentSelectedPool, tokenX, tokenY, + tokenXAmount, + tokenYAmount, + pointAndshapeAndAmountChange, pointChange, currentPoint, + liquidityShape, leftPoint, setLeftPoint, @@ -2406,6 +2728,7 @@ function SetPointsComponent() { } }, [currentSelectedPool, switch_pool_loading]); + // 左侧改变===》点位 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin @@ -2422,6 +2745,13 @@ function SetPointsComponent() { pointChange({ leftPoint, rightPoint, currentPoint }); } }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); + // 数据有变动==》去掉token 太少提示 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + pointAndshapeAndAmountChange(); + } + }, [liquidityShape, tokenXAmount, tokenYAmount, leftPoint, rightPoint]) + // 修改bin --> 合适的右点位 --->合适的bin function changeBin(bin: number) { diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index b7ca9c4d2..56e5b9838 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -106,16 +106,16 @@ export const FEELIST = [ export const POINTLEFTRANGE = -800000; export const POINTRIGHTRANGE = 800000; export interface UserLiquidityInfo { - lpt_id: string; - owner_id: string; + lpt_id?: string; + owner_id?: string; pool_id: string; left_point: number; right_point: number; amount: string; - unclaimed_fee_x: string; - unclaimed_fee_y: string; - mft_id: string; - v_liquidity: string; + unclaimed_fee_x?: string; + unclaimed_fee_y?: string; + mft_id?: string; + v_liquidity?: string; part_farm_ratio?: string; unfarm_part_amount?: string; status_in_other_seed?: string; @@ -1459,7 +1459,13 @@ function get_x_y_amount_by_condition({ } // 根据 区间和这段区间里的token x tokeny的数量,获得这段区间的高度 -function get_l_amount_by_condition({ +/** + * + * @param param0 + * @returns + * token_x_amount、token_y_amount ---> NonDivisible + */ +export function get_l_amount_by_condition({ left_point, right_point, token_x_amount, From a112729d1047d65c9bb91aee9262f12bd9bdd13c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 1 Jul 2023 22:55:53 +0800 Subject: [PATCH 048/204] add user 24 fee apr --- src/components/d3Chart/DclChart.tsx | 287 ++++++++++++++++++- src/components/d3Chart/interfaces.tsx | 41 +++ src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 116 ++++---- src/services/commonV3.ts | 8 +- src/services/indexer.ts | 16 +- 5 files changed, 395 insertions(+), 73 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 9fc76c584..5fcbfa453 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -11,8 +11,9 @@ import { divide_liquidities_into_bins, UserLiquidityInfo, getBinPointByPoint, + get_x_y_amount_by_condition, } from '../../services/commonV3'; -import { getDclPoolPoints, getDclUserPoints } from '../../services/indexer'; +import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy } from 'lodash'; import { IChartData, @@ -20,8 +21,17 @@ import { IChartConfig, IBinDetail, IPoolChartConfig, + IUserLiquiditiesDetail, + IDCLAccountFee, + IDclLogData, + IProcessedLogData, } from './interfaces'; -import { formatPrice, formatNumber, formatPercentage } from './utils'; +import { + formatPrice, + formatNumber, + formatPercentage, + formatWithCommas_usd, +} from './utils'; import { get_custom_config_for_chart, get_default_config_for_chart, @@ -29,6 +39,9 @@ import { import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; +import { getBoostTokenPrices } from '../../services/farm'; +import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; + export default function DclChart({ pool_id, leftPoint, @@ -53,7 +66,7 @@ export default function DclChart({ point?: number; all?: boolean; }; - newlyAddedLiquidities?:UserLiquidityInfo[] + newlyAddedLiquidities?: UserLiquidityInfo[]; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -64,6 +77,12 @@ export default function DclChart({ const [zoom, setZoom] = useState(); const [randomId, setRandomId] = useState('.' + createRandomString()); const [drawChartDone, setDrawChartDone] = useState(false); + const [user_liquidities, set_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + const [user_liquidities_detail, set_user_liquidities_detail] = + useState(); + const [tokenPriceList, setTokenPriceList] = useState>(); /** constant start */ const appearanceConfig: IPoolChartConfig = config || {}; const dragBarWidth = 28; @@ -90,6 +109,12 @@ export default function DclChart({ ); /** constant end */ const { accountId } = useWalletSelector(); + useEffect(() => { + // get all token prices + getBoostTokenPrices().then((result) => { + setTokenPriceList(result); + }); + }, []); useEffect(() => { if (pool_id) { get_pool_detail(pool_id); @@ -101,7 +126,13 @@ export default function DclChart({ if (pool) { get_chart_data(); } - }, [pool, accountId, newlyAddedLiquidities]); + }, [pool, accountId]); + useEffect(() => { + if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { + const new_list = get_latest_user_chart_data(); + setChartDataList(new_list); + } + }, [newlyAddedLiquidities, user_liquidities]); useEffect(() => { if (price_range && chartDataList) { drawChart(); @@ -223,6 +254,228 @@ export default function DclChart({ removeParams?.point, drawChartDone, ]); + /** + * 获取个人流动性图表详情信息 + * 用户 hover 框里数据展示 + */ + useEffect(() => { + if ( + user_liquidities.length && + pool && + chartType == 'USER' && + tokenPriceList + ) { + get_user_liquidities_detail(); + } + }, [user_liquidities, pool, chartType, tokenPriceList]); + async function get_user_liquidities_detail() { + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + + const points: number[] = []; + let total_x_amount = Big(0); + let total_y_amount = Big(0); + let total_value = Big(0); + let total_fee_earned = Big(0); + let apr_24 = ''; + if (dcl_fee_result) { + const dclAccountFee: IDCLAccountFee = dcl_fee_result; + const { total_earned_fee, apr } = dclAccountFee; + + // 总共赚到的fee + const { total_fee_x, total_fee_y } = total_earned_fee || {}; + const total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + const total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value.plus( + total_earned_fee_y_value + ); + + // 24小时平均利润 + const { fee_data, user_token, change_log_data } = apr; + const { fee_x, fee_y } = fee_data; + // 针对后端接口 fee_x、fee_y 会有负值处理成0 + const fee_x_final = Big(fee_x || 0).lt(0) ? 0 : fee_x; + const fee_y_final = Big(fee_y || 0).lt(0) ? 0 : fee_y; + const fee_x_24 = toReadableNumber( + token_x_metadata.decimals, + Big(fee_x_final || 0).toFixed() + ); + const fee_y_24 = toReadableNumber( + token_y_metadata.decimals, + Big(fee_y_final || 0).toFixed() + ); + const fee_x_24_value = Big(fee_x_24).mul(price_x); + const fee_y_24_value = Big(fee_y_24).mul(price_y); + const total_fee_24_value = fee_x_24_value.plus(fee_y_24_value); + + // 24小时平均本金 + const processed_change_log: IProcessedLogData[] = []; + const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 + const second24 = 24 * 60 * 60; + const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 + const { token_x, token_y } = user_token; + const token_x_NonDivisible = toNonDivisibleNumber( + token_x_metadata.decimals, + Big(token_x || 0).toFixed() + ); + const token_y_NonDivisible = toNonDivisibleNumber( + token_y_metadata.decimals, + Big(token_y || 0).toFixed() + ); + const current_user_token: IDclLogData = { + token_x: token_x_NonDivisible, + token_y: token_y_NonDivisible, + timestamp: current_time, + }; + const current_processed = process_log_data( + current_user_token, + before24Time + ); + processed_change_log.push(current_processed); + + if (change_log_data?.length) { + change_log_data.reverse(); + change_log_data.forEach((log: IDclLogData) => { + const pre_processed_log = + processed_change_log[processed_change_log.length - 1]; + const { token_x, token_y, timestamp } = log; // timestamp 纳秒 + const real_token_x = Big(pre_processed_log.token_x) + .minus(token_x) + .toFixed(); + const real_token_y = Big(pre_processed_log.token_y) + .minus(token_y) + .toFixed(); + const new_log = { + token_x: real_token_x, + token_y: real_token_y, + timestamp: Big(timestamp).div(1000000000).toFixed(0), + }; + const processed_log: IProcessedLogData = process_log_data( + new_log, + before24Time + ); + processed_change_log.push(processed_log); + }); + // for 加权 + processed_change_log.forEach( + (log: IProcessedLogData, index: number) => { + const { distance_from_24 } = log; + if (index !== processed_change_log.length - 1) { + const next_log = processed_change_log[index + 1]; + const dis = Big(distance_from_24) + .minus(next_log.distance_from_24) + .toFixed(); + log.distance_from_24 = dis; + } + } + ); + } + // 24小时apr + let total_processed_log_value = Big(0); + processed_change_log.forEach((log: IProcessedLogData) => { + const { total_value, distance_from_24 } = log; + total_processed_log_value = total_processed_log_value.plus( + Big(total_value).mul(distance_from_24) + ); + }); + const principal = total_processed_log_value.div(second24); + if (principal.gt(0)) { + apr_24 = formatPercentage( + total_fee_24_value.div(principal).mul(100).toFixed() + ); + } + } + user_liquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point, amount } = l; + points.push(left_point, right_point); + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point, + right_point, + amount, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + total_x_amount = total_x_amount.plus(total_x); + total_y_amount = total_y_amount.plus(total_y); + }); + const total_x_value = Big(price_x).mul(total_x_amount); + const total_y_value = Big(price_y).mul(total_y_amount); + total_value = total_x_value.plus(total_y_value); + points.sort((b: number, a: number) => { + return b - a; + }); + const min_point = points[0]; + const max_point = points[points.length - 1]; + const min_price = get_price_by_point(min_point); + const max_price = get_price_by_point(max_point); + set_user_liquidities_detail({ + total_value: formatWithCommas_usd(total_value.toFixed()), + min_price: formatPrice(min_price), + max_price: formatPrice(max_price), + total_x_amount: formatNumber(total_x_amount.toFixed()), + total_y_amount: formatNumber(total_y_amount.toFixed()), + apr_24, + total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), + }); + } + /** + * + * @param log 这笔log的本金 + * @param before24Time 秒 + * timestamp 秒 + * @returns + */ + function process_log_data(log: IDclLogData, before24Time: string) { + const { token_x_metadata, token_y_metadata } = pool; + const { token_x, token_y, timestamp } = log; + const token_x_amount = toReadableNumber( + token_x_metadata.decimals, + Big(token_x || 0).toFixed() + ); + const token_y_amount = toReadableNumber( + token_y_metadata.decimals, + Big(token_y || 0).toFixed() + ); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const token_x_value = Big(token_x_amount).mul(price_x); + const token_y_value = Big(token_y_amount).mul(price_y); + const total_value = token_x_value.plus(token_y_value).toFixed(); + const distance_from_24 = Big(timestamp).minus(before24Time).toFixed(0); // 秒 + return { + distance_from_24, + total_value, + token_x: Big(token_x).toFixed(), + token_y: Big(token_y).toFixed(), + }; + } + function get_latest_user_chart_data() { + const { token_x_metadata, token_y_metadata } = pool; + const { bin: bin_final } = getConfig(); + const nfts = user_liquidities.concat(newlyAddedLiquidities || []); + const list = divide_liquidities_into_bins({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + return list; + } /** * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 */ @@ -254,10 +507,10 @@ export default function DclChart({ let list = []; if (chartType == 'USER' && accountId) { const liquidities = await list_liquidities(); - let nfts = liquidities.filter((l: UserLiquidityInfo) => { + const nfts = liquidities.filter((l: UserLiquidityInfo) => { return l.pool_id == pool_id; }); - nfts = nfts.concat(newlyAddedLiquidities || []); + set_user_liquidities(nfts); list = divide_liquidities_into_bins({ liquidities: nfts, slot_number_in_a_bin: bin_final, @@ -1200,8 +1453,6 @@ export default function DclChart({
Fee APR (24h) - {/* todo test 数据 */} - {/* {binDetail?.point} */} {binDetail?.feeApr} @@ -1310,27 +1561,37 @@ export default function DclChart({
Your Liquidity - ~$294.25 + + {user_liquidities_detail?.total_value} +
Price Range - 1.5636 - 2.7102 + {user_liquidities_detail?.min_price} -{' '} + {user_liquidities_detail?.max_price}
Position - 20 NEAR + 36.2418 USDC + {user_liquidities_detail?.total_x_amount}{' '} + {pool?.token_x_metadata.symbol} +{' '} + {user_liquidities_detail?.total_y_amount}{' '} + {pool?.token_y_metadata.symbol}
24h APR - 34.5% + + {user_liquidities_detail?.apr_24} +
Total Earned Fee - $8.283 + + {user_liquidities_detail?.total_earned_fee} +
{/* current 价格 */} diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index e8ad925bb..c7c894314 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -53,3 +53,44 @@ export interface IPoolChartConfig { radiusMode?: boolean; targetPoint?: number; } + +export interface IUserLiquiditiesDetail { + total_value: string; + min_price: string; + max_price: string; + total_x_amount: string; + total_y_amount: string; + apr_24: string; + total_earned_fee: string; +} + +export interface IDCLAccountFee { + total_earned_fee: { + total_fee_x: string; + total_fee_y: string; + }; + apr: { + fee_data: { + fee_x: string; + fee_y: string; + }; + user_token: { + token_x: string; + token_y: string; + }; + change_log_data: IDclLogData[]; + }; +} + +export interface IDclLogData { + event_method?: string; + token_x: string; + token_y: string; + timestamp: string; +} +export interface IProcessedLogData { + total_value: string; + distance_from_24: string; + token_x: string; + token_y: string; +} diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index b99d5c660..04850f588 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -121,7 +121,7 @@ import { get_default_config_for_chart, SLIDER_BIN_NUMBER, RADIUS_DEFAULT_NUMBER, - max_nft_divisional_per_side + max_nft_divisional_per_side, } from '../../components/d3Chart/config'; import { IChartItemConfig, @@ -170,7 +170,9 @@ export default function AddYourLiquidityPageV3() { useState(false); const [switch_pool_loading, set_switch_pool_loading] = useState(true); - const [new_user_liquidities, set_new_user_liquidities] = useState([]); + const [new_user_liquidities, set_new_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); // callBack handle useAddAndRemoveUrlHandle(); @@ -728,24 +730,28 @@ export default function AddYourLiquidityPageV3() { return slots; } /** - * step1 当数据发生改变 + * step1 当数据发生改变 * leftPoint, rightPoint 有效 * tokenXAmount, tokenYAmount 至少有一个有值 - * ===> 可以触发 + * ===> 可以触发 * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 * step3 把新增的liquidity传递给Chart组件, * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 * step5 疑问;?实时修改图表 会导致效率什么问题吗? */ function generate_new_user_chart() { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint) && (+tokenXAmount > 0 || +tokenYAmount > 0)) { - let new_nfts:any; + if ( + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + (+tokenXAmount > 0 || +tokenYAmount > 0) + ) { + let new_nfts: any; if (liquidityShape == 'Spot') { const new_nft = getLiquiditySpot(); - new_nfts = [new_nft] + new_nfts = [new_nft]; } else { - new_nfts = getLiquidityForCurveAndBidAskMode(); - if (!new_nfts) return; + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; } const processed_new_nfts = process_liquidities(new_nfts); set_new_user_liquidities(processed_new_nfts); @@ -771,7 +777,7 @@ export default function AddYourLiquidityPageV3() { // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), // token_x: tokenSort ? tokenX : tokenY, // token_y: tokenSort ? tokenY : tokenX, - } + }; } function getLiquidityForCurveAndBidAskMode() { /** @@ -1728,34 +1734,34 @@ export default function AddYourLiquidityPageV3() { /** * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 */ - function process_liquidities(liquidities:IAddLiquidityInfo[]) { + function process_liquidities(liquidities: IAddLiquidityInfo[]) { const { pool_id } = currentSelectedPool; - const new_liquidities:UserLiquidityInfo[] = []; - liquidities.forEach((l:IAddLiquidityInfo) => { + const new_liquidities: UserLiquidityInfo[] = []; + liquidities.forEach((l: IAddLiquidityInfo) => { const { left_point, right_point, amount_x, amount_y } = l; const L = get_l_amount_by_condition({ left_point, right_point, - token_x_amount:amount_x, - token_y_amount:amount_y, - poolDetail:currentSelectedPool - }) + token_x_amount: amount_x, + token_y_amount: amount_y, + poolDetail: currentSelectedPool, + }); new_liquidities.push({ pool_id, left_point, right_point, - amount: L - }) - }) + amount: L, + }); + }); return new_liquidities; } function pointAndshapeAndAmountChange() { set_token_amount_tip(''); if (liquidityShape == 'Spot') { if (tokenXAmount) { - changeTokenXAmount(tokenXAmount) + changeTokenXAmount(tokenXAmount); } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount) + changeTokenYAmount(tokenYAmount); } } } @@ -1805,7 +1811,6 @@ export default function AddYourLiquidityPageV3() { getLiquiditySpot, getLiquidityForCurveAndBidAskMode, - }} >
@@ -2174,33 +2179,43 @@ export default function AddYourLiquidityPageV3() { )}
{/* new user chart part */} - { - isSignedIn && currentSelectedPool ?
-
-
- + {isSignedIn && currentSelectedPool ? ( +
+
+
+ +
+
+ Generate +
-
Generate
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )}
- { - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
:null - } - + ) : null} + {currentSelectedPool && currentSelectedPool.pool_id && ( )} @@ -2745,13 +2760,12 @@ function SetPointsComponent() { pointChange({ leftPoint, rightPoint, currentPoint }); } }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); - // 数据有变动==》去掉token 太少提示 + // 数据有变动==》去掉token 太少提示 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { pointAndshapeAndAmountChange(); } - }, [liquidityShape, tokenXAmount, tokenYAmount, leftPoint, rightPoint]) - + }, [liquidityShape, tokenXAmount, tokenYAmount, leftPoint, rightPoint]); // 修改bin --> 合适的右点位 --->合适的bin function changeBin(bin: number) { diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 56e5b9838..8c7d204b1 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1411,7 +1411,7 @@ export function divide_liquidities_into_bins({ return bins_final; } // 根据 point 区间和 L 高度,获得这段区间里的token x 和 token y的数量 -function get_x_y_amount_by_condition({ +export function get_x_y_amount_by_condition({ left_point, right_point, amount, @@ -1460,9 +1460,9 @@ function get_x_y_amount_by_condition({ // 根据 区间和这段区间里的token x tokeny的数量,获得这段区间的高度 /** - * - * @param param0 - * @returns + * + * @param param0 + * @returns * token_x_amount、token_y_amount ---> NonDivisible */ export function get_l_amount_by_condition({ diff --git a/src/services/indexer.ts b/src/services/indexer.ts index e92b9974b..2c3877107 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -498,11 +498,17 @@ export const getDCLAccountFee = async (props: { account_id: string | number; }): Promise => { const paramString = genUrlParams(props); - - return await fetch(config.indexerUrl + `/get-fee-by-account?${paramString}`, { - method: 'GET', - headers: { 'Content-type': 'application/json; charset=UTF-8' }, - }).then((res) => res.json()); + try { + return await fetch( + config.indexerUrl + `/get-fee-by-account?${paramString}`, + { + method: 'GET', + headers: { 'Content-type': 'application/json; charset=UTF-8' }, + } + ).then((res) => res.json()); + } catch (error) { + return; + } }; export interface ProposalHash { From c7b715849f0a566b3a05b660b382f2f1a08f0a18 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 2 Jul 2023 00:50:01 +0800 Subject: [PATCH 049/204] add user chart hover data --- src/components/d3Chart/DclChart.tsx | 16 ++++++++-------- src/services/commonV3.ts | 1 + 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 5fcbfa453..e7baa7f0a 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -135,8 +135,10 @@ export default function DclChart({ }, [newlyAddedLiquidities, user_liquidities]); useEffect(() => { if (price_range && chartDataList) { - drawChart(); - setDrawChartDone(true); + if (chartType !== 'USER' || chartType =='USER' && chartDataList.length) { + drawChart(); + setDrawChartDone(true); + } } }, [price_range, chartDataList]); useEffect(() => { @@ -1133,7 +1135,6 @@ export default function DclChart({ .range([0, svgWidth - svgPaddingX * 2]); } function scaleAxis_User() { - chartDataList; const binWidth = getConfig().bin * pool.point_delta; const min_point = Math.max( chartDataList[0].point - binWidth * 2, @@ -1280,7 +1281,7 @@ export default function DclChart({ return (
{/* 控件按钮*/} @@ -1557,12 +1558,11 @@ export default function DclChart({ ) : null}
- {/* hover到区域上的悬浮框 todo test数据*/}
Your Liquidity - {user_liquidities_detail?.total_value} + {user_liquidities_detail?.total_value || '-'}
@@ -1584,13 +1584,13 @@ export default function DclChart({
24h APR - {user_liquidities_detail?.apr_24} + {user_liquidities_detail?.apr_24 || '-'}
Total Earned Fee - {user_liquidities_detail?.total_earned_fee} + {user_liquidities_detail?.total_earned_fee || '-'}
diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 8c7d204b1..7af250f0e 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1296,6 +1296,7 @@ export function divide_liquidities_into_bins({ tokenY: TokenMetadata; poolDetail: PoolInfo; }) { + if (!liquidities.length) return []; // split data to slots const liquidities_in_slot_unit: { [point: number]: IChartData } = {}; const { point_delta, pool_id } = poolDetail; From 7a9cd1025e4cbc187f2160e46ff6422364f5967c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 2 Jul 2023 11:20:36 +0800 Subject: [PATCH 050/204] fix storage issue --- src/services/swapV3.ts | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 037584169..c0f2e93a7 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1038,7 +1038,7 @@ export const batch_add_liquidity = async ({ ], }); } - const neededStorage = await get_user_storage_detail({ size: 1 }); + const neededStorage = await get_user_storage_detail({ size: 9 }); if (!ONLY_ZEROS.test(neededStorage)) { transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, @@ -1387,7 +1387,7 @@ export const batch_remove_liquidity_contract = async ({ ], }); } - const neededStorage = await checkTokenNeedsStorageDeposit_v3(); + const neededStorage = await get_user_storage_detail({ size: 0 }); if (neededStorage) { transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, @@ -1452,7 +1452,7 @@ export const claim_all_liquidity_fee = async ({ }); } - const neededStorage = await checkTokenNeedsStorageDeposit_v3(); + const neededStorage = await get_user_storage_detail({ size: 0 }); if (neededStorage) { transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, From feef51d3073b18679f4b80795d568398c99433f6 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 2 Jul 2023 11:33:50 +0800 Subject: [PATCH 051/204] update --- src/services/swapV3.ts | 9 +++------ 1 file changed, 3 insertions(+), 6 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index c0f2e93a7..be293fe57 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -773,12 +773,9 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { } = detail; if (size + cur_liquidity_slots + cur_order_slots > max_slots) { - deposit_fee = deposit_fee.plus( - new Big(slot_price).mul( - size + cur_liquidity_slots + cur_order_slots - max_slots - ) - ); - + const need_num = size + cur_liquidity_slots + cur_order_slots - max_slots; + const need_num_final = Math.max(need_num, 10); + deposit_fee = deposit_fee.plus(new Big(slot_price).mul(need_num_final)); if (user_id !== detail.sponser_id) { deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); } From aa5df3f96ddf35ce4f65770d53decd6419e946b2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 2 Jul 2023 12:29:56 +0800 Subject: [PATCH 052/204] update --- src/pages/poolsV3/PoolDetailV3.tsx | 72 ++---------------------------- src/services/swapV3.ts | 7 +-- 2 files changed, 8 insertions(+), 71 deletions(-) diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 205af971f..1d4934d61 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -126,6 +126,7 @@ import { numberWithCommas } from '~pages/Orderly/utiles'; import { HiOutlineExternalLink } from 'react-icons/hi'; import Big from 'big.js'; import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; +import DclChart from '../../components/d3Chart/DclChart'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); export default function PoolDetailV3() { @@ -1116,32 +1117,6 @@ function YourLiquidityBox(props: {
- - {/* - -
-
- - - {token_x_metadata.symbol} - -
- - {getTotalTokenAmount().total_x} - -
-
-
- - - {token_y_metadata.symbol} - -
- - {getTotalTokenAmount().total_y} - -
*/} -
{ @@ -2019,11 +1994,13 @@ function Chart(props: any) { showLiqudityButton={true} /> ) : ( + // todo + // )} ); @@ -2743,7 +2720,6 @@ function LiquidityChart(props: any) { const [chartLoading, setChartLoading] = useState(true); const [noData, setNoData] = useState(true); const [rateDirection, setRateDirection] = useState(true); - const chartDom = useRef(null); const isMobile = isClientMobie(); useEffect(() => { if (poolDetail?.token_x_metadata) { @@ -2756,31 +2732,6 @@ function LiquidityChart(props: any) { } } }, [poolDetail]); - useEffect(() => { - if (depthData) { - drawChartData({ - depthData, - token_x_decimals: poolDetail.token_x_metadata.decimals, - token_y_decimals: poolDetail.token_y_metadata.decimals, - chartDom, - sort: rateDirection, - onlyCurrent: true, - sizey: isMobile ? 220 : 330, - ticks: isMobile ? 5 : 8, - space_x: isMobile ? 20 : 50, - }); - const { liquidities } = depthData; - const list = Object.values(liquidities); - if (list.length == 0) { - setNoData(true); - } else { - setNoData(false); - } - setChartLoading(false); - } else { - setChartLoading(true); - } - }, [depthData, rateDirection]); const rateDOM = useMemo(() => { const { current_point, token_x_metadata, token_y_metadata } = poolDetail; const rate = @@ -2850,22 +2801,7 @@ function LiquidityChart(props: any) { {!chartLoading && noData ? ( ) : ( - - - - - + )} ); diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index be293fe57..3f3e2f286 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -361,8 +361,9 @@ export const v3Swap = async ({ ], }); } - + debugger; if (LimitOrderWithSwap) { + debugger; const pool_id = LimitOrderWithSwap.pool_id; const fee = Number(pool_id.split(V3_POOL_SPLITER)[2]); @@ -743,7 +744,7 @@ export interface UserStorageDetail { locked_near: string; storage_for_asset: string; slot_price: string; - sponser_id: string; + sponsor_id: string; } export const get_user_storage_detail = async ({ size }: { size: number }) => { @@ -776,7 +777,7 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { const need_num = size + cur_liquidity_slots + cur_order_slots - max_slots; const need_num_final = Math.max(need_num, 10); deposit_fee = deposit_fee.plus(new Big(slot_price).mul(need_num_final)); - if (user_id !== detail.sponser_id) { + if (user_id !== detail.sponsor_id) { deposit_fee = deposit_fee.plus(new Big(detail.locked_near)); } } From 67eb75fec92c16e5080eda0a1e5d2bd769c46efc Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 00:25:26 +0800 Subject: [PATCH 053/204] add small chart --- src/components/d3Chart/DclChart.tsx | 146 +++------------------------ src/components/d3Chart/utils.ts | 4 +- src/pages/pools/LiquidityPage.tsx | 28 ++++-- src/pages/poolsV3/PoolDetailV3.tsx | 111 +++++++++++--------- src/services/commonV3.ts | 151 +++++++++++++++++++++++++++- src/services/swapV3.ts | 2 +- src/state/pool.ts | 32 ------ 7 files changed, 252 insertions(+), 222 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index e7baa7f0a..e13b7947f 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -12,6 +12,7 @@ import { UserLiquidityInfo, getBinPointByPoint, get_x_y_amount_by_condition, + get_account_24_apr, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy } from 'lodash'; @@ -102,7 +103,7 @@ export default function DclChart({ const disFromPercentBoxToDragBar = +( appearanceConfig.disFromPercentBoxToDragBar || 2 ); - const svgPaddingX = +(appearanceConfig.svgPaddingX || 5); + const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% const whole_bars_background_padding = +( appearanceConfig.whole_bars_background_padding || 20 @@ -135,7 +136,10 @@ export default function DclChart({ }, [newlyAddedLiquidities, user_liquidities]); useEffect(() => { if (price_range && chartDataList) { - if (chartType !== 'USER' || chartType =='USER' && chartDataList.length) { + if ( + chartType !== 'USER' || + (chartType == 'USER' && chartDataList.length) + ) { drawChart(); setDrawChartDone(true); } @@ -287,8 +291,7 @@ export default function DclChart({ let apr_24 = ''; if (dcl_fee_result) { const dclAccountFee: IDCLAccountFee = dcl_fee_result; - const { total_earned_fee, apr } = dclAccountFee; - + const { total_earned_fee } = dclAccountFee; // 总共赚到的fee const { total_fee_x, total_fee_y } = total_earned_fee || {}; const total_earned_fee_x = toReadableNumber( @@ -304,101 +307,10 @@ export default function DclChart({ total_fee_earned = total_earned_fee_x_value.plus( total_earned_fee_y_value ); - - // 24小时平均利润 - const { fee_data, user_token, change_log_data } = apr; - const { fee_x, fee_y } = fee_data; - // 针对后端接口 fee_x、fee_y 会有负值处理成0 - const fee_x_final = Big(fee_x || 0).lt(0) ? 0 : fee_x; - const fee_y_final = Big(fee_y || 0).lt(0) ? 0 : fee_y; - const fee_x_24 = toReadableNumber( - token_x_metadata.decimals, - Big(fee_x_final || 0).toFixed() - ); - const fee_y_24 = toReadableNumber( - token_y_metadata.decimals, - Big(fee_y_final || 0).toFixed() - ); - const fee_x_24_value = Big(fee_x_24).mul(price_x); - const fee_y_24_value = Big(fee_y_24).mul(price_y); - const total_fee_24_value = fee_x_24_value.plus(fee_y_24_value); - - // 24小时平均本金 - const processed_change_log: IProcessedLogData[] = []; - const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 - const second24 = 24 * 60 * 60; - const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 - const { token_x, token_y } = user_token; - const token_x_NonDivisible = toNonDivisibleNumber( - token_x_metadata.decimals, - Big(token_x || 0).toFixed() - ); - const token_y_NonDivisible = toNonDivisibleNumber( - token_y_metadata.decimals, - Big(token_y || 0).toFixed() + // 24h 利润 + apr_24 = formatPercentage( + get_account_24_apr(dcl_fee_result, pool, tokenPriceList) ); - const current_user_token: IDclLogData = { - token_x: token_x_NonDivisible, - token_y: token_y_NonDivisible, - timestamp: current_time, - }; - const current_processed = process_log_data( - current_user_token, - before24Time - ); - processed_change_log.push(current_processed); - - if (change_log_data?.length) { - change_log_data.reverse(); - change_log_data.forEach((log: IDclLogData) => { - const pre_processed_log = - processed_change_log[processed_change_log.length - 1]; - const { token_x, token_y, timestamp } = log; // timestamp 纳秒 - const real_token_x = Big(pre_processed_log.token_x) - .minus(token_x) - .toFixed(); - const real_token_y = Big(pre_processed_log.token_y) - .minus(token_y) - .toFixed(); - const new_log = { - token_x: real_token_x, - token_y: real_token_y, - timestamp: Big(timestamp).div(1000000000).toFixed(0), - }; - const processed_log: IProcessedLogData = process_log_data( - new_log, - before24Time - ); - processed_change_log.push(processed_log); - }); - // for 加权 - processed_change_log.forEach( - (log: IProcessedLogData, index: number) => { - const { distance_from_24 } = log; - if (index !== processed_change_log.length - 1) { - const next_log = processed_change_log[index + 1]; - const dis = Big(distance_from_24) - .minus(next_log.distance_from_24) - .toFixed(); - log.distance_from_24 = dis; - } - } - ); - } - // 24小时apr - let total_processed_log_value = Big(0); - processed_change_log.forEach((log: IProcessedLogData) => { - const { total_value, distance_from_24 } = log; - total_processed_log_value = total_processed_log_value.plus( - Big(total_value).mul(distance_from_24) - ); - }); - const principal = total_processed_log_value.div(second24); - if (principal.gt(0)) { - apr_24 = formatPercentage( - total_fee_24_value.div(principal).mul(100).toFixed() - ); - } } user_liquidities.forEach((l: UserLiquidityInfo) => { const { left_point, right_point, amount } = l; @@ -434,37 +346,6 @@ export default function DclChart({ total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), }); } - /** - * - * @param log 这笔log的本金 - * @param before24Time 秒 - * timestamp 秒 - * @returns - */ - function process_log_data(log: IDclLogData, before24Time: string) { - const { token_x_metadata, token_y_metadata } = pool; - const { token_x, token_y, timestamp } = log; - const token_x_amount = toReadableNumber( - token_x_metadata.decimals, - Big(token_x || 0).toFixed() - ); - const token_y_amount = toReadableNumber( - token_y_metadata.decimals, - Big(token_y || 0).toFixed() - ); - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - const token_x_value = Big(token_x_amount).mul(price_x); - const token_y_value = Big(token_y_amount).mul(price_y); - const total_value = token_x_value.plus(token_y_value).toFixed(); - const distance_from_24 = Big(timestamp).minus(before24Time).toFixed(0); // 秒 - return { - distance_from_24, - total_value, - token_x: Big(token_x).toFixed(), - token_y: Big(token_y).toFixed(), - }; - } function get_latest_user_chart_data() { const { token_x_metadata, token_y_metadata } = pool; const { bin: bin_final } = getConfig(); @@ -1141,7 +1022,7 @@ export default function DclChart({ POINTLEFTRANGE ); const max_point = Math.min( - chartDataList[chartDataList.length - 1].point + binWidth * 3, + chartDataList[chartDataList.length - 1].point + binWidth * 2, POINTRIGHTRANGE ); const min_price = get_price_by_point(min_point); @@ -1281,7 +1162,10 @@ export default function DclChart({ return (
{/* 控件按钮*/} diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index c7b7ed9e8..79fdf0427 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -44,10 +44,10 @@ export const formatPrice = (v: string | number) => { const big = Big(v); if (big.eq(0)) { return '0'; - } else if (big.lt(0.01)) { + } else if (big.lt(0.0001)) { return '<0.0001'; } else { - return big.toFixed(4, 1); + return big.toFixed(4, 0); } }; export const formatToInternationalCurrencySystem$ = (v: string | number) => { diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 1eaebd255..70f4f82da 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -150,6 +150,7 @@ import { useTokenPriceList } from '../../state/token'; import { useSeedFarmsByPools } from '../../state/pool'; import { RiArrowRightSLine } from 'react-icons/ri'; +import DclChart from '../../components/d3Chart/DclChart'; const HIDE_LOW_TVL = 'REF_FI_HIDE_LOW_TVL'; @@ -1769,7 +1770,7 @@ function PoolRowV2({ >
{'$' + toInternationalCurrencySystem(pool.tvl.toString())}
+ {/* 缩略图 中文*/} + {!mark && ( +
+ +
+ )}
); @@ -1985,7 +2003,6 @@ function WatchListCard({ ); } - function LiquidityPage_({ pools, sortBy, @@ -2044,7 +2061,7 @@ function LiquidityPage_({ const intl = useIntl(); const inputRef = useRef(null); - const allPoolsV2 = useAllPoolsV2(); + let allPoolsV2 = useAllPoolsV2(); const [tvlV2, setTvlV2] = useState(); @@ -2129,7 +2146,6 @@ function LiquidityPage_({ setFarmCountStar(count); }); }, []); - const tokensStar = [REF_META_DATA, unwrapedNear]; const poolReSortingFunc = (p1: Pool, p2: Pool) => { const v1 = volumes[p1.id] ? parseFloat(volumes[p1.id]) : 0; @@ -2821,7 +2837,7 @@ function LiquidityPage_({ {activeTab === 'v2' && (
-
+
@@ -2982,8 +2998,8 @@ function LiquidityPage_({ )}
+
-
{allPoolsV2 .sort(poolv2ReSortingFunc) diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 1d4934d61..301c96e33 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -50,6 +50,7 @@ import { get_pool_id, get_pool_name, openUrl, + get_account_24_apr, } from '~services/commonV3'; import { ftGetTokensMetadata } from '../../services/ft-contract'; import { @@ -102,7 +103,6 @@ import { useV3TvlChart, useDCLPoolTransaction, useDCLTopBinFee, - useDCLAccountAPR, } from '~state/pool'; import { getV3Pool24VolumeById } from '~services/indexer'; import { @@ -127,6 +127,9 @@ import { HiOutlineExternalLink } from 'react-icons/hi'; import Big from 'big.js'; import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; import DclChart from '../../components/d3Chart/DclChart'; +import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; +import { formatPercentage } from '../../components/d3Chart/utils'; +import { getDCLAccountFee } from '../../services/indexer'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); export default function PoolDetailV3() { @@ -619,16 +622,15 @@ function YourLiquidityBox(props: { matched_seeds: Seed[]; }) { const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; - console.log('liquidities: ', liquidities); const [user_liquidities_detail, set_user_liquidities_detail] = useState< UserLiquidityDetail[] >([]); - - console.log('user_liquidities_detail: ', user_liquidities_detail); - const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); + const [accountAPR, setAccountAPR] = useState(''); const [operationType, setOperationType] = useState('add'); - const { token_x_metadata, token_y_metadata } = poolDetail; + const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; + const { accountId } = useWalletSelector(); + const history = useHistory(); useEffect(() => { if (liquidities) { const temp_list: UserLiquidityDetail[] = []; @@ -681,6 +683,23 @@ function YourLiquidityBox(props: { set_user_liquidities_detail(temp_list); } }, [liquidities, Object.keys(tokenPriceList).length]); + useEffect(() => { + if (poolDetail && tokenPriceList) { + get_24_apr(); + } + }, [poolDetail, tokenPriceList]); + async function get_24_apr() { + let apr_24 = ''; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + if (dcl_fee_result) { + // 24h 利润 + apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + } + setAccountAPR(formatPercentage(apr_24)); + } function get_amount_x_y(liquidity: UserLiquidityInfo) { const [tokenX, tokenY] = [token_x_metadata, token_y_metadata]; const { left_point, right_point, amount: L } = liquidity; @@ -877,25 +896,10 @@ function YourLiquidityBox(props: { total_fee_price: display_total_price, }; } - - function addLiquidity() { - setOperationType('add'); - setShowSelectLiquidityBox(true); - } function removeLiquidity() { setOperationType('remove'); setShowSelectLiquidityBox(true); } - - const { accountId } = useWalletSelector(); - - const history = useHistory(); - - const accountAPR = useDCLAccountAPR({ - pool_id: poolDetail.pool_id, - account_id: accountId, - }); - const [hover, setHover] = useState(false); const [noReverseRange, setNoReverseRange] = useState(true); @@ -943,9 +947,6 @@ function YourLiquidityBox(props: { value = new BigNumber(1).dividedBy(value).toFixed(); } } - - console.log('value: ', value); - return value; } @@ -966,8 +967,6 @@ function YourLiquidityBox(props: { }) || []; const rangeList = findRangeIntersection(priceRangeList); - console.log('rangeList: ', rangeList); - return { rangeList, rateMapTokens: getRateMapTokens(), @@ -987,25 +986,26 @@ function YourLiquidityBox(props: { - - {/* {liquidities?.length > 1 ? ( - - {liquidities.length} NFTs - - ) : null} */} ~{getTotalLiquditiesTvl()}
- {/*
- {liquidities?.length > 1 ? ( - - {liquidities.length} NFTs - - ) : null} -
*/} - -
+ {/* chart area */} +
+ +
+
- {accountAPR} + {accountAPR || '-'}
@@ -1121,8 +1121,6 @@ function YourLiquidityBox(props: { { e.stopPropagation(); - // addLiquidity(); - goAddliquidityV2(); }} color="#fff" @@ -1994,13 +1992,11 @@ function Chart(props: any) { showLiqudityButton={true} /> ) : ( - // todo - // )} ); @@ -2137,8 +2133,6 @@ export function RecentTransactions({ const { swapTransactions, liquidityTransactions, limitOrderTransactions } = useDCLPoolTransaction({ pool_id }); - console.log('pool_id: ', pool_id); - const [tab, setTab] = useState(storedTab || 'swap'); const onChangeTab = (tab: RencentTabKey) => { @@ -2732,6 +2726,20 @@ function LiquidityChart(props: any) { } } }, [poolDetail]); + useEffect(() => { + if (depthData) { + const { liquidities } = depthData; + const list = Object.values(liquidities); + if (list.length == 0) { + setNoData(true); + } else { + setNoData(false); + } + setChartLoading(false); + } else { + setChartLoading(true); + } + }, [depthData, rateDirection]); const rateDOM = useMemo(() => { const { current_point, token_x_metadata, token_y_metadata } = poolDetail; const rate = @@ -2801,7 +2809,12 @@ function LiquidityChart(props: any) { {!chartLoading && noData ? ( ) : ( - +
+ +
)} ); diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 7af250f0e..2326ad75c 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -36,7 +36,12 @@ import { scientificNotationToString } from '../utils/numbers'; import { getTokens } from './tokens_static'; import Big from 'big.js'; -import { IChartData } from '~components/d3Chart/interfaces'; +import { + IChartData, + IDCLAccountFee, + IDclLogData, + IProcessedLogData, +} from '~components/d3Chart/interfaces'; const { REF_UNI_V3_SWAP_CONTRACT_ID, boostBlackList } = getConfig(); /** @@ -1499,3 +1504,147 @@ export function get_l_amount_by_condition({ } return L; } + +export function get_account_24_apr( + dclAccountFee: IDCLAccountFee | any, + pool: PoolInfo, + tokenPriceList: any +) { + const { token_x_metadata, token_y_metadata } = pool; + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + let apr_24 = ''; + const { apr } = dclAccountFee; + // 24小时平均利润 + const { fee_data, user_token, change_log_data } = apr; + const { fee_x, fee_y } = fee_data; + // 针对后端接口 fee_x、fee_y 会有负值处理成0 + const fee_x_final = Big(fee_x || 0).lt(0) ? 0 : fee_x; + const fee_y_final = Big(fee_y || 0).lt(0) ? 0 : fee_y; + const fee_x_24 = toReadableNumber( + token_x_metadata.decimals, + Big(fee_x_final || 0).toFixed() + ); + const fee_y_24 = toReadableNumber( + token_y_metadata.decimals, + Big(fee_y_final || 0).toFixed() + ); + const fee_x_24_value = Big(fee_x_24).mul(price_x); + const fee_y_24_value = Big(fee_y_24).mul(price_y); + const total_fee_24_value = fee_x_24_value.plus(fee_y_24_value); + + // 24小时平均本金 + const processed_change_log: IProcessedLogData[] = []; + const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 + const second24 = 24 * 60 * 60; + const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 + const { token_x, token_y } = user_token; + const token_x_NonDivisible = toNonDivisibleNumber( + token_x_metadata.decimals, + Big(token_x || 0).toFixed() + ); + const token_y_NonDivisible = toNonDivisibleNumber( + token_y_metadata.decimals, + Big(token_y || 0).toFixed() + ); + const current_user_token: IDclLogData = { + token_x: token_x_NonDivisible, + token_y: token_y_NonDivisible, + timestamp: current_time, + }; + const current_processed = process_log_data( + current_user_token, + before24Time, + pool, + tokenPriceList + ); + processed_change_log.push(current_processed); + + if (change_log_data?.length) { + change_log_data.reverse(); + change_log_data.forEach((log: IDclLogData) => { + const pre_processed_log = + processed_change_log[processed_change_log.length - 1]; + const { token_x, token_y, timestamp } = log; // timestamp 纳秒 + const real_token_x = Big(pre_processed_log.token_x) + .minus(token_x) + .toFixed(); + const real_token_y = Big(pre_processed_log.token_y) + .minus(token_y) + .toFixed(); + const new_log = { + token_x: real_token_x, + token_y: real_token_y, + timestamp: Big(timestamp).div(1000000000).toFixed(0), + }; + const processed_log: IProcessedLogData = process_log_data( + new_log, + before24Time, + pool, + tokenPriceList + ); + processed_change_log.push(processed_log); + }); + // for 加权 + processed_change_log.forEach((log: IProcessedLogData, index: number) => { + const { distance_from_24 } = log; + if (index !== processed_change_log.length - 1) { + const next_log = processed_change_log[index + 1]; + const dis = Big(distance_from_24) + .minus(next_log.distance_from_24) + .toFixed(); + log.distance_from_24 = dis; + } + }); + } + // 24小时apr + let total_processed_log_value = Big(0); + processed_change_log.forEach((log: IProcessedLogData) => { + const { total_value, distance_from_24 } = log; + total_processed_log_value = total_processed_log_value.plus( + Big(total_value).mul(distance_from_24) + ); + }); + const principal = total_processed_log_value.div(second24); + if (principal.gt(0)) { + apr_24 = total_fee_24_value.div(principal).mul(100).toFixed(); + } + return apr_24; +} + +/** + * + * @param log 这笔log的本金 + * @param before24Time 秒 + * timestamp 秒 + * @returns + */ +function process_log_data( + log: IDclLogData, + before24Time: string, + pool: PoolInfo, + tokenPriceList: any +) { + const { token_x_metadata, token_y_metadata } = pool; + const { token_x, token_y, timestamp } = log; + const token_x_amount = toReadableNumber( + token_x_metadata.decimals, + Big(token_x || 0).toFixed() + ); + const token_y_amount = toReadableNumber( + token_y_metadata.decimals, + Big(token_y || 0).toFixed() + ); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const token_x_value = Big(token_x_amount).mul(price_x); + const token_y_value = Big(token_y_amount).mul(price_y); + const total_value = token_x_value.plus(token_y_value).toFixed(); + const distance_from_24 = Big(timestamp).minus(before24Time).toFixed(0); // 秒 + return { + distance_from_24, + total_value, + token_x: Big(token_x).toFixed(), + token_y: Big(token_y).toFixed(), + }; +} diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 3f3e2f286..404b56f36 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -774,7 +774,7 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { } = detail; if (size + cur_liquidity_slots + cur_order_slots > max_slots) { - const need_num = size + cur_liquidity_slots + cur_order_slots - max_slots; + const need_num = size + cur_liquidity_slots + cur_order_slots - max_slots; const need_num_final = Math.max(need_num, 10); deposit_fee = deposit_fee.plus(new Big(slot_price).mul(need_num_final)); if (user_id !== detail.sponsor_id) { diff --git a/src/state/pool.ts b/src/state/pool.ts index 4053b35f6..574c1907c 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -1420,38 +1420,6 @@ export const useDCLPoolTransaction = ({ }; }; -export const useDCLAccountAPR = ({ - pool_id, - account_id, -}: { - pool_id: string | number; - account_id: string | number; -}) => { - const [accountAPR, setAccountAPR] = useState('-'); - - useEffect(() => { - if (!account_id || !pool_id) return; - - getDCLAccountFee({ - pool_id, - account_id, - }).then((res) => { - if (!res || ONLY_ZEROS.test(res.total_liquidity)) { - setAccountAPR('-'); - } else { - const apr = new Big(res.total_fee) - .div(res.total_liquidity) - .mul(365) - .toFixed(2); - - setAccountAPR(apr + '%'); - } - }); - }, [account_id, pool_id]); - - return accountAPR; -}; - export const useDCLTopBinFee = ({ pool_id, number, From 438f67f2bebe96a2101fdbee7d40f273a9cbb608 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 01:26:21 +0800 Subject: [PATCH 054/204] update --- src/components/d3Chart/DclChart.tsx | 15 ++++++++++----- src/components/pool/YourLiquidityV2.tsx | 10 +++++----- 2 files changed, 15 insertions(+), 10 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index e13b7947f..9fb4af555 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -15,7 +15,7 @@ import { get_account_24_apr, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; -import { sortBy } from 'lodash'; +import { sortBy, debounce } from 'lodash'; import { IChartData, IChartItemConfig, @@ -42,7 +42,6 @@ import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; - export default function DclChart({ pool_id, leftPoint, @@ -86,6 +85,9 @@ export default function DclChart({ const [tokenPriceList, setTokenPriceList] = useState>(); /** constant start */ const appearanceConfig: IPoolChartConfig = config || {}; + let [timerObj, setTimerObj] = useState({ + timer: '' + }); const dragBarWidth = 28; const percentBoxWidth = 44; const min_bar_height = 2; @@ -117,10 +119,13 @@ export default function DclChart({ }); }, []); useEffect(() => { + clearTimeout(timerObj.timer); if (pool_id) { - get_pool_detail(pool_id); - leftPoint && setDragLeftPoint(leftPoint); - rightPoint && setDragRightPoint(rightPoint); + timerObj.timer = setTimeout(() => { + get_pool_detail(pool_id); + leftPoint && setDragLeftPoint(leftPoint); + rightPoint && setDragRightPoint(rightPoint); + }, 500) } }, [pool_id]); useEffect(() => { diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 2d662080d..92d8b16bb 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -63,7 +63,6 @@ import { UpDownButton } from '../portfolio/Tool'; import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; import { PortfolioData } from '../../pages/Portfolio'; import { isMobile } from '~utils/device'; -import { useDCLAccountAPR } from '~state/pool'; import Big from 'big.js'; import { useWalletSelector } from '~context/WalletSelectorContext'; const is_mobile = isMobile(); @@ -1865,10 +1864,11 @@ function UserLiquidityLineStyleGroup1({ const { accountId } = useWalletSelector(); - const poolApr = useDCLAccountAPR({ - pool_id, - account_id: accountId, - }); + // const poolApr = useDCLAccountAPR({ + // pool_id, + // account_id: accountId, + // }); + const poolApr = ''; const [claim_loading, set_claim_loading] = useState(false); From c4c5703ea1bbd734f7cde4e4692fd7cbf0264f5c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 09:21:51 +0800 Subject: [PATCH 055/204] add recent log in table pool --- src/components/d3Chart/DclChart.tsx | 4 ++-- src/pages/stable/StableSwapPage.tsx | 10 ++++++++++ src/services/swapV3.ts | 16 +++++++++++----- 3 files changed, 23 insertions(+), 7 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 9fb4af555..c056a8324 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -86,7 +86,7 @@ export default function DclChart({ /** constant start */ const appearanceConfig: IPoolChartConfig = config || {}; let [timerObj, setTimerObj] = useState({ - timer: '' + timer: '', }); const dragBarWidth = 28; const percentBoxWidth = 44; @@ -125,7 +125,7 @@ export default function DclChart({ get_pool_detail(pool_id); leftPoint && setDragLeftPoint(leftPoint); rightPoint && setDragRightPoint(rightPoint); - }, 500) + }, 500); } }, [pool_id]); useEffect(() => { diff --git a/src/pages/stable/StableSwapPage.tsx b/src/pages/stable/StableSwapPage.tsx index 97f9ec801..fafbcd033 100644 --- a/src/pages/stable/StableSwapPage.tsx +++ b/src/pages/stable/StableSwapPage.tsx @@ -29,6 +29,9 @@ import BigNumber from 'bignumber.js'; import { getStablePoolFromCache, Pool, StablePool } from '../../services/pool'; import { getStableSwapTabKey } from './StableSwapPageUSN'; import { STABLE_TOKEN_IDS } from '../../services/near'; +import { RecentTransactions } from '../pools/DetailsPage'; +import { useTokens } from '~state/token'; + export const DEFAULT_ACTIONS = ['add_liquidity', 'remove_liquidity']; const STABLE_TOKENS = ['USDT.e', 'USDC', 'DAI']; @@ -79,6 +82,7 @@ function StableSwapPage({ pool }: { pool: Pool }) { const nearBalances = useWalletTokenBalances( tokens?.map((token) => token.id) || [] ); + const pool_tokens = useTokens(pool?.tokenIds); const changeAction = (actionName: string) => { localStorage.setItem(REF_STABLE_SWAP_TAB_KEY, actionName); @@ -127,6 +131,12 @@ function StableSwapPage({ pool }: { pool: Pool }) { {} {renderModule(actionName)} {} +
+ +
); } diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 404b56f36..ec084310a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -444,7 +444,7 @@ export const v3Swap = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: true, + registrationOnly: false, }), ], }); @@ -916,7 +916,7 @@ export const add_liquidity = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: true, + registrationOnly: false, }), ], }); @@ -1043,7 +1043,7 @@ export const batch_add_liquidity = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: true, + registrationOnly: false, }), ], }); @@ -1390,7 +1390,10 @@ export const batch_remove_liquidity_contract = async ({ transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ - storageDepositAction({ amount: neededStorage, registrationOnly: true }), + storageDepositAction({ + amount: neededStorage, + registrationOnly: false, + }), ], }); } @@ -1455,7 +1458,10 @@ export const claim_all_liquidity_fee = async ({ transactions.unshift({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ - storageDepositAction({ amount: neededStorage, registrationOnly: true }), + storageDepositAction({ + amount: neededStorage, + registrationOnly: false, + }), ], }); } From 8e5ee94ce1ad098b3a18db9e09c649e094b723e9 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 18:23:25 +0800 Subject: [PATCH 056/204] fix bugs --- src/pages/pools/DetailsPage.tsx | 76 +++++++++++++-------------------- src/services/commonV3.ts | 36 +++++++++++++--- src/services/indexer.ts | 2 + src/services/swapV3.ts | 15 ++++--- 4 files changed, 70 insertions(+), 59 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 154d826a4..13ce33833 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -32,12 +32,6 @@ import Loading from '~components/layout/Loading'; import { FarmMiningIcon } from '~components/icon/FarmMining'; import { FarmStamp, FarmStampNew } from '~components/icon/FarmStamp'; import { ChartLoading } from '~components/icon/Loading'; -import { - REF_FARM_CONTRACT_ID, - REF_FI_CONTRACT_ID, - STABLE_POOL_ID, - REF_FARM_BOOST_CONTRACT_ID, -} from '~services/near'; import { PoolSlippageSelector } from '~components/forms/SlippageSelector'; import { Link } from 'react-router-dom'; import { canFarm } from '~services/pool'; @@ -58,7 +52,6 @@ import InputAmount from '~components/forms/InputAmount'; import { isMobile } from '~utils/device'; import ReactModal from 'react-modal'; import { toRealSymbol } from '~utils/token'; - import { BackArrowWhite, BackArrowGray, @@ -82,7 +75,10 @@ import { } from '~components/button/Button'; import { wallet } from '~services/near'; import { BreadCrumb } from '~components/layout/BreadCrumb'; -import { LP_TOKEN_DECIMALS } from '../../services/m-token'; +import { + LP_TOKEN_DECIMALS, + LP_STABLE_TOKEN_DECIMALS, +} from '../../services/m-token'; import { ResponsiveContainer, LineChart, @@ -176,6 +172,7 @@ import { getEffectiveFarmList, sort_tokens_by_base } from '~services/commonV3'; import { openUrl } from '../../services/commonV3'; import { numberWithCommas } from '../Orderly/utiles'; import { HiOutlineExternalLink, HiOutlineLink } from 'react-icons/hi'; +const STABLE_POOL_IDS = getConfig().STABLE_POOL_IDS; interface ParamTypes { id: string; @@ -1477,31 +1474,22 @@ export function RecentTransactions({ }); const renderLiquidityTransactions = liquidityTransactions.map((tx) => { - const swapIn = tokens.find((t) => t.id === tx.token_in); - - const swapOut = tokens.find((t) => t.id === tx.token_out); - - if (!swapIn || !swapOut) return null; - - const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_in); - const displayInAmount = - Number(AmountIn) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(AmountIn, 6)); - - const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_out); - - const displayOutAmount = - Number(AmountOut) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(AmountOut, 6)); + const { shares, pool_id } = tx; + let lp_amount; + if (new Set(STABLE_POOL_IDS || []).has(pool_id.toString())) { + lp_amount = toReadableNumber(LP_STABLE_TOKEN_DECIMALS, shares || '0'); + } else { + lp_amount = toReadableNumber(LP_TOKEN_DECIMALS, shares || '0'); + } + const display_lp_amount = + Number(lp_amount) < 0.001 + ? '<0.001' + : numberWithCommas(toPrecision(lp_amount, 3)); const txLink = ( @@ -1518,29 +1506,23 @@ export function RecentTransactions({ - - {displayInAmount} - - - - {toRealSymbol(swapIn.symbol)} - - - + - - - {displayOutAmount} - - - - {toRealSymbol(swapOut.symbol)} + + {display_lp_amount} - {tx.timestamp} - - {txLink} + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + ); diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 2326ad75c..3d6e50e5e 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1380,7 +1380,6 @@ export function divide_liquidities_into_bins({ total_x_amount_in_bin_i = total_x_amount_in_bin_i.plus(token_x); total_y_amount_in_bin_i = total_y_amount_in_bin_i.plus(token_y); }); - // get L in this bin const bin_i_L = get_l_amount_by_condition({ left_point: bin_i_point_start, @@ -1394,6 +1393,8 @@ export function divide_liquidities_into_bins({ total_y_amount_in_bin_i.toFixed() ), poolDetail, + slots: slots_in_bin_i, + binWidth, }); // @@ -1468,7 +1469,7 @@ export function get_x_y_amount_by_condition({ /** * * @param param0 - * @returns + * @returns bin * token_x_amount、token_y_amount ---> NonDivisible */ export function get_l_amount_by_condition({ @@ -1477,21 +1478,44 @@ export function get_l_amount_by_condition({ token_x_amount, token_y_amount, poolDetail, + slots, + binWidth, }: { left_point: number; right_point: number; token_x_amount: string; token_y_amount: string; poolDetail: PoolInfo; + slots?: IChartData[]; + binWidth?: number; }) { let L; - const { current_point } = poolDetail; + const { current_point, point_delta } = poolDetail; // in range if (current_point >= left_point && right_point > current_point) { - if (Big(token_y_amount).gt(0)) { - L = getLByTokenY(left_point, current_point, token_y_amount); + // 中文 已知这个 bin 里每一个slot的高度,直接加权算 + if (slots) { + let L_temp = Big(0); + slots.forEach((slot: IChartData) => { + const { liquidity } = slot; + L_temp = L_temp.plus(Big(liquidity || 0).mul(point_delta)); + }); + L = L_temp.div(binWidth).toFixed(); } else { - L = getLByTokenX(current_point + 1, right_point, token_x_amount); + let lx = '0'; + let ly = '0'; + if (Big(token_y_amount).gt(0)) { + ly = getLByTokenY(left_point, current_point + 1, token_y_amount); + } + if (Big(token_x_amount).gt(0)) { + lx = getLByTokenX(current_point, right_point, token_x_amount); + } + if (Big(lx).gt(0)) { + L = lx; + } + if (Big(ly).gt(0)) { + L = ly; + } } } // only y token diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 2c3877107..b9de546c6 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -390,6 +390,8 @@ export interface ClassicPoolLiquidtyRecentTransaction { amount_in: string; amount_out: string; tx_id: string; + shares?: string; + pool_id?: string; } export const getClassicPoolLiquidtyRecentTransaction = async (props: { diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index ec084310a..b41d47dcd 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -444,7 +444,7 @@ export const v3Swap = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: false, + registrationOnly: neededStorage == '0.5', }), ], }); @@ -763,7 +763,10 @@ export const get_user_storage_detail = async ({ size }: { size: number }) => { user_id, }, }); - + // first register + if (!detail) { + return '0.5'; + } const { max_slots, cur_order_slots, @@ -916,7 +919,7 @@ export const add_liquidity = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: false, + registrationOnly: neededStorage == '0.5', }), ], }); @@ -1043,7 +1046,7 @@ export const batch_add_liquidity = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: false, + registrationOnly: neededStorage == '0.5', }), ], }); @@ -1392,7 +1395,7 @@ export const batch_remove_liquidity_contract = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: false, + registrationOnly: neededStorage == '0.5', }), ], }); @@ -1460,7 +1463,7 @@ export const claim_all_liquidity_fee = async ({ functionCalls: [ storageDepositAction({ amount: neededStorage, - registrationOnly: false, + registrationOnly: neededStorage == '0.5', }), ], }); From d8a6767469403800f8e0dd10f8cc9de01efedc61 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 21:44:33 +0800 Subject: [PATCH 057/204] update --- src/components/pool/RemoveOldPoolV3.tsx | 523 ++++++++++++++++++++++ src/components/pool/YourLiquidityV2.tsx | 45 +- src/pages/pools/DetailsPage.tsx | 89 ++-- src/pages/poolsV3/YourLiquidityPageV3.tsx | 6 +- src/services/indexer.ts | 1 + src/services/swapV3.ts | 24 - 6 files changed, 617 insertions(+), 71 deletions(-) create mode 100644 src/components/pool/RemoveOldPoolV3.tsx diff --git a/src/components/pool/RemoveOldPoolV3.tsx b/src/components/pool/RemoveOldPoolV3.tsx new file mode 100644 index 000000000..23e2e8f69 --- /dev/null +++ b/src/components/pool/RemoveOldPoolV3.tsx @@ -0,0 +1,523 @@ +import { path } from 'animejs'; +import React, { useEffect, useMemo, useState, useContext, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { WalletContext } from '../../utils/wallets-integration'; +import { useHistory } from 'react-router-dom'; +import { Card } from '~components/card/Card'; +import { isMobile } from '~utils/device'; +import { ModalClose } from '~components/icon'; +import { TokenMetadata } from '../../services/ft-contract'; +import { SwitchButton, Slider } from '~components/icon/V3'; +import { + GradientButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from '../../components/button/Button'; +import { PoolSlippageSelectorV3 } from '~components/forms/SlippageSelector'; +import Modal from 'react-modal'; +import BigNumber from 'bignumber.js'; +import { + toPrecision, + toReadableNumber, + toNonDivisibleNumber, + formatWithCommas, +} from '~utils/numbers'; +import { + getPriceByPoint, + CONSTANT_D, + UserLiquidityInfo, + getXAmount_per_point_by_Lx, + getYAmount_per_point_by_Ly, + sort_tokens_by_base, +} from '../../services/commonV3'; +import { PoolInfo, remove_liquidity } from '../../services/swapV3'; +import _ from 'lodash'; +import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; +export const RemoveOldPoolV3 = (props: any) => { + const { + tokenMetadata_x_y, + poolDetail, + userLiquidity, + tokenPriceList, + isLegacy, + ...restProps + }: { + tokenMetadata_x_y: TokenMetadata[]; + poolDetail: PoolInfo; + userLiquidity: UserLiquidityInfo; + tokenPriceList: any; + isLegacy?: boolean; + restProps: any; + } = props; + const [slippageTolerance, setSlippageTolerance] = useState(0.5); + const [tokenXAmount, setTokenXAmount] = useState(''); + const [tokenYAmount, setTokenYAmount] = useState(''); + const [liquidityAmount, setLiquidityAmount] = useState(''); + const [removeAmount, setRemoveAmount] = useState(''); + const [removeTokenXAmount, setRemoveTokenXAmount] = useState(''); + const [removeTokenYAmount, setRemoveTokenYAmount] = useState(''); + const [isInrange, setIsInrange] = useState(true); + const [removeLoading, setRemoveLoading] = useState(false); + const [rateDirection, setRateDirection] = useState(true); + const [removePercentAmount, setRemovePercentAmount] = useState('100'); + const [removePercentList] = useState([0, 25, 50, 75, 100]); + const { globalState } = useContext(WalletContext); + const v3PoolRemoveRef = useRef(null); + const sliderRef = useRef(null); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + if (tokenMetadata_x_y && userLiquidity && poolDetail) { + const { current_point } = poolDetail; + const { left_point, right_point } = userLiquidity; + if (current_point >= left_point && right_point > current_point) { + setIsInrange(true); + } else { + setIsInrange(false); + } + get_liquidity_x_y(); + getLiquidityAmount(); + } + }, [tokenMetadata_x_y, userLiquidity, poolDetail]); + useEffect(() => { + if (liquidityAmount) { + changeRemoveAmount('100'); + } + }, [liquidityAmount]); + useEffect(() => { + if (v3PoolRemoveRef.current) { + v3PoolRemoveRef.current.style.backgroundSize = `${removePercentAmount}% 100%`; + } + if (sliderRef.current) { + sliderRef.current.style.left = `${+removePercentAmount}%`; + const marginLeft = -13 - (20 * +removePercentAmount) / 100; + sliderRef.current.style.marginLeft = `${marginLeft}px`; + } + }, [removePercentAmount]); + function get_liquidity_x_y() { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { left_point, right_point, amount: L } = userLiquidity; + const { current_point } = poolDetail; + // in range + if (current_point >= left_point && right_point > current_point) { + const tokenYAmount = getY(left_point, current_point, L, tokenY); + const tokenXAmount = getX(current_point + 1, right_point, L, tokenX); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + setTokenXAmount(new BigNumber(tokenXAmount).plus(amountx).toFixed()); + setTokenYAmount(new BigNumber(tokenYAmount).plus(amounty).toFixed()); + } + // only y token + if (current_point >= right_point) { + const tokenYAmount = getY(left_point, right_point, L, tokenY); + setTokenYAmount(tokenYAmount); + } + // only x token + if (left_point > current_point) { + const tokenXAmount = getX(left_point, right_point, L, tokenX); + setTokenXAmount(tokenXAmount); + } + } + function getLiquidityPrice() { + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + const tokenYTotalPrice = new BigNumber(tokenYAmount || 0).multipliedBy( + priceY + ); + const tokenXTotalPrice = new BigNumber(tokenXAmount || 0).multipliedBy( + priceX + ); + let total_price = tokenYTotalPrice.plus(tokenXTotalPrice); + total_price = new BigNumber(removePercentAmount) + .multipliedBy(total_price) + .dividedBy(100); + if (total_price.isEqualTo(0)) { + return '$0'; + } else if (total_price.isLessThan('0.01')) { + return '$<0.01'; + } else { + return `$` + formatWithCommas(toPrecision(total_price.toFixed(), 2)); + } + } + } + function getLiquidityAmount() { + const { amount } = userLiquidity; + setLiquidityAmount(amount); + } + + function displayLiquidityAmount() { + if (liquidityAmount) { + return toPrecision(liquidityAmount, 3); + } + } + function getY( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + + function getPoolFee() { + if (poolDetail) { + return poolDetail.fee / 10000; + } + return ''; + } + function changeRemoveAmount(value: string) { + setRemovePercentAmount(value); + const amount = new BigNumber(liquidityAmount) + .multipliedBy(value) + .dividedBy(100) + .toFixed(0, 1); + setRemoveAmount(amount); + getMinimumInfo(amount); + } + function getMinimumInfo(amount: string) { + if (liquidityAmount) { + const proportion = new BigNumber(amount || 0).dividedBy(liquidityAmount); + setRemoveTokenXAmount( + proportion.multipliedBy(tokenXAmount || '0').toFixed() + ); + setRemoveTokenYAmount( + proportion.multipliedBy(tokenYAmount || '0').toFixed() + ); + } + } + function getMinTokenAmount() { + const rate = 100 - slippageTolerance; + const result: any = {}; + if (removeTokenXAmount) { + const minX = new BigNumber(removeTokenXAmount || 0).multipliedBy( + rate / 100 + ); + let displayX = ''; + if (minX.isEqualTo(0)) { + displayX = '0'; + } else if (minX.isLessThan(0.001)) { + displayX = '<0.001'; + } else { + displayX = toPrecision(minX.toFixed(), 3); + } + result.minX = minX.toFixed(); + result.displayX = displayX; + } + if (removeTokenYAmount) { + const minY = new BigNumber(removeTokenYAmount || 0).multipliedBy( + rate / 100 + ); + let displayY = ''; + if (minY.isEqualTo(0)) { + displayY = '0'; + } else if (minY.isLessThan(0.001)) { + displayY = '<0.001'; + } else { + displayY = toPrecision(minY.toFixed(), 3); + } + result.minY = minY.toFixed(); + result.displayY = displayY; + } + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + const tokenXTotalPrice = new BigNumber(result.minX || 0).multipliedBy( + priceX + ); + const tokenYTotalPrice = new BigNumber(result.minY || 0).multipliedBy( + priceY + ); + const total_price = tokenYTotalPrice.plus(tokenXTotalPrice); + if (total_price.isEqualTo(0)) { + result.minPrice = '$0'; + } else if (total_price.isLessThan('0.001')) { + result.minPrice = '<$0.001'; + } else { + result.minPrice = `$` + toPrecision(total_price.toFixed(), 3); + } + } + return result; + } + function remove() { + setRemoveLoading(true); + const [tokenX, tokenY] = tokenMetadata_x_y; + const { lpt_id, mft_id } = userLiquidity; + + sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); + + remove_liquidity({ + token_x: tokenX, + token_y: tokenY, + lpt_id, + mft_id, + amount: removeAmount, + min_amount_x: toNonDivisibleNumber(tokenX.decimals, MINDATA.minX), + min_amount_y: toNonDivisibleNumber(tokenY.decimals, MINDATA.minY), + isLegacy, + }); + } + function switchRate() { + setRateDirection(!rateDirection); + } + function getCurrentPrice(type: string) { + if (poolDetail && tokenMetadata_x_y) { + const { current_point } = poolDetail; + const [tokenX, tokenY] = tokenMetadata_x_y; + const rate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + let price = getPriceByPoint(current_point, rate); + if (type == 'l') { + price = new BigNumber('1').dividedBy(price).toFixed(); + } + + const price_big = new BigNumber(price); + if (price_big.isLessThan('0.001')) { + return '<0.001'; + } else { + return toPrecision(price, 6); + } + } + } + function getTokenPrice(type: string) { + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + if (type == 'l') { + return tokenPriceList[tokenX.id]?.price || '-'; + } + if (type == 'r') { + return tokenPriceList[tokenY.id]?.price || '-'; + } + } + return '-'; + } + const MINDATA: { + minX: string; + displayX: string; + minY: string; + displayY: string; + minPrice: string; + } = getMinTokenAmount(); + const isRemoveLiquidityDisabled = !( + +removeAmount > 0 && + new BigNumber(removeAmount || 0).isLessThanOrEqualTo(liquidityAmount || 0) + ); + const tokens = sort_tokens_by_base(tokenMetadata_x_y); + return ( + + +
+ + + +
+ +
+
+
+
+
+ + +
+ + {tokens[0]?.symbol}/{tokens[1]?.symbol} + +
+ {getLiquidityPrice()} +
+
+
+ {removePercentList.map((p) => { + return ( +
{ + if (isLegacy) return; + changeRemoveAmount(p.toString()); + }} + > + + {p}% + + +
+ ); + })} +
+
+ { + changeRemoveAmount(e.target.value); + }} + disabled={isLegacy ? true : false} + value={removePercentAmount} + type="range" + className={`w-full ${ + isLegacy ? 'pause cursor-not-allowed' : 'cursor-pointer' + }`} + style={{ backgroundSize: '100% 100%' }} + min="0" + max="100" + step="any" + /> +
+ + + {toPrecision(removePercentAmount.toString(), 0)}% + +
+
+
+
+ +
+
+
+ + + +
+
+
+ + {MINDATA.displayX || '-'} + +
+ + + {tokenMetadata_x_y && tokenMetadata_x_y[0].symbol} + +
+
+
+ + {MINDATA.displayY || '-'} + +
+ + + {tokenMetadata_x_y && tokenMetadata_x_y[1].symbol} + +
+
+
+ {isSignedIn ? ( + + } + /> + + ) : ( +
+ +
+ )} +
+
+ ); +}; diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 92d8b16bb..94d033ec5 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -43,6 +43,7 @@ import { get_pool_name, openUrl, sort_tokens_by_base, + get_account_24_apr, } from '../../services/commonV3'; import BigNumber from 'bignumber.js'; import { @@ -65,6 +66,9 @@ import { PortfolioData } from '../../pages/Portfolio'; import { isMobile } from '~utils/device'; import Big from 'big.js'; import { useWalletSelector } from '~context/WalletSelectorContext'; +import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; +import { formatPercentage } from '../../components/d3Chart/utils'; +import { getDCLAccountFee } from '../../services/indexer'; const is_mobile = isMobile(); const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const LiquidityContext = createContext(null); @@ -1697,13 +1701,11 @@ function UserLiquidityLineStyleGroup1({ // liquidityDetail, // showAddBox, // } = useContext(LiquidityContext); - const [hover, setHover] = useState(false); - const publicData = groupYourLiquidityList[0]; - const [showRemoveBox, setShowRemoveBox] = useState(false); - + const [accountAPR, setAccountAPR] = useState(''); + const [claim_loading, set_claim_loading] = useState(false); const { tokenMetadata_x_y, fee, @@ -1714,8 +1716,26 @@ function UserLiquidityLineStyleGroup1({ poolDetail, go_farm, } = publicData; - + const tokens = sort_tokens_by_base(tokenMetadata_x_y); + const { accountId } = useWalletSelector(); const history = useHistory(); + useEffect(() => { + if (poolDetail && tokenPriceList) { + get_24_apr(); + } + }, [poolDetail, tokenPriceList]); + async function get_24_apr() { + let apr_24 = ''; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + if (dcl_fee_result) { + // 24h 利润 + apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + } + setAccountAPR(formatPercentage(apr_24)); + } const groupList = () => { const your_liquidity = groupYourLiquidityList.reduce((prev, cur) => { @@ -1859,19 +1879,6 @@ function UserLiquidityLineStyleGroup1({ const url_pool_id = get_pool_name(pool_id); history.push(`/poolV2/${url_pool_id}`); } - - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - - const { accountId } = useWalletSelector(); - - // const poolApr = useDCLAccountAPR({ - // pool_id, - // account_id: accountId, - // }); - const poolApr = ''; - - const [claim_loading, set_claim_loading] = useState(false); - function claimRewards() { if (!canClaim) return; @@ -2016,7 +2023,7 @@ function UserLiquidityLineStyleGroup1({
- {poolApr} + {accountAPR} {related_seed_info.your_apr && ( diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 13ce33833..511a04a59 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1443,9 +1443,11 @@ export function RecentTransactions({ ); return ( - + - + {displayInAmount} @@ -1454,7 +1456,7 @@ export function RecentTransactions({ - + {displayOutAmount} @@ -1464,7 +1466,7 @@ export function RecentTransactions({ - + {tx.timestamp} {txLink} @@ -1474,18 +1476,17 @@ export function RecentTransactions({ }); const renderLiquidityTransactions = liquidityTransactions.map((tx) => { - const { shares, pool_id } = tx; - let lp_amount; - if (new Set(STABLE_POOL_IDS || []).has(pool_id.toString())) { - lp_amount = toReadableNumber(LP_STABLE_TOKEN_DECIMALS, shares || '0'); - } else { - lp_amount = toReadableNumber(LP_TOKEN_DECIMALS, shares || '0'); - } - const display_lp_amount = - Number(lp_amount) < 0.001 - ? '<0.001' - : numberWithCommas(toPrecision(lp_amount, 3)); - + const { amounts } = tx; + const renderTokens: any[] = []; + const amountsObj: any[] = JSON.parse(amounts.replace(/\'/g, '"')); + amountsObj.forEach((amount: string, index) => { + if (Big(amount || 0).gt(0)) { + renderTokens.push({ + token: tokens[index], + amount: toReadableNumber(tokens[index].decimals, amountsObj[index]), + }); + } + }); const txLink = ( - + + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Add'} @@ -1505,13 +1510,34 @@ export function RecentTransactions({ - - - {display_lp_amount} - + + {renderTokens.map((renderToken, index) => { + return ( + <> + + {formatNumber(renderToken.amount)} + + + + {toRealSymbol(renderToken.token.symbol)} + + {index !== renderTokens.length - 1 ? ( + + + ) : null} + + ); + })} - + { @@ -1582,9 +1608,9 @@ export function RecentTransactions({
-
+
{tab === 'liquidity' && ( -
+
{tab === 'liquidity' && ( ); } + +export const formatNumber = (v: string | number) => { + const big = Big(v || 0); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.001)) { + return '<0.001'; + } else { + return big.toFixed(3, 1); + } +}; diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index 4fa39f02d..bc283e907 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -51,6 +51,7 @@ import { import BigNumber from 'bignumber.js'; import { FarmBoost, getBoostTokenPrices, Seed } from '../../services/farm'; import { RemovePoolV3 } from '~components/pool/RemovePoolV3'; +import { RemoveOldPoolV3 } from '~components/pool/RemoveOldPoolV3'; import { AddPoolV3 } from '~components/pool/AddPoolV3'; import { PoolTabV3 } from '~components/pool/PoolTabV3'; import { @@ -1445,8 +1446,9 @@ function UserLiquidityLine_old({
+ {/* todo */} {showRemoveBox ? ( - { setShowRemoveBox(false); @@ -1466,7 +1468,7 @@ function UserLiquidityLine_old({ transform: 'translate(-50%, -50%)', }, }} - > + > ) : null} { - throw new Error(`${tokenB.id} doesn't exist.`); - }); - - if (DCLRegistered === null) { - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - { - methodName: 'storage_deposit', - args: { - registration_only: true, - account_id: getCurrentWallet()?.wallet?.getAccountId(), - }, - gas: '30000000000000', - amount: '0.5', - }, - ], - }); - } - const new_point = pool_id.split(V3_POOL_SPLITER)[0] === tokenA.id ? point : -point; From a5f27e9822e8da48e4bdf2e1d99d673c3efa0047 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 3 Jul 2023 22:44:55 +0800 Subject: [PATCH 058/204] update --- src/components/pool/RemovePoolV3.tsx | 10 +- src/components/pool/YourLiquidityV2.tsx | 2 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 65 +- .../poolsV3/AddYourLiquidityPageV3Copy.tsx | 2483 ----------------- 4 files changed, 15 insertions(+), 2545 deletions(-) delete mode 100644 src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 489b3a5ba..2aec9cf50 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -55,7 +55,7 @@ import DclChart from '../../components/d3Chart/DclChart'; export type RemoveType = 'left' | 'right' | 'all'; /** - * 遗产nft 的处理,采用老的弹窗ui?todo + * 中文 * @param props * @returns */ @@ -477,7 +477,7 @@ export const RemovePoolV3 = (props: any) => { */ let total_token_x_amount = Big(0); let total_token_y_amount = Big(0); - let total_value = Big(0); + let minimum_total_value = Big(0); const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); if (whole_deleted_nfts.length) { whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { @@ -519,12 +519,14 @@ export const RemovePoolV3 = (props: any) => { const priceY = tokenPriceList[tokenY.id]?.price || 0; const token_x_value = total_token_x_amount.mul(priceX); const token_y_value = total_token_y_amount.mul(priceY); - total_value = token_x_value.plus(token_y_value); + minimum_total_value = token_x_value.plus(token_y_value); } + const rate = (100 - slippageTolerance) / 100; return { total_token_x_amount: total_token_x_amount.toFixed(), total_token_y_amount: total_token_y_amount.toFixed(), - total_value: total_value.toFixed(), + minimum_total_value: minimum_total_value.toFixed(), + total_value: minimum_total_value.div(rate).toFixed(), }; } function get_un_deleted_range(liquidity: UserLiquidityInfo) { diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 94d033ec5..ac5e04b5d 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1731,7 +1731,7 @@ function UserLiquidityLineStyleGroup1({ account_id: accountId, }); if (dcl_fee_result) { - // 24h 利润 + // 24h 利润 中文 apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); } setAccountAPR(formatPercentage(apr_24)); diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 04850f588..9c63fd5c3 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -251,7 +251,7 @@ export default function AddYourLiquidityPageV3() { // new useEffect(() => { // init - if (currentSelectedPool) { + if (currentSelectedPool?.pool_id) { const { current_point, point_delta } = currentSelectedPool; const n = get_slot_number_in_a_bin(); const bin_width = n * point_delta; @@ -2727,7 +2727,7 @@ function SetPointsComponent() { // init useEffect(() => { - if (currentSelectedPool && !switch_pool_loading) { + if (currentSelectedPool?.pool_id && !switch_pool_loading) { const { current_point } = currentSelectedPool; const right_point = handlePointToAppropriatePoint( current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER @@ -2743,7 +2743,7 @@ function SetPointsComponent() { } }, [currentSelectedPool, switch_pool_loading]); - // 左侧改变===》点位 + // 中文 左侧改变===》点位 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin @@ -3552,7 +3552,11 @@ function PointInputComponent({ onChange={({ target }) => { setInputStatus(true); const inputPrice = target.value; - setCustomPrice(inputPrice); + if (Big(target.value || 0).lt(0)) { + setCustomPrice('0'); + } else { + setCustomPrice(inputPrice); + } }} />
@@ -3607,59 +3611,6 @@ export function IntegerInputComponent({
); } -export function IntegerInputComponentCopy({ - value, - setValue, - disabled, - className, - max, - onBlur, -}: any) { - const removeLeadingZeros = (s: string) => { - const oldLen = s.length; - s = s.replace(/^0+/, ''); - - if (s.length === 0 && oldLen > 0) { - s = '0'; - } - - if (max && Number(s) > max) { - return max; - } - - return s; - }; - - const handleChange = (val: string) => { - val = val.replace(/[^\d]/g, ''); - val = removeLeadingZeros(val); - setValue(val); - }; - - return ( -
- { - if (onBlur) { - onBlur(); - } else if (!target.value) { - setValue(1); - } - }} - onChange={({ target }) => { - handleChange(target.value); - }} - /> -
- ); -} - function OneSide({ show }: { show: boolean }) { return (
(null); - const [tokenY, setTokenY] = useState(null); - const [tokenXAmount, setTokenXAmount] = useState(''); - const [tokenYAmount, setTokenYAmount] = useState(''); - const [listPool, setListPool] = useState([]); - const [buttonHover, setButtonHover] = useState(false); - const [tokenPriceList, setTokenPriceList] = useState>({}); - const [currentPools, setCurrentPools] = - useState>(null); - const [onlyAddYToken, setOnlyAddYToken] = useState(false); // real - const [onlyAddXToken, setOnlyAddXToken] = useState(false); // real - const [invalidRange, setInvalidRange] = useState(false); // real - const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); - const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - const [leftPoint, setLeftPoint] = useState(0); // real - const [rightPoint, setRightPoint] = useState(0); // real - const [currentPoint, setCurrentPoint] = useState(); // real - const [currentSelectedPool, setCurrentSelectedPool] = - useState(null); // real - const [feeBoxStatus, setFeeBoxStatus] = useState(true); - const [buttonSort, setButtonSort] = useState(false); - const [selectHover, setSelectHover] = useState(false); - const [viewPoolHover, setViewPoolHover] = useState(false); - const [topPairs, setTopPairs] = useState([]); - const [seed_list, set_seed_list] = useState(); - const [related_seeds, set_related_seeds] = useState([]); - // callBack handle - useAddAndRemoveUrlHandle(); - const history = useHistory(); - const triTokenIds = useTriTokenIdsOnRef(); - const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); - const triTokens = useTriTokens(); - const balances = useTokenBalances(); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const nearBalance = useDepositableBalance('NEAR'); - const intl = useIntl(); - const intl_select = intl.formatMessage({ id: 'select_s' }); - const OPEN_CREATE_POOL_ENTRY = false; - useEffect(() => { - getBoostTokenPrices().then(setTokenPriceList); - get_list_pools(); - get_seeds(); - }, []); - useEffect(() => { - if (tokenX) { - const tokenXId = tokenX.id; - if (tokenXId) { - if (isSignedIn) { - ftGetBalance(tokenXId).then((available: string) => - setTokenXBalanceFromNear( - toReadableNumber( - tokenX.decimals, - tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - if (tokenY) { - const tokenYId = tokenY.id; - if (tokenYId) { - if (isSignedIn) { - ftGetBalance(tokenYId).then((available: string) => - setTokenYBalanceFromNear( - toReadableNumber( - tokenY.decimals, - tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - }, [tokenX, tokenY, isSignedIn, nearBalance]); - useEffect(() => { - if (listPool.length > 0) { - get_init_pool(); - } - }, [listPool]); - useEffect(() => { - if (currentSelectedPool && tokenX && tokenY) { - const { fee } = currentSelectedPool; - const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); - history.replace(`#${link}`); - if (seed_list && currentSelectedPool.pool_id) { - get_optional_seeds(); - } - } - }, [currentSelectedPool, tokenX, tokenY, seed_list]); - useEffect(() => { - if (tokenX && tokenY) { - searchPools(); - } - }, [tokenX, tokenY, tokenPriceList, listPool]); - useEffect(() => { - if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { - getTopPairs(); - } - }, [listPool, tokenPriceList]); - async function getTopPairs() { - const listPromise = listPool.map(async (p: PoolInfo) => { - const token_x = p.token_x; - const token_y = p.token_y; - - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged) - .toFixed(); - const tvlx = - Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * - Number(pricey); - - p.tvl = tvlx + tvly; - - return p; - }); - const list: PoolInfo[] = await Promise.all(listPromise); - list.sort((b: PoolInfo, a: PoolInfo) => { - return a.tvl - b.tvl; - }); - const top3 = list.slice(0, 3); - setTopPairs(top3); - } - - if (!refTokens || !triTokens || !triTokenIds) return ; - const allTokens = getAllTokens(refTokens, triTokens); - const nearSwapTokens = allTokens.filter((token) => token.onRef); - async function get_seeds() { - const seeds = await get_all_seeds(); - set_seed_list(seeds); - } - function get_optional_seeds() { - const optional_seeds = get_matched_seeds_for_dcl_pool({ - seeds: seed_list, - pool_id: currentSelectedPool.pool_id, - }); - if (optional_seeds.length) { - set_related_seeds(optional_seeds); - } else { - set_related_seeds([]); - } - } - async function get_init_pool() { - let tokenx_id, tokeny_id, pool_fee; - const hash = decodeURIComponent(location.hash); - if (hash.indexOf('<>') > -1) { - // new link - [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); - } else { - // old link - [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); - } - if (tokenx_id && tokeny_id && pool_fee) { - const tokenx = await ftGetTokenMetadata(tokenx_id); - const tokeny = await ftGetTokenMetadata(tokeny_id); - setTokenX(tokenx); - setTokenY(tokeny); - } - } - function goYourLiquidityPage() { - if (history.length == 2) { - history.push('/yourliquidity'); - } else { - history.goBack(); - } - } - async function get_list_pools() { - const list: PoolInfo[] = await list_pools(); - if (list.length > 0) { - setListPool(list); - } - } - function searchPools() { - const hash = decodeURIComponent(location.hash); - let url_fee; - if (hash.indexOf('<>') > -1) { - url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; - } else { - url_fee = +hash.slice(1).split('|')[2]; - } - const currentPoolsMap = {}; - if (listPool.length > 0 && tokenX && tokenY) { - const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { - // TODO 增加pool 状态的判断 - const { token_x, token_y, state } = pool; - if ( - (token_x == tokenX.id && token_y == tokenY.id) || - (token_x == tokenY.id && token_y == tokenX.id) - ) - return true; - }); - if (availablePools.length > 0) { - /*** percent start */ - const tvlList: number[] = []; - availablePools.map((p: PoolInfo) => { - const { - total_x, - total_y, - token_x, - token_y, - total_fee_x_charged, - total_fee_y_charged, - } = p; - const firstToken = tokenX.id == token_x ? tokenX : tokenY; - const secondToken = tokenY.id == token_y ? tokenY : tokenX; - const firstTokenPrice = - (tokenPriceList && - tokenPriceList[firstToken.id] && - tokenPriceList[firstToken.id].price) || - '0'; - const secondTokenPrice = - (tokenPriceList && - tokenPriceList[secondToken.id] && - tokenPriceList[secondToken.id].price) || - '0'; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged || 0) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged || 0) - .toFixed(); - const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) - .times(firstTokenPrice) - .toNumber(); - const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) - .times(secondTokenPrice) - .toNumber(); - const totalTvl = tvlx + tvly; - p.tvl = totalTvl; - tvlList.push(totalTvl); - return p; - }); - const sumOfTvlList = _.sum(tvlList); - const tvlPercents = - sumOfTvlList === 0 - ? ['0', '0', '0', '0'] - : availablePools.map((p: PoolInfo) => - scientificNotationToString( - ((p.tvl / sumOfTvlList) * 100).toString() - ) - ); - const nonZeroIndexes: number[] = []; - tvlPercents.forEach((p, index) => { - if (Number(p) > 0) { - nonZeroIndexes.push(index); - } - }); - const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); - const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); - const finalPercents = tvlPercents.map((p, index) => { - if (nonZeroIndexes.includes(index)) { - const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; - return newP; - } - return p; - }); - const maxPercent = _.max(finalPercents); - let maxPercentPool; - availablePools.forEach((pool: PoolInfo, index) => { - const f = pool.fee; - const temp: PoolInfo = { - ...pool, - percent: finalPercents[index], - tvl: tvlList[index], - }; - currentPoolsMap[f] = temp; - if (finalPercents[index] == maxPercent) { - maxPercentPool = temp; - } - }); - // url-fee-pool - const urlFeePool = url_fee - ? currentPoolsMap[url_fee] || { fee: url_fee } - : null; - setCurrentPools(currentPoolsMap); - setCurrentSelectedPool(urlFeePool || maxPercentPool); - } else { - setCurrentPools({}); - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } else { - setCurrentPools({}); - if (tokenX && tokenY) { - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } - } - function switchSelectedFee(fee: number) { - if (tokenX && tokenY && currentPools) { - const pool = currentPools[fee]; - setCurrentSelectedPool(pool || { fee }); - if (!pool) { - setOnlyAddXToken(false); - setOnlyAddYToken(false); - setInvalidRange(false); - } - } - } - function changeTokenXAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenXAmount(amount); - if (sort) { - if (!onlyAddXToken) { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenYAmount(amount_result); - } - } else { - if (!onlyAddYToken) { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } - } - } - function changeTokenYAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenYAmount(amount); - if (sort) { - if (!onlyAddYToken) { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } else { - if (!onlyAddXToken) { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenXAmount(amount_result); - } - } - } - function getTokenYAmountByCondition({ - // real - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - // X-->L - const L = - +amount * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); - // L-->current Y - const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); - // L--> Y - const Y = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1)); - const decimalsRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - - const Y_result = (Y + Yc) * decimalsRate; - return Y_result.toString(); - } - } - function getTokenXAmountByCondition({ - // real - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - let L; - // Yc-->L - if (leftPoint == currentPoint) { - L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); - } else { - // Y-->L - L = - +amount * - ((Math.sqrt(CONSTANT_D) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); - } - const X = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); - const decimalsRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const X_result = X * decimalsRate; - return X_result.toString(); - } - } - function switchFeeBoxStatus() { - setFeeBoxStatus(!feeBoxStatus); - } - function pointChange({ - leftPoint, - rightPoint, - currentPoint, - }: { - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setLeftPoint(leftPoint); - setRightPoint(rightPoint); - setCurrentPoint(currentPoint); - setInvalidRange(false); - setOnlyAddXToken(false); - setOnlyAddYToken(false); - // invalid point - if (leftPoint >= rightPoint) { - setInvalidRange(true); - setTokenXAmount(''); - setTokenYAmount(''); - return; - } - // can only add x token - if (leftPoint > currentPoint) { - setOnlyAddXToken(true); - if (sort) { - setTokenYAmount(''); - } else { - setTokenXAmount(''); - } - return; - } - // can only add y token - if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { - setOnlyAddYToken(true); - if (sort) { - setTokenXAmount(''); - } else { - setTokenYAmount(''); - } - return; - } - if (sort) { - if (tokenXAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } else { - if (tokenXAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - } - function switchButtonSort() { - if (tokenX || tokenY) { - setTokenX(tokenY); - setTokenY(tokenX); - setTokenXAmount(tokenYAmount); - setTokenYAmount(tokenXAmount); - setTokenXBalanceFromNear(tokenYBalanceFromNear); - setTokenYBalanceFromNear(tokenXBalanceFromNear); - } - setButtonSort(!buttonSort); - } - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; - } - } - function goPoolsPage() { - const poolId = currentSelectedPool?.pool_id; - if (poolId) { - const newPoolId = get_pool_name(poolId); - openUrl(`/poolV2/${newPoolId}`); - } else { - localStorage.setItem(REF_FI_POOL_ACTIVE_TAB, 'v2'); - openUrl('/pools'); - } - } - const tokenSort = tokenX?.id == currentSelectedPool?.token_x; - const mobileDevice = isMobile(); - return ( - <> -
-
- -
- - - -
-
-
-
-
-
-
- -
- - - -
-
- {/* left area */} -
-
- { - if (tokenY && tokenY.id == token.id) return; - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - if (tokenX && tokenX.id == token.id) return; - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - className="pt-0 absolute top-8 outline-none left-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - - -
- } - /> -
{ - setViewPoolHover(true); - }} - onMouseLeave={() => { - setViewPoolHover(false); - }} - onClick={goPoolsPage} - className={`flex items-center justify-center bg-viewPoolBgColor rounded-md px-3.5 py-1 mb-3 cursor-pointer ${ - viewPoolHover ? 'text-white' : 'text-primaryText' - }`} - > - - - - -
-
-
-
- - {tokenX ? ( -
- - - {toRealSymbol(tokenX.symbol)} - -
- ) : ( - <> - {} - - - )} - -
- } - onSelect={(token) => { - if (tokenY && tokenY.id == token.id) return; - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - balances={balances} - /> -
-
{ - if (!mobileDevice) { - setButtonHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setButtonHover(false); - } - }} - onTouchStart={() => { - if (mobileDevice) { - setButtonHover(true); - } - }} - onTouchEnd={() => { - if (mobileDevice) { - setButtonHover(false); - } - }} - onClick={switchButtonSort} - className="flex flex-col items-center justify-center border-2 border-switchIconBorderColor w-6 h-6 rounded-lg mx-2 cursor-pointer box-content bg-switchIconBgColor" - > - - -
-
- - {tokenY ? ( -
- - - {toRealSymbol(tokenY.symbol)} - -
- ) : ( - <> - - - )} - -
- } - onSelect={(token: TokenMetadata) => { - if (tokenX && tokenX.id == token.id) return; - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - balances={balances} - /> -
-
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > 0 ? ( - {displayTvl(currentPools[fee].tvl)} - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- - - - - - - -
-
- {/* right area */} - {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} -
-
-
-
- - ); -} -function CreatePoolComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - buttonSort, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - buttonSort: boolean; -}) { - const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); - const [createPoolRate, setCreatePoolRate] = useState(''); - const [rateStatus, setRateStatus] = useState(true); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - useEffect(() => { - if (createPoolRate) { - const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); - setCreatePoolRate(toPrecision(rateString, 6)); - } - }, [buttonSort]); - function getCurrentPriceValue(token: TokenMetadata) { - if (token) { - const price = tokenPriceList[token.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; - } - } - function createPool() { - setCreatePoolButtonLoading(true); - const { fee } = currentSelectedPool; - const pointDelta = POINTDELTAMAP[fee]; - let decimalRate = - Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); - let init_point = getPointByPrice( - pointDelta, - createPoolRate, - decimalRate, - true - ); - const arr = [tokenX.id, tokenY.id]; - arr.sort(); - if (arr[0] !== tokenX.id) { - decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - init_point = getPointByPrice( - pointDelta, - new BigNumber(1).dividedBy(createPoolRate).toFixed(), - decimalRate, - true - ); - create_pool({ - token_a: tokenY.id, - token_b: tokenX.id, - fee: currentSelectedPool.fee, - init_point, - }); - } else { - create_pool({ - token_a: tokenX.id, - token_b: tokenY.id, - fee: currentSelectedPool.fee, - init_point, - }); - } - } - function switchRate() { - setRateStatus(!rateStatus); - } - function getPoolRate() { - if (createPoolRate) { - const rate = 1 / +createPoolRate; - return toPrecision(rate.toString(), 6); - } - return ''; - } - const mobileDevice = isMobile(); - return ( -
-
- - : -
-
- -
-
-

- -

-
-
-

- -

-
- - 1 {toRealSymbol(tokenX?.symbol)} = - -
- { - setCreatePoolRate(target.value); - }} - /> - - {toRealSymbol(tokenY?.symbol)} - -
-
-
- - - -
- {rateStatus ? ( -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue(tokenX)}) - - - - {createPoolRate} {toRealSymbol(tokenY?.symbol)} - -
- ) : ( -
- 1 {toRealSymbol(tokenY?.symbol)} - - ({getCurrentPriceValue(tokenY)}) - - - - {getPoolRate()} {toRealSymbol(tokenX?.symbol)} - -
- )} - - -
-
-
-
-
- {isSignedIn ? ( - - ( - <> - - - )} - /> - - ) : ( - - )} -
- ); -} -function AddLiquidityComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - pointChange, - onlyAddXToken, - onlyAddYToken, - invalidRange, - seeds, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - tokenXAmount: string; - tokenYAmount: string; - tokenXBalanceFromNear: string; - tokenYBalanceFromNear: string; - pointChange: any; - onlyAddXToken: boolean; - onlyAddYToken: boolean; - invalidRange: boolean; - seeds: Seed[]; -}) { - let [leftCustomPrice, setLeftCustomPrice] = useState(''); - let [rightCustomPrice, setRightCustomPrice] = useState(''); - let [leftPoint, setLeftPoint] = useState(0); - let [rightPoint, setRightPoint] = useState(0); - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [currentPoint, setCurrentPoint] = useState(); - const [chartLoading, setChartLoading] = useState(false); - const [noDataForChart, setNoDataForChart] = useState(false); - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const [currentCheckedQuickOption, setCurrentCheckedQuickOption] = useState< - number | string - >(); - const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); - const [quickOptionsMapPoint, setQuickOptionsMapPoint] = useState< - Record - >({}); - const { globalState } = useContext(WalletContext); - const [timer, setTimer] = useState(null); - const [depthData, setDepthData] = useState(null); - const [displayedSeedIndex, setDisplayedSeedIndex] = useState(0); - const isSignedIn = globalState.isSignedIn; - const intl = useIntl(); - const chartDom = useRef(null); - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - // init - useEffect(() => { - const { current_point, point_delta } = currentSelectedPool; - // 5, 10 20 50 - const optionsMapPoints_temp = {}; - quickOptions.forEach((p: number) => { - const { left_p, right_p } = getPointByCondition(p); - optionsMapPoints_temp[p] = { left_p, right_p }; - if (p == 10) { - leftPoint = left_p; - rightPoint = right_p; - setLeftPoint(left_p); - setRightPoint(right_p); - } - }); - // full - const l_p_temp = _.max([current_point - 400000, -800000]); - const r_p_temp = _.min([current_point + 400000, 800000]); - let l_p = Math.floor(l_p_temp / point_delta) * point_delta; - let r_p = Math.floor(r_p_temp / point_delta) * point_delta; - if (r_p - l_p >= POINTRIGHTRANGE) { - l_p = l_p + point_delta; - } - optionsMapPoints_temp['full'] = { left_p: l_p, right_p: r_p }; - // set - setCurrentPoint(current_point); - setQuickOptionsMapPoint(optionsMapPoints_temp); - setCurrentCheckedQuickOption(10); - // show chart data - setChartLoading(true); - setNoDataForChart(false); - clearTimeout(timer); - const timer_latest = setTimeout(() => { - getChartData(); - }, 1000); - setTimer(timer_latest); - }, [currentSelectedPool]); - useEffect(() => { - pointChange({ leftPoint, rightPoint, currentPoint }); - const targetKey = Object.keys(quickOptionsMapPoint).find((key: string) => { - const { left_p, right_p } = quickOptionsMapPoint[key]; - if (left_p == leftPoint && right_p == rightPoint) { - return key; - } - }); - if (targetKey) { - setCurrentCheckedQuickOption(targetKey); - } else { - setCurrentCheckedQuickOption(undefined); - } - if (depthData) { - drawChartData({ - depthData, - left_point: leftPoint, - right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, - }); - } - }, [leftPoint, rightPoint]); - async function getChartData() { - const depthData = await get_pool_marketdepth(currentSelectedPool.pool_id); - const length = drawChartData({ - depthData, - left_point: leftPoint, - right_point: rightPoint, - token_x_decimals, - token_y_decimals, - chartDom, - sort: tokenX.id == token_x, - space_x: 5, - }); - if (length == 0) { - setNoDataForChart(true); - } else { - setDepthData(depthData); - } - setChartLoading(false); - } - function getPointByCondition(p: number) { - const { point_delta, token_x } = currentSelectedPool; - const c_price = getCurrentPrice_real_decimal(); - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const tokenSort = tokenX.id == token_x ? true : false; - const reduce_p = 1 - p / 100; - const add_p = 1 + p / 100; - if (tokenSort) { - // -10% example - const l_price = new BigNumber(c_price).multipliedBy(reduce_p).toFixed(); - const point_l = getPointByPrice( - point_delta, - l_price.toString(), - decimalRate - ); - // +10% example - const r_price = new BigNumber(c_price).multipliedBy(add_p).toFixed(); - const point_r = getPointByPrice( - point_delta, - r_price.toString(), - decimalRate - ); - return { left_p: point_l, right_p: point_r }; - } else { - const c_price2 = new BigNumber(1).dividedBy(c_price).toFixed(); - // +10% example - const priceAdd = new BigNumber(c_price2).multipliedBy(add_p); - const l_price_2 = new BigNumber(1).dividedBy(priceAdd).toFixed(); - const point_l_2 = getPointByPrice( - point_delta, - l_price_2.toString(), - decimalRate - ); - // -10% example - const priceDivide = new BigNumber(c_price2).multipliedBy(reduce_p); - const r_price_2 = new BigNumber(1).dividedBy(priceDivide).toFixed(); - const point_r_2 = getPointByPrice( - point_delta, - r_price_2.toString(), - decimalRate - ); - return { left_p: point_l_2, right_p: point_r_2 }; - } - } - function getCurrentPrice() { - let price = getCurrentPrice_real_decimal(); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } - if (price) { - return toPrecision(price.toString(), 8); - } else { - return '-'; - } - } - function getCurrentPriceValue() { - if (tokenX) { - const price = tokenPriceList[tokenX.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; - } - } - function getCurrentPrice_real_decimal() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { current_point, token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(current_point, decimalRate); - return price; - } - return 0; - } - function addOneSlot(direction: string) { - const { point_delta } = currentSelectedPool; - const l_p = leftPoint + point_delta; - const r_p = rightPoint + point_delta; - if (direction == 'l') { - setLeftPoint(Math.max(Math.min(POINTRIGHTRANGE, l_p), POINTLEFTRANGE)); - } else if (direction == 'r') { - const target_slot_r = Math.max( - Math.min(POINTRIGHTRANGE, r_p), - POINTLEFTRANGE - ); - if (target_slot_r - leftPoint >= POINTRIGHTRANGE) return; - setRightPoint(target_slot_r); - } - } - function reduceOneSlot(direction: string) { - const { point_delta } = currentSelectedPool; - const l_p = leftPoint - point_delta; - const r_p = rightPoint - point_delta; - if (direction == 'l') { - const target_slot_l = Math.max( - Math.min(POINTRIGHTRANGE, l_p), - POINTLEFTRANGE - ); - if (rightPoint - target_slot_l >= POINTRIGHTRANGE) return; - setLeftPoint(target_slot_l); - } else if (direction == 'r') { - setRightPoint(Math.max(Math.min(POINTRIGHTRANGE, r_p), POINTLEFTRANGE)); - } - } - function handlePriceToAppropriatePoint() { - const { point_delta, token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - if (leftCustomPrice) { - if (!tokenSort) { - leftCustomPrice = new BigNumber(1).dividedBy(leftCustomPrice).toFixed(); - } - const c_point = getPointByPrice( - point_delta, - leftCustomPrice, - decimalRate - ); - setLeftCustomPrice(''); - setLeftPoint(c_point); - if (rightPoint - c_point >= POINTRIGHTRANGE) { - const appropriate_r_point = POINTRIGHTRANGE + c_point - point_delta; - setRightPoint(appropriate_r_point); - } - } - if (rightCustomPrice) { - if (!tokenSort) { - rightCustomPrice = new BigNumber(1) - .dividedBy(rightCustomPrice) - .toFixed(); - } - const c_point = getPointByPrice( - point_delta, - rightCustomPrice, - decimalRate - ); - setRightCustomPrice(''); - setRightPoint(c_point); - if (c_point - leftPoint >= POINTRIGHTRANGE) { - const appropriate_l_point = c_point - POINTRIGHTRANGE + point_delta; - setLeftPoint(appropriate_l_point); - } - } - } - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - if (tokenSort) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } else { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - } else if (onlyAddYToken) { - if (tokenSort) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } else { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - return !(condition1 && condition2); - } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - } - function getLeftPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(leftPoint, decimalRate); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getRightPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(rightPoint, decimalRate); - if (tokenX.id == token_y) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function addLiquidity() { - setAddLiquidityButtonLoading(true); - const { pool_id } = currentSelectedPool; - add_liquidity({ - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: tokenSort - ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - amount_y: tokenSort - ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - token_x: tokenSort ? tokenX : tokenY, - token_y: tokenSort ? tokenY : tokenX, - }); - } - function quickChangePoint(item: string | number) { - if (currentCheckedQuickOption == item) return; - const { point_delta, current_point } = currentSelectedPool; - const decimalRateTurn = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - if (item == 'full') { - const l_p_temp = _.max([current_point - 400000, -800000]); - const r_p_temp = _.min([current_point + 400000, 800000]); - let l_p = Math.floor(l_p_temp / point_delta) * point_delta; - let r_p = Math.floor(r_p_temp / point_delta) * point_delta; - if (r_p - l_p >= POINTRIGHTRANGE) { - l_p = l_p + point_delta; - } - setLeftPoint(l_p); - setRightPoint(r_p); - } else { - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price_c = getPriceByPoint(currentPoint, decimalRate); - if (tokenSort) { - const price_l_new = new BigNumber(1 - +item / 100) - .multipliedBy(price_c) - .toFixed(); - const price_r_new = new BigNumber(1 + +item / 100) - .multipliedBy(price_c) - .toFixed(); - const l_point = getPointByPrice( - point_delta, - price_l_new, - decimalRateTurn - ); - const r_point = getPointByPrice( - point_delta, - price_r_new, - decimalRateTurn - ); - setLeftPoint(l_point); - setRightPoint(r_point); - } else { - const price_c_2 = new BigNumber(1).dividedBy(price_c).toFixed(); - const price_l_new_2 = new BigNumber(1 - +item / 100) - .multipliedBy(price_c_2) - .toFixed(); - const price_r_new_2 = new BigNumber(1 + +item / 100) - .multipliedBy(price_c_2) - .toFixed(); - const price_l_new = new BigNumber(1).dividedBy(price_r_new_2).toFixed(); - const price_r_new = new BigNumber(1).dividedBy(price_l_new_2).toFixed(); - const l_point = getPointByPrice( - point_delta, - price_l_new, - decimalRateTurn - ); - const r_point = getPointByPrice( - point_delta, - price_r_new, - decimalRateTurn - ); - setLeftPoint(l_point); - setRightPoint(r_point); - } - } - } - function getButtonText() { - let txt: any = ( - - ); - if (invalidRange) { - txt = ( - - ); - } else if ( - (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || - (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || - (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - !onlyAddXToken && - !onlyAddYToken && - (+tokenXAmount == 0 || +tokenYAmount == 0) - ) { - txt = ( - - ); - } else if ( - +tokenXAmount > 0 && - new BigNumber(tokenXAmount).isGreaterThan( - getMax(tokenX, tokenXBalanceFromNear) - ) - ) { - txt = ( - - ); - } else if ( - +tokenYAmount > 0 && - new BigNumber(tokenYAmount).isGreaterThan( - getMax(tokenY, tokenYBalanceFromNear) - ) - ) { - txt = ( - - ); - } - return txt; - } - function getPriceTip() { - const tip = intl.formatMessage({ id: 'price_on_slot_tip' }); - let result: string = `
${tip}
`; - return result; - } - function get_related_seeds() { - const temp_seeds = seeds.map((seed: Seed) => { - const [contractId, temp_mft_id] = seed.seed_id.split('@'); - const [fixRange, pool_id, left_point, right_point] = - temp_mft_id.split('&'); - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price_left = getPriceByPoint(+left_point, decimalRate); - const price_right = getPriceByPoint(+right_point, decimalRate); - return { - seed, - price_left, - price_right, - }; - }); - const tokenSort = tokenX.id == token_x ? true : false; - const targetSeed = temp_seeds[displayedSeedIndex]; - const { price_left, price_right } = targetSeed; - let price_left_final = price_left; - let price_right_final = price_right; - - if (!tokenSort) { - price_left_final = new BigNumber(1).dividedBy(price_right).toFixed(); - price_right_final = new BigNumber(1).dividedBy(price_left).toFixed(); - } - const display_price_left = displayNumberToAppropriateDecimals( - price_left_final.toString() - ); - const display_price_right = displayNumberToAppropriateDecimals( - price_right_final.toString() - ); - return ( -
- - 1 {tokenX.symbol} ={' '} - -
- { - setDisplayedSeedIndex(displayedSeedIndex - 1); - }} - className={`transform rotate-90 mr-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex > 0 ? '' : 'hidden' - }`} - > - { - if (!tokenSort) { - leftCustomPrice = price_right_final; - rightCustomPrice = price_left_final; - } else { - leftCustomPrice = price_left_final; - rightCustomPrice = price_right_final; - } - setLeftCustomPrice(leftCustomPrice); - setRightCustomPrice(rightCustomPrice); - handlePriceToAppropriatePoint(); - }} - > - {display_price_left} ~ {display_price_right} - - {tokenY.symbol} - { - setDisplayedSeedIndex(displayedSeedIndex + 1); - }} - className={`transform -rotate-90 ml-1.5 text-primaryText cursor-pointer ${ - seeds.length > 1 && displayedSeedIndex < seeds.length - 1 - ? '' - : 'hidden' - }`} - > -
-
- ); - } - function rewardRangeTip() { - const tip = intl.formatMessage({ id: 'reward_range_tip' }); - let result: string = `
${tip}
`; - return result; - } - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const isAddLiquidityDisabled = getButtonStatus(); - const mobileDevice = isMobile(); - return ( -
-
- -
-
-
- - - -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue()}) - - - - {getCurrentPrice()} {toRealSymbol(tokenY?.symbol)} - -
-
- {/* range chart area */} -
- - - - - - - - {chartLoading ? ( - - ) : null} - {noDataForChart ? : null} -
- {/* input range area */} -
-
-
- - - - {tokenSort ? ( - { - reduceOneSlot('l'); - }} - addOneSlot={() => { - addOneSlot('l'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - > - ) : ( - { - addOneSlot('r'); - }} - addOneSlot={() => { - reduceOneSlot('r'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - )} -
-
- - - - {tokenSort ? ( - { - reduceOneSlot('r'); - }} - addOneSlot={() => { - addOneSlot('r'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - > - ) : ( - { - addOneSlot('l'); - }} - addOneSlot={() => { - reduceOneSlot('l'); - }} - handlePriceToAppropriatePoint={handlePriceToAppropriatePoint} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - > - )} -
-
-
-
- - -
- {quickOptions.map((item: number, index) => { - return ( -
{ - quickChangePoint(item); - }} - key={index} - className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ - currentCheckedQuickOption == item - ? 'bg-v3PurpleColor border-v3PurpleColor text-white' - : 'border-v3GreyColor text-v3LightGreyColor' - }`} - > - ± {item}% -
- ); - })} -
{ - quickChangePoint('full'); - }} - className={`flex items-center justify-center rounded-lg h-6 py-0.5 xs:px-1 md:px-1 lg:px-1.5 box-content cursor-pointer font-sans text-sm border whitespace-nowrap ${ - currentCheckedQuickOption == 'full' - ? 'bg-v3PurpleColor border-v3PurpleColor text-white' - : 'border-v3GreyColor text-v3LightGreyColor' - }`} - > - -
-
- {seeds.length ? ( -
-
- -
- - -
-
- {get_related_seeds()} -
- ) : null} -
-
-
- -
- -
-
-
- -
- -
-
- {isSignedIn ? ( - - <>{getButtonText()}} - /> - - ) : ( - - )} -
- ); -} - -function NoDataComponent(props: any) { - const { isNoPool } = props; - const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); - const mobileDevice = isMobile(); - return ( -
-
- -
- {isNoPool ? ( -
- -
- ) : null} -
- {/* range chart area */} -
- -
- {/* input range area */} -
-
-
- - - -
-
- -
- -
- -
-
-
-
- - - -
-
- -
- -
- -
-
-
-
-
- - {quickOptions.map((item: number, index) => { - return ( -
- ± {item}% -
- ); - })} -
- -
-
-
-
- - ( - <> - {isNoPool ? ( - - ) : ( - - )} - - )} - /> - -
- ); -} -function PointInputComponent({ - reduceOneSlot, - addOneSlot, - handlePriceToAppropriatePoint, - customPrice, - getPrice, - setCustomPrice, - inputStatus, - setInputStatus, -}: any) { - return ( -
-
{ - reduceOneSlot('r'); - }} - > - -
- { - handlePriceToAppropriatePoint(); - setInputStatus(false); - }} - value={inputStatus ? customPrice : getPrice()} - onChange={({ target }) => { - setInputStatus(true); - const inputPrice = target.value; - setCustomPrice(inputPrice); - }} - /> -
{ - addOneSlot('r'); - }} - > - -
-
- ); -} -function OneSide({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InvalidRange({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InputAmount({ - token, - balance, - tokenPriceList, - changeAmount, - amount, - currentSelectedPool, - hidden, -}: { - token: TokenMetadata; - balance: string; - tokenPriceList: Record; - changeAmount: any; - amount: string; - currentSelectedPool: PoolInfo; - hidden: Boolean; -}) { - const [inputPrice, setInputPrice] = useState(''); - const [showNearTip, setShowNearTip] = useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - useEffect(() => { - const price = token ? tokenPriceList[token.id]?.price : ''; - if (price && amount) { - setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); - } else { - setInputPrice(''); - } - if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { - const difference = new BigNumber(maxBalance).minus(amount); - const b = difference.toFixed(); - const r = difference.isLessThan(0); - if (r) { - setShowNearTip(true); - } else { - setShowNearTip(false); - } - } else { - setShowNearTip(false); - } - }, [amount, token, tokenPriceList.length]); - function getBalance() { - let r = '0'; - if (token && balance) { - r = formatWithCommas(toPrecision(balance.toString(), 3)); - } - return isSignedIn ? r : '-'; - } - function showCurrentPrice() { - if (isNoPool) { - return '$-'; - } else if (inputPrice) { - return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); - } - return '$-'; - } - const maxBalance = - token?.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - const isNoPool = !currentSelectedPool?.pool_id; - return ( -
- - {showNearTip && !isNoPool ? ( -
- - -
- ) : null} -
- ); -} -function getAllTokens(refTokens: TokenMetadata[], triTokens: TokenMetadata[]) { - triTokens.forEach((tk) => { - const tokenInRef = refTokens.find((token) => token.id === tk.id); - if (tokenInRef) { - tokenInRef.onTri = true; - } else { - refTokens.push(tk); - } - }); - - return refTokens; -} From d3e8556b90824e801fedd30dd0cc6bd41c7bb2b4 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 4 Jul 2023 00:13:42 +0800 Subject: [PATCH 059/204] update --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 268 ++++--------------- 1 file changed, 50 insertions(+), 218 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 9c63fd5c3..5075a7808 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -779,6 +779,16 @@ export default function AddYourLiquidityPageV3() { // token_y: tokenSort ? tokenY : tokenX, }; } + function formatWithCommas_for_tip(v: string) { + const v_big = Big(v || 0); + let v_temp; + if (v_big.lt(0.001)) { + v_temp = v; + } else { + v_temp = toPrecision(v, 3, true, false); + } + return v_temp; + } function getLiquidityForCurveAndBidAskMode() { /** * 已知条件: @@ -854,8 +864,12 @@ export default function AddYourLiquidityPageV3() { min_token_x_amount_needed ); if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + // 给出提示 token x 数量太少不能添加 1 + set_token_amount_tip( + `You need at least ${formatWithCommas_for_tip( + min_token_x_amount_needed + )}${' '}${tokenX.symbol}` + ); return; } else { nftList_x = get_decline_pattern_nfts({ @@ -883,8 +897,12 @@ export default function AddYourLiquidityPageV3() { min_token_y_amount_needed ); if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + // 给出提示 token y 数量太少不能添加 2 + set_token_amount_tip( + `You need at least ${formatWithCommas_for_tip( + min_token_y_amount_needed + )}${' '}${tokenY.symbol}` + ); return; } else { nftList_y = get_rise_pattern_nfts({ @@ -913,8 +931,12 @@ export default function AddYourLiquidityPageV3() { min_token_x_amount_needed ); if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); + // 给出提示 token x 数量太少不能添加 3 + set_token_amount_tip( + `You need at least ${formatWithCommas_for_tip( + min_token_x_amount_needed + )}${' '}${tokenX.symbol}` + ); return; } else { nftList_x = get_rise_pattern_nfts({ @@ -942,8 +964,13 @@ export default function AddYourLiquidityPageV3() { min_token_y_amount_needed ); if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); + // 给出提示 token y 数量太少不能添加 4 + + set_token_amount_tip( + `You need at least ${formatWithCommas_for_tip( + min_token_y_amount_needed + )}${' '}${tokenY.symbol}` + ); return; } else { nftList_y = get_decline_pattern_nfts({ @@ -2260,16 +2287,6 @@ function AddLiquidityButton() { onlyAddXToken, onlyAddYToken, invalidRange, - set_token_amount_tip, - get_y_nfts_contain_current_curve, - get_x_nfts_contain_current_curve, - get_x_nfts_contain_current_bid_ask, - get_y_nfts_contain_current_bid_ask, - get_rise_pattern_nfts, - get_decline_pattern_nfts, - formula_of_token_x, - formula_of_token_y, - getLiquiditySpot, getLiquidityForCurveAndBidAskMode, } = useContext(LiquidityProviderData); @@ -2312,213 +2329,28 @@ function AddLiquidityButton() { setAddLiquidityButtonLoading(false); return; } - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: tokenXAmount_nonDivisible, - amount_y: tokenYAmount_nonDivisible, - }); - } - function addLiquidityForCurveAndBidAskModeCopy() { /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_x_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 - * step2 分配好后,获得对右侧的最小token数量要求 - * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 - */ - const { point_delta, current_point } = currentSelectedPool; - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'floor' + * 计算出 nftList token x tokeny 的数量,这是需要的总数量 + * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 + * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 + */ + let last_total_needed_token_x_amount = Big(0); + let last_total_needed_token_y_amount = Big(0); + nftList.forEach((nft: IAddLiquidityInfo) => { + const { amount_x, amount_y } = nft; + last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( + amount_x || 0 ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'ceil' + last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( + amount_y || 0 ); - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(''); - if (liquidityShape == 'Curve') { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_curve({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_x = get_decline_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_curve({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_y = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } else { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_bid_ask({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 - set_token_amount_tip(`${tokenX.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_bid_ask({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 - set_token_amount_tip(`${tokenY.symbol} Token amount is too little`); - setAddLiquidityButtonLoading(false); - return; - } else { - nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } - } + }); batch_add_liquidity({ liquidityInfos: nftList, token_x: tokenX, token_y: tokenY, - amount_x: tokenXAmount_nonDivisible, - amount_y: tokenYAmount_nonDivisible, + amount_x: last_total_needed_token_x_amount.toFixed(), + amount_y: last_total_needed_token_y_amount.toFixed(), }); } function getMax(token: TokenMetadata, balance: string) { From 601b8ccfa6e43a32127859138bdeb1bc39e8d0e8 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 4 Jul 2023 08:42:25 +0800 Subject: [PATCH 060/204] update --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 5075a7808..1c46f9c0c 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -783,9 +783,9 @@ export default function AddYourLiquidityPageV3() { const v_big = Big(v || 0); let v_temp; if (v_big.lt(0.001)) { - v_temp = v; + v_temp = v_big.toFixed(6, 3); } else { - v_temp = toPrecision(v, 3, true, false); + v_temp = formatWithCommas(v_big.toFixed(3, 3)); } return v_temp; } @@ -2266,7 +2266,7 @@ export default function AddYourLiquidityPageV3() { } /** * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 提示 todo + * 双边 一侧token 数量太多 传递的时候只传实际使用值 * @returns */ function AddLiquidityButton() { From 4bf986be1fad27c177cc7457417ac8e092f6aafa Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 4 Jul 2023 09:43:43 +0800 Subject: [PATCH 061/204] update --- src/components/d3Chart/DclChart.tsx | 24 +++++++++----------- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 1 + 2 files changed, 12 insertions(+), 13 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index c056a8324..76f118f47 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -24,8 +24,6 @@ import { IPoolChartConfig, IUserLiquiditiesDetail, IDCLAccountFee, - IDclLogData, - IProcessedLogData, } from './interfaces'; import { formatPrice, @@ -123,8 +121,6 @@ export default function DclChart({ if (pool_id) { timerObj.timer = setTimeout(() => { get_pool_detail(pool_id); - leftPoint && setDragLeftPoint(leftPoint); - rightPoint && setDragRightPoint(rightPoint); }, 500); } }, [pool_id]); @@ -187,11 +183,10 @@ export default function DclChart({ d3.select(`${randomId} .overlap rect`) .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) .attr('width', W); - setLeftPoint && setLeftPoint(newPoint); } }, [dragLeftPoint, price_range, drawChartDone]); useEffect(() => { - if (isValid(leftPoint) && isValid(dragLeftPoint)) { + if (isValid(leftPoint)) { setDragLeftPoint(leftPoint); } }, [leftPoint]); @@ -228,11 +223,10 @@ export default function DclChart({ ); const W = x - leftX - dragBarWidth / 2; d3.select(`${randomId} .overlap rect`).attr('width', W); - setRightPoint && setRightPoint(newPoint); } }, [dragRightPoint, price_range, drawChartDone]); useEffect(() => { - if (isValid(rightPoint) && isValid(dragRightPoint)) { + if (isValid(rightPoint)) { setDragRightPoint(rightPoint); } }, [rightPoint]); @@ -502,7 +496,7 @@ export default function DclChart({ } if (appearanceConfig.controlHidden) { remove_control(); - } else { + } else { // init // drag left draw_drag_left({ scale }); // drag right @@ -901,7 +895,7 @@ export default function DclChart({ const p = scale.invert(e.x); const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); setDragLeftPoint(newLeftPoint); - setLeftPoint(newLeftPoint); + setLeftPoint && setLeftPoint(newLeftPoint); }); d3.select(`${randomId} .drag-left`).call(dragLeft); } @@ -944,7 +938,7 @@ export default function DclChart({ const p = scale.invert(e.x); const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); setDragRightPoint(newRightPoint); - setRightPoint(newRightPoint); + setRightPoint && setRightPoint(newRightPoint); }); d3.select(`${randomId} .drag-right`).call(dragRight); } @@ -1000,12 +994,16 @@ export default function DclChart({ function clickToLeft() { const { bin } = getConfig(); const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - setDragLeftPoint(get_nearby_bin_left_point(newPoint)); + const newPoint_nearby_bin = get_nearby_bin_left_point(newPoint); + setDragLeftPoint(newPoint_nearby_bin); + setLeftPoint && setLeftPoint(newPoint_nearby_bin); } function clickToRight() { const { bin } = getConfig(); const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - setDragRightPoint(get_nearby_bin_left_point(newPoint)); + const newPoint_nearby_bin = get_nearby_bin_left_point(newPoint); + setDragRightPoint(newPoint_nearby_bin); + setRightPoint && setRightPoint(newPoint_nearby_bin); } function scaleAxis() { if (chartType == 'USER') { diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 1c46f9c0c..4577962f0 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2577,6 +2577,7 @@ function SetPointsComponent() { // 中文 左侧改变===》点位 useEffect(() => { + debugger; if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin const diff = rightPoint - leftPoint; From 3c5ec9e02a500bfa12af511ee5b26706acbf289a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 4 Jul 2023 21:37:58 +0800 Subject: [PATCH 062/204] optimize --- src/components/d3Chart/DclChart.tsx | 90 +++++++++++--------- src/components/d3Chart/interfaces.tsx | 1 + src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 1 - 3 files changed, 52 insertions(+), 40 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 76f118f47..4994a52b9 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -24,6 +24,7 @@ import { IPoolChartConfig, IUserLiquiditiesDetail, IDCLAccountFee, + IRMTYPE, } from './interfaces'; import { formatPrice, @@ -105,6 +106,7 @@ export default function DclChart({ ); const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 const whole_bars_background_padding = +( appearanceConfig.whole_bars_background_padding || 20 ); @@ -116,6 +118,7 @@ export default function DclChart({ setTokenPriceList(result); }); }, []); + // init useEffect(() => { clearTimeout(timerObj.timer); if (pool_id) { @@ -124,26 +127,27 @@ export default function DclChart({ }, 500); } }, [pool_id]); + // init 从后端获取数据 useEffect(() => { if (pool) { get_chart_data(); } }, [pool, accountId]); + // 更新用户数据 useEffect(() => { if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { const new_list = get_latest_user_chart_data(); setChartDataList(new_list); } }, [newlyAddedLiquidities, user_liquidities]); + // 绘制图表 useEffect(() => { - if (price_range && chartDataList) { - if ( - chartType !== 'USER' || - (chartType == 'USER' && chartDataList.length) - ) { - drawChart(); - setDrawChartDone(true); - } + if ( + (chartType !== 'USER' && price_range && chartDataList) || + (chartType == 'USER' && chartDataList?.length) + ) { + drawChart(); + setDrawChartDone(true); } }, [price_range, chartDataList]); useEffect(() => { @@ -371,10 +375,12 @@ export default function DclChart({ async function get_chart_data() { const { range } = getConfig(); const list = await get_data_from_back_end(); - const [price_l_default, price_r_default] = - get_price_range_by_percent(range); - set_price_range([+price_l_default, +price_r_default]); - setZoom(range); + if (chartType !== 'USER') { + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + } setChartDataList(list); } async function get_data_from_back_end() { @@ -457,7 +463,7 @@ export default function DclChart({ return [price_l, price_r]; } function drawChart() { - const data: IChartData[] = getDataDisplayInChart(); + const data: IChartData[] = process_chart_data_for_display(); const scale = scaleAxis(); const scaleBar = scaleAxisY(); // down bars @@ -496,14 +502,15 @@ export default function DclChart({ } if (appearanceConfig.controlHidden) { remove_control(); - } else { // init + } else { + // init // drag left draw_drag_left({ scale }); // drag right draw_drag_right({ scale }); } } - function getDataDisplayInChart() { + function process_chart_data_for_display() { const { bin: bin_final } = getConfig(); const { token_x_metadata, token_y_metadata, point_delta } = pool; const decimalRate_price = @@ -765,7 +772,7 @@ export default function DclChart({ scale: Function; scaleBar: Function; }) { - const { sortP, sortY } = getChartDataListRange_x_y(); + const { sortP, sortY } = get_price_and_liquidity_range(); d3.select(`${randomId} .whole_bars_background`) .on('mousemove', function (e) { d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); @@ -808,7 +815,7 @@ export default function DclChart({ scale: Function; scaleBar: Function; }) { - const { sortP, sortY } = getChartDataListRange_x_y(); + const { sortP, sortY } = get_price_and_liquidity_range(); const min_bin_price = sortP[0]; const max_bin_price = sortP[sortP.length - 1]; const { fromLeft, fromRight, all, point } = removeParams; @@ -894,6 +901,7 @@ export default function DclChart({ if (rightX < e.x || e.x < dragBarWidth / 2) return; const p = scale.invert(e.x); const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); + setDragLeftPoint(newLeftPoint); setLeftPoint && setLeftPoint(newLeftPoint); }); @@ -994,17 +1002,22 @@ export default function DclChart({ function clickToLeft() { const { bin } = getConfig(); const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_nearby_bin_left_point(newPoint); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); setDragLeftPoint(newPoint_nearby_bin); setLeftPoint && setLeftPoint(newPoint_nearby_bin); } function clickToRight() { const { bin } = getConfig(); const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_nearby_bin_left_point(newPoint); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); setDragRightPoint(newPoint_nearby_bin); setRightPoint && setRightPoint(newPoint_nearby_bin); } + function get_bin_point_by_point(point: number, type?: IRMTYPE) { + const { point_delta } = pool; + const slot_num = getConfig().bin; + return getBinPointByPoint(point_delta, slot_num, point, type); + } function scaleAxis() { if (chartType == 'USER') { return scaleAxis_User(); @@ -1019,7 +1032,7 @@ export default function DclChart({ .range([0, svgWidth - svgPaddingX * 2]); } function scaleAxis_User() { - const binWidth = getConfig().bin * pool.point_delta; + const binWidth = get_bin_width(); const min_point = Math.max( chartDataList[0].point - binWidth * 2, POINTLEFTRANGE @@ -1054,26 +1067,25 @@ export default function DclChart({ ); }); const sortL = sortBy(L); - return ( - d3 - .scaleLinear() - // .domain([+sortL[0], +sortL[sortL.length - 1]]) - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]) - ); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); } function scaleAxisY_User() { - const { sortY: sortL } = getChartDataListRange_x_y(); - return ( - d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - // .domain([+sortL[0], +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight - whole_bars_background_padding]) - .clamp(true) - ); + const { sortY: sortL } = get_price_and_liquidity_range(); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true); + } + function get_bin_width() { + const slot_num_in_a_bin = getConfig().bin; + const { point_delta } = pool; + return point_delta * slot_num_in_a_bin; } - function getChartDataListRange_x_y() { + function get_price_and_liquidity_range() { const Y: number[] = []; const X: number[] = []; const chartDataListInrange = @@ -1089,10 +1101,10 @@ export default function DclChart({ }); const sortY = sortBy(Y); const sortX = sortBy(X); - const sortX_map_P = sortX.map((x) => { + const sortX_Price = sortX.map((x) => { return get_price_by_point(x); }); - return { sortP: sortX_map_P, sortY }; + return { sortP: sortX_Price, sortY }; } function diffPrices(newPrice: string, peferencePrice?: string) { let movePercent; diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index c7c894314..063ba7d15 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -94,3 +94,4 @@ export interface IProcessedLogData { token_x: string; token_y: string; } +export type IRMTYPE = 'round' | 'floor' | 'ceil'; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 4577962f0..1c46f9c0c 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2577,7 +2577,6 @@ function SetPointsComponent() { // 中文 左侧改变===》点位 useEffect(() => { - debugger; if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin const diff = rightPoint - leftPoint; From 1cd775147baddf771f0b19fa19304e41728c43a3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 4 Jul 2023 22:54:10 +0800 Subject: [PATCH 063/204] update --- src/components/d3Chart/DclChart.tsx | 10 ++++++++++ src/components/d3Chart/interfaces.tsx | 1 + 2 files changed, 11 insertions(+) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 4994a52b9..d4ecffff7 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -617,6 +617,16 @@ export default function DclChart({ } function draw_axis({ scale }: { scale: any }) { const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); + if (appearanceConfig.ticks || chartType == 'USER') { + axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { + const dBig = new Big(d); + if (dBig.gte(10000)) { + return dBig.toFixed(0); + } else { + return d; + } + }); + } d3.select(`${randomId} svg .axis`) .call(axis) .selectAll('text') diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 063ba7d15..b784da645 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -52,6 +52,7 @@ export interface IPoolChartConfig { hoverBoxHidden?: boolean; radiusMode?: boolean; targetPoint?: number; + ticks?:number; } export interface IUserLiquiditiesDetail { From ed311a275af25d5606a9745e7f28a04efb8ab224 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 10:31:31 +0800 Subject: [PATCH 064/204] fix bug --- src/components/d3Chart/interfaces.tsx | 2 +- src/components/pool/RemovePoolV3.tsx | 31 ++++++++++++++++++++++++++- src/services/swapV3.ts | 30 +++++++++++++++++++++++++- 3 files changed, 60 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index b784da645..009ec89fc 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -52,7 +52,7 @@ export interface IPoolChartConfig { hoverBoxHidden?: boolean; radiusMode?: boolean; targetPoint?: number; - ticks?:number; + ticks?: number; } export interface IUserLiquiditiesDetail { diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 2aec9cf50..777a4391c 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -383,12 +383,16 @@ export const RemovePoolV3 = (props: any) => { * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 * step3 未截断的区间 和 token 数量作为 添加nft的参数 */ + let min_withdraw_token_x_amount = Big(0); + let min_withdraw_token_y_amount = Big(0); if (broken_deleted_nfts.length) { const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; const addLiquidityInfoList: IAddLiquidityInfo[] = []; broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { const { amount, lpt_id, left_point, right_point, mft_id } = l; const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [whole_token_x_amount, whole_token_y_amount] = + get_x_y_amount_of_liquidity({ left_point, right_point, amount }); const [new_token_x_amount, new_token_y_amount] = get_x_y_amount_of_liquidity({ left_point: new_left_point, @@ -401,6 +405,21 @@ export const RemovePoolV3 = (props: any) => { right_point: right_point, amount, }); + + const remove_part_token_x_amount = new Big( + whole_token_x_amount || 0 + ).minus(new_token_x_amount || 0); + const remove_part_token_y_amount = new Big( + whole_token_y_amount || 0 + ).minus(whole_token_y_amount || 0); + const rate = (100 - slippageTolerance) / 100; + min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + remove_part_token_x_amount.mul(rate) + ); + min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + remove_part_token_y_amount.mul(rate) + ); + addLiquidityInfoList.push({ pool_id, left_point: new_left_point, @@ -460,13 +479,23 @@ export const RemovePoolV3 = (props: any) => { }); batch_remove_liquidity = batchRemoveLiquidity; } - + const widthdraw_infos = { + min_withdraw_token_x_amount: toNonDivisibleNumber( + tokenX.decimals, + min_withdraw_token_x_amount.toFixed() + ), + min_withdraw_token_y_amount: toNonDivisibleNumber( + tokenY.decimals, + min_withdraw_token_y_amount.toFixed() + ), + }; batch_remove_liquidity_contract({ token_x: tokenX, token_y: tokenY, batch_remove_liquidity, batch_update_liquidity, mint_liquidities, + widthdraw_infos, }); } function get_minimum_received_data() { diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index ed2785eb4..685bd93b8 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1261,12 +1261,17 @@ export const batch_remove_liquidity_contract = async ({ batch_remove_liquidity, batch_update_liquidity, mint_liquidities, + widthdraw_infos, }: { token_x: TokenMetadata; token_y: TokenMetadata; batch_remove_liquidity: IRemoveLiquidityInfo[]; batch_update_liquidity: IBatchUpdateiquidityInfo; mint_liquidities: UserLiquidityInfo[]; + widthdraw_infos: { + min_withdraw_token_x_amount: string; + min_withdraw_token_y_amount: string; + }; }) => { const max_number = 20; const transactions: Transaction[] = []; @@ -1338,8 +1343,28 @@ export const batch_remove_liquidity_contract = async ({ ], }); } + const widthdrawActions: any[] = []; + const { min_withdraw_token_x_amount, min_withdraw_token_y_amount } = + widthdraw_infos; + if (Big(min_withdraw_token_x_amount).gt(0)) { + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_x.id, amount: min_withdraw_token_x_amount }, + gas: '55000000000000', + }); + } + if (Big(min_withdraw_token_y_amount).lt(0)) { + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_y.id, amount: min_withdraw_token_y_amount }, + gas: '55000000000000', + }); + } + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: widthdrawActions, + }); } - const ftBalance_x = await ftGetStorageBalance(token_x.id); if (!ftBalance_x) { transactions.unshift({ @@ -1376,6 +1401,9 @@ export const batch_remove_liquidity_contract = async ({ ], }); } + + // width draw + return executeMultipleTransactions(transactions); }; From 4eb259287a90fd43800d2f27964f8d57dbaa555a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 12:24:22 +0800 Subject: [PATCH 065/204] fix bug --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6 ++-- src/pages/poolsV3/PoolDetailV3.tsx | 33 ++++++++++++++++---- 2 files changed, 30 insertions(+), 9 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 1c46f9c0c..7910905ff 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2736,7 +2736,7 @@ function SetPointsComponent() { } return (
{/* chart area */}
@@ -2848,7 +2848,7 @@ function SetPointsComponent() {
{/* content */} -
-
+
*/}
{/* target price input box */}
([]); const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); const [accountAPR, setAccountAPR] = useState(''); + const [earned_fee, set_earned_fee] = useState(''); const [operationType, setOperationType] = useState('add'); const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; const { accountId } = useWalletSelector(); @@ -685,19 +686,40 @@ function YourLiquidityBox(props: { }, [liquidities, Object.keys(tokenPriceList).length]); useEffect(() => { if (poolDetail && tokenPriceList) { - get_24_apr(); + get_24_apr_and_fee(); } }, [poolDetail, tokenPriceList]); - async function get_24_apr() { + async function get_24_apr_and_fee() { let apr_24 = ''; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + let total_fee_earned = ''; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ pool_id, account_id: accountId, }); if (dcl_fee_result) { // 24h 利润 apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + // total earned fee + const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; + // 总共赚到的fee + const total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + const total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value.plus( + total_earned_fee_y_value + ).toFixed(); } + + set_earned_fee(formatWithCommas_usd(total_fee_earned)); setAccountAPR(formatPercentage(apr_24)); } function get_amount_x_y(liquidity: UserLiquidityInfo) { @@ -1112,8 +1134,7 @@ function YourLiquidityBox(props: {
)}
- - {getTotalFee().total_fee_price} + {earned_fee || '-'}
From b99e813d0146e6a2a7faf3f36cc6f7052725d52b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 15:48:57 +0800 Subject: [PATCH 066/204] update --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 57 +++++++++++--------- 1 file changed, 32 insertions(+), 25 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 7910905ff..cf1c46fce 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -165,7 +165,7 @@ export default function AddYourLiquidityPageV3() { const [topPairs, setTopPairs] = useState([]); const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); - const [token_amount_tip, set_token_amount_tip] = useState(); + const [token_amount_tip, set_token_amount_tip] = useState(); const [only_suppport_spot_shape, set_only_suppport_spot_shape] = useState(false); const [switch_pool_loading, set_switch_pool_loading] = @@ -779,13 +779,17 @@ export default function AddYourLiquidityPageV3() { // token_y: tokenSort ? tokenY : tokenX, }; } - function formatWithCommas_for_tip(v: string) { + function formatWithCommas_for_tip(v: string, commas?:boolean) { const v_big = Big(v || 0); let v_temp; if (v_big.lt(0.001)) { v_temp = v_big.toFixed(6, 3); } else { - v_temp = formatWithCommas(v_big.toFixed(3, 3)); + if (commas) { + v_temp = formatWithCommas(v_big.toFixed(3, 3)); + } else { + v_temp = v_big.toFixed(3, 3); + } } return v_temp; } @@ -848,7 +852,7 @@ export default function AddYourLiquidityPageV3() { const slot_number_in_a_bin = SLOT_NUMBER; const binWidth = slot_number_in_a_bin * point_delta; const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(''); + set_token_amount_tip(<>); if (liquidityShape == 'Curve') { if (bin_number_left > 1) { // 左侧做等差 @@ -865,11 +869,12 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_x_amount.lt(0)) { // 给出提示 token x 数量太少不能添加 1 - set_token_amount_tip( - `You need at least ${formatWithCommas_for_tip( - min_token_x_amount_needed - )}${' '}${tokenX.symbol}` - ); + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip(min_token_x_amount_needed, true); + const tip = You need at least { + setTokenXAmount(a); + }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenX.symbol} + set_token_amount_tip(tip) return; } else { nftList_x = get_decline_pattern_nfts({ @@ -898,11 +903,12 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_y_amount.lt(0)) { // 给出提示 token y 数量太少不能添加 2 - set_token_amount_tip( - `You need at least ${formatWithCommas_for_tip( - min_token_y_amount_needed - )}${' '}${tokenY.symbol}` - ); + const a = formatWithCommas_for_tip(min_token_y_amount_needed) + const a_display = formatWithCommas_for_tip(min_token_y_amount_needed, true) + const tip = You need at least { + setTokenYAmount(a); + }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenY.symbol} + set_token_amount_tip(tip) return; } else { nftList_y = get_rise_pattern_nfts({ @@ -932,11 +938,12 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_x_amount.lt(0)) { // 给出提示 token x 数量太少不能添加 3 - set_token_amount_tip( - `You need at least ${formatWithCommas_for_tip( - min_token_x_amount_needed - )}${' '}${tokenX.symbol}` - ); + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip(min_token_x_amount_needed, true); + const tip = You need at least { + setTokenXAmount(a); + }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenX.symbol} + set_token_amount_tip(tip) return; } else { nftList_x = get_rise_pattern_nfts({ @@ -965,12 +972,12 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_y_amount.lt(0)) { // 给出提示 token y 数量太少不能添加 4 - - set_token_amount_tip( - `You need at least ${formatWithCommas_for_tip( - min_token_y_amount_needed - )}${' '}${tokenY.symbol}` - ); + const a = formatWithCommas_for_tip(min_token_y_amount_needed) + const a_display = formatWithCommas_for_tip(min_token_y_amount_needed, true) + const tip = You need at least { + setTokenYAmount(a); + }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenY.symbol} + set_token_amount_tip(tip) return; } else { nftList_y = get_decline_pattern_nfts({ From 9aa2f1f52e15264ba266ce5d90619deca26bfbd0 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 17:03:02 +0800 Subject: [PATCH 067/204] update1 --- src/components/pool/RemovePoolV3.tsx | 40 ++++--- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 105 ++++++++++++++----- src/pages/poolsV3/PoolDetailV3.tsx | 13 ++- 3 files changed, 114 insertions(+), 44 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 777a4391c..a231af936 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -391,8 +391,8 @@ export const RemovePoolV3 = (props: any) => { broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { const { amount, lpt_id, left_point, right_point, mft_id } = l; const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [whole_token_x_amount, whole_token_y_amount] = - get_x_y_amount_of_liquidity({ left_point, right_point, amount }); + // const [whole_token_x_amount, whole_token_y_amount] = + // get_x_y_amount_of_liquidity({ left_point, right_point, amount }); const [new_token_x_amount, new_token_y_amount] = get_x_y_amount_of_liquidity({ left_point: new_left_point, @@ -405,20 +405,30 @@ export const RemovePoolV3 = (props: any) => { right_point: right_point, amount, }); + if (Big(min_token_x_amount).gt(new_token_x_amount)) { + min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + Big(min_token_x_amount).minus(new_token_x_amount) + ); + } + if (Big(min_token_y_amount).gt(new_token_y_amount)) { + min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + Big(min_token_y_amount).minus(new_token_y_amount) + ); + } - const remove_part_token_x_amount = new Big( - whole_token_x_amount || 0 - ).minus(new_token_x_amount || 0); - const remove_part_token_y_amount = new Big( - whole_token_y_amount || 0 - ).minus(whole_token_y_amount || 0); - const rate = (100 - slippageTolerance) / 100; - min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - remove_part_token_x_amount.mul(rate) - ); - min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - remove_part_token_y_amount.mul(rate) - ); + // const remove_part_token_x_amount = new Big( + // whole_token_x_amount || 0 + // ).minus(new_token_x_amount || 0); + // const remove_part_token_y_amount = new Big( + // whole_token_y_amount || 0 + // ).minus(whole_token_y_amount || 0); + // const rate = (100 - slippageTolerance) / 100; + // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + // remove_part_token_x_amount.mul(rate) + // ); + // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + // remove_part_token_y_amount.mul(rate) + // ); addLiquidityInfoList.push({ pool_id, diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index cf1c46fce..a04a08aa0 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -165,7 +165,8 @@ export default function AddYourLiquidityPageV3() { const [topPairs, setTopPairs] = useState([]); const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); - const [token_amount_tip, set_token_amount_tip] = useState(); + const [token_amount_tip, set_token_amount_tip] = + useState(); const [only_suppport_spot_shape, set_only_suppport_spot_shape] = useState(false); const [switch_pool_loading, set_switch_pool_loading] = @@ -779,7 +780,7 @@ export default function AddYourLiquidityPageV3() { // token_y: tokenSort ? tokenY : tokenX, }; } - function formatWithCommas_for_tip(v: string, commas?:boolean) { + function formatWithCommas_for_tip(v: string, commas?: boolean) { const v_big = Big(v || 0); let v_temp; if (v_big.lt(0.001)) { @@ -870,11 +871,25 @@ export default function AddYourLiquidityPageV3() { if (remain_token_x_amount.lt(0)) { // 给出提示 token x 数量太少不能添加 1 const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip(min_token_x_amount_needed, true); - const tip = You need at least { - setTokenXAmount(a); - }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenX.symbol} - set_token_amount_tip(tip) + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); return; } else { nftList_x = get_decline_pattern_nfts({ @@ -903,12 +918,26 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_y_amount.lt(0)) { // 给出提示 token y 数量太少不能添加 2 - const a = formatWithCommas_for_tip(min_token_y_amount_needed) - const a_display = formatWithCommas_for_tip(min_token_y_amount_needed, true) - const tip = You need at least { - setTokenYAmount(a); - }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenY.symbol} - set_token_amount_tip(tip) + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); return; } else { nftList_y = get_rise_pattern_nfts({ @@ -939,11 +968,25 @@ export default function AddYourLiquidityPageV3() { if (remain_token_x_amount.lt(0)) { // 给出提示 token x 数量太少不能添加 3 const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip(min_token_x_amount_needed, true); - const tip = You need at least { - setTokenXAmount(a); - }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenX.symbol} - set_token_amount_tip(tip) + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); return; } else { nftList_x = get_rise_pattern_nfts({ @@ -972,12 +1015,26 @@ export default function AddYourLiquidityPageV3() { ); if (remain_token_y_amount.lt(0)) { // 给出提示 token y 数量太少不能添加 4 - const a = formatWithCommas_for_tip(min_token_y_amount_needed) - const a_display = formatWithCommas_for_tip(min_token_y_amount_needed, true) - const tip = You need at least { - setTokenYAmount(a); - }} className='mx-0.5 cursor-pointer underline'>{a_display}{tokenY.symbol} - set_token_amount_tip(tip) + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); return; } else { nftList_y = get_decline_pattern_nfts({ diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 72869af25..74ca81fb2 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -128,7 +128,10 @@ import Big from 'big.js'; import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; import DclChart from '../../components/d3Chart/DclChart'; import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; -import { formatPercentage, formatWithCommas_usd } from '../../components/d3Chart/utils'; +import { + formatPercentage, + formatWithCommas_usd, +} from '../../components/d3Chart/utils'; import { getDCLAccountFee } from '../../services/indexer'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); @@ -692,7 +695,7 @@ function YourLiquidityBox(props: { async function get_24_apr_and_fee() { let apr_24 = ''; let total_fee_earned = ''; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ pool_id, account_id: accountId, }); @@ -714,9 +717,9 @@ function YourLiquidityBox(props: { const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value.plus( - total_earned_fee_y_value - ).toFixed(); + total_fee_earned = total_earned_fee_x_value + .plus(total_earned_fee_y_value) + .toFixed(); } set_earned_fee(formatWithCommas_usd(total_fee_earned)); From 341fbe57e376397fe05be1cdf153c6296921bfe7 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 18:26:48 +0800 Subject: [PATCH 068/204] fix bugs --- src/components/pool/RemovePoolV3.tsx | 42 ++++++++-------- src/pages/pools/DetailsPage.tsx | 15 ++++-- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 17 ++++--- src/pages/poolsV3/PoolDetailV3.tsx | 52 ++++++++++++++------ src/services/swapV3.ts | 36 ++++++-------- 5 files changed, 93 insertions(+), 69 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index a231af936..2e0f07eca 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -405,16 +405,16 @@ export const RemovePoolV3 = (props: any) => { right_point: right_point, amount, }); - if (Big(min_token_x_amount).gt(new_token_x_amount)) { - min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - Big(min_token_x_amount).minus(new_token_x_amount) - ); - } - if (Big(min_token_y_amount).gt(new_token_y_amount)) { - min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - Big(min_token_y_amount).minus(new_token_y_amount) - ); - } + // if (Big(min_token_x_amount).gt(new_token_x_amount)) { + // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + // Big(min_token_x_amount).minus(new_token_x_amount) + // ); + // } + // if (Big(min_token_y_amount).gt(new_token_y_amount)) { + // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + // Big(min_token_y_amount).minus(new_token_y_amount) + // ); + // } // const remove_part_token_x_amount = new Big( // whole_token_x_amount || 0 @@ -489,23 +489,23 @@ export const RemovePoolV3 = (props: any) => { }); batch_remove_liquidity = batchRemoveLiquidity; } - const widthdraw_infos = { - min_withdraw_token_x_amount: toNonDivisibleNumber( - tokenX.decimals, - min_withdraw_token_x_amount.toFixed() - ), - min_withdraw_token_y_amount: toNonDivisibleNumber( - tokenY.decimals, - min_withdraw_token_y_amount.toFixed() - ), - }; + // const widthdraw_infos = { + // min_withdraw_token_x_amount: toNonDivisibleNumber( + // tokenX.decimals, + // min_withdraw_token_x_amount.toFixed() + // ), + // min_withdraw_token_y_amount: toNonDivisibleNumber( + // tokenY.decimals, + // min_withdraw_token_y_amount.toFixed() + // ), + // }; batch_remove_liquidity_contract({ token_x: tokenX, token_y: tokenY, batch_remove_liquidity, batch_update_liquidity, mint_liquidities, - widthdraw_infos, + // widthdraw_infos, }); } function get_minimum_received_data() { diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 511a04a59..3cf7a497b 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1434,7 +1434,6 @@ export function RecentTransactions({ const txLink = ( @@ -1467,9 +1466,17 @@ export function RecentTransactions({ - {tx.timestamp} - - {txLink} + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + ); diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index a04a08aa0..8a0ca3c0a 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -289,6 +289,9 @@ export default function AddYourLiquidityPageV3() { currentSelectedPool, liquidityShape, ]); + useEffect(() => { + set_token_amount_tip(null); + }, [tokenXAmount, tokenYAmount ,currentSelectedPool]) async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -853,7 +856,7 @@ export default function AddYourLiquidityPageV3() { const slot_number_in_a_bin = SLOT_NUMBER; const binWidth = slot_number_in_a_bin * point_delta; const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(<>); + set_token_amount_tip(null); if (liquidityShape == 'Curve') { if (bin_number_left > 1) { // 左侧做等差 @@ -1846,8 +1849,8 @@ export default function AddYourLiquidityPageV3() { }); return new_liquidities; } - function pointAndshapeAndAmountChange() { - set_token_amount_tip(''); + function pointAndshapeChange() { + set_token_amount_tip(null); if (liquidityShape == 'Spot') { if (tokenXAmount) { changeTokenXAmount(tokenXAmount); @@ -1889,7 +1892,7 @@ export default function AddYourLiquidityPageV3() { token_amount_tip, set_token_amount_tip, switch_pool_loading, - pointAndshapeAndAmountChange, + pointAndshapeChange, get_y_nfts_contain_current_curve, get_x_nfts_contain_current_curve, @@ -2576,7 +2579,7 @@ function SetPointsComponent() { tokenY, tokenXAmount, tokenYAmount, - pointAndshapeAndAmountChange, + pointAndshapeChange, pointChange, currentPoint, @@ -2659,9 +2662,9 @@ function SetPointsComponent() { // 数据有变动==》去掉token 太少提示 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - pointAndshapeAndAmountChange(); + pointAndshapeChange(); } - }, [liquidityShape, tokenXAmount, tokenYAmount, leftPoint, rightPoint]); + }, [liquidityShape,leftPoint, rightPoint]); // 修改bin --> 合适的右点位 --->合适的bin function changeBin(bin: number) { diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 74ca81fb2..d119bc4dc 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -2187,7 +2187,6 @@ export function RecentTransactions({ const txLink = ( @@ -2218,9 +2217,17 @@ export function RecentTransactions({ - {tx.timestamp} - - {txLink} + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + ); @@ -2249,7 +2256,6 @@ export function RecentTransactions({ const txLink = ( @@ -2290,9 +2296,17 @@ export function RecentTransactions({ - {tx.timestamp} - - {txLink} + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + ); @@ -2330,21 +2344,21 @@ export function RecentTransactions({ const txLink = ( ); - + console.log(' {tx}',tx) return ( + {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && - 'Cancelled'} + 'Cancel'} - {tx.method_name.toLowerCase().indexOf('place') > -1 && 'Place'} + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Place'} @@ -2378,11 +2392,17 @@ export function RecentTransactions({ - - {tx.timestamp} + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} - - {txLink} ); diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 685bd93b8..9fa4f943f 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1261,17 +1261,17 @@ export const batch_remove_liquidity_contract = async ({ batch_remove_liquidity, batch_update_liquidity, mint_liquidities, - widthdraw_infos, + // widthdraw_infos, }: { token_x: TokenMetadata; token_y: TokenMetadata; batch_remove_liquidity: IRemoveLiquidityInfo[]; batch_update_liquidity: IBatchUpdateiquidityInfo; mint_liquidities: UserLiquidityInfo[]; - widthdraw_infos: { - min_withdraw_token_x_amount: string; - min_withdraw_token_y_amount: string; - }; + // widthdraw_infos: { + // min_withdraw_token_x_amount: string; + // min_withdraw_token_y_amount: string; + // }; }) => { const max_number = 20; const transactions: Transaction[] = []; @@ -1344,22 +1344,16 @@ export const batch_remove_liquidity_contract = async ({ }); } const widthdrawActions: any[] = []; - const { min_withdraw_token_x_amount, min_withdraw_token_y_amount } = - widthdraw_infos; - if (Big(min_withdraw_token_x_amount).gt(0)) { - widthdrawActions.push({ - methodName: 'withdraw_asset', - args: { token_id: token_x.id, amount: min_withdraw_token_x_amount }, - gas: '55000000000000', - }); - } - if (Big(min_withdraw_token_y_amount).lt(0)) { - widthdrawActions.push({ - methodName: 'withdraw_asset', - args: { token_id: token_y.id, amount: min_withdraw_token_y_amount }, - gas: '55000000000000', - }); - } + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_x.id}, + gas: '55000000000000', + }); + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_y.id }, + gas: '55000000000000', + }); transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: widthdrawActions, From b2b6cafc886822b906f9ab768cfd2515069cee44 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 18:47:56 +0800 Subject: [PATCH 069/204] update --- src/services/swapV3.ts | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 9fa4f943f..771d5e602 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1273,7 +1273,7 @@ export const batch_remove_liquidity_contract = async ({ // min_withdraw_token_y_amount: string; // }; }) => { - const max_number = 20; + const max_number = 10; const transactions: Transaction[] = []; if (mint_liquidities.length) { const functionCallsV3: any = []; @@ -1320,7 +1320,7 @@ export const batch_remove_liquidity_contract = async ({ const { add_liquidity_infos, remove_liquidity_infos } = batch_update_liquidity; const length = add_liquidity_infos.length; - const ts_length = Math.ceil(length / (max_number / 2)); + const ts_length = Math.ceil(length / max_number); for (let i = 0; i < ts_length; i++) { let batch_update_liquidity_i; const startIndex = i * max_number; From 722271518751f5cd1247c0d6635d1b5c640a2e6a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 19:50:33 +0800 Subject: [PATCH 070/204] update --- src/components/d3Chart/DclChart.tsx | 32 +++++++++++++-------------- src/components/d3Chart/interfaces.tsx | 2 +- src/components/d3Chart/utils.ts | 24 ++++++++++++++++++++ 3 files changed, 41 insertions(+), 17 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index d4ecffff7..b5f9ff4eb 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -31,6 +31,8 @@ import { formatNumber, formatPercentage, formatWithCommas_usd, + formatWithCommas_number, + formatPriceWithCommas } from './utils'; import { get_custom_config_for_chart, @@ -553,7 +555,6 @@ export default function DclChart({ ); const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = d; - const { current_point } = pool; const { colors } = getConfig(); const total_token_x = Big(token_x).plus(order_x); @@ -564,21 +565,20 @@ export default function DclChart({ ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() : '0'; setBinDetail({ - // point, feeApr: formatPercentage(apr), - color: +point > current_point ? colors[1] : colors[0], + colors, ...(total_token_x.gt(0) ? { - token_x_amount: formatNumber(total_token_x.toFixed()), - token_x_amount_in_liquidity: formatNumber(token_x), - token_x_amount_in_order: formatNumber(order_x), + token_x_amount: formatWithCommas_number(total_token_x.toFixed()), + token_x_amount_in_liquidity: formatWithCommas_number(token_x), + token_x_amount_in_order: formatWithCommas_number(order_x), } : {}), ...(total_token_y.gt(0) ? { - token_y_amount: formatNumber(total_token_y.toFixed()), - token_y_amount_in_liquidity: formatNumber(token_y), - token_y_amount_in_order: formatNumber(order_y), + token_y_amount: formatWithCommas_number(total_token_y.toFixed()), + token_y_amount_in_liquidity: formatWithCommas_number(token_y), + token_y_amount_in_order: formatWithCommas_number(order_y), } : {}), price_by_token_x: formatPrice(price_by_token_x), @@ -1146,13 +1146,13 @@ export default function DclChart({ } function get_current_price_by_token_x() { if (pool) { - return formatPrice(get_current_price()); + return formatPriceWithCommas(get_current_price()); } return '-'; } function get_current_price_by_token_y() { if (pool) { - return formatPrice(reverse_price(get_current_price())); + return formatPriceWithCommas(reverse_price(get_current_price())); } return '-'; } @@ -1359,7 +1359,7 @@ export default function DclChart({ - {/* hover到柱子上的悬浮框 */} + {/* hover到柱子(bin)上的悬浮框 */}
Fee APR (24h) @@ -1392,7 +1392,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.color}`, + backgroundColor: `${binDetail?.colors[1]}`, }} > in Liquidity @@ -1409,7 +1409,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.color}`, + backgroundColor: `${binDetail?.colors[1]}`, }} > by Limit Orders @@ -1438,7 +1438,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.color}`, + backgroundColor: `${binDetail?.colors[0]}`, }} > in Liquidity @@ -1455,7 +1455,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.color}`, + backgroundColor: `${binDetail?.colors[0]}`, }} > by Limit Orders diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 009ec89fc..e0de193d0 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -35,7 +35,7 @@ export interface IBinDetail { token_y_amount_in_order?: string; price_by_token_x?: string; price_by_token_y?: string; - color: string; + colors: string[]; } export interface IPoolChartConfig { diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index 79fdf0427..34422a0d8 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -39,6 +39,17 @@ export const formatNumber = (v: string | number) => { return big.toFixed(2, 1); } }; +export const formatWithCommas_number = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return formatWithCommas(big.toFixed(2, 1)); + } +}; export const formatPrice = (v: string | number) => { if (isInvalid(v)) return '-'; const big = Big(v); @@ -50,6 +61,19 @@ export const formatPrice = (v: string | number) => { return big.toFixed(4, 0); } }; +export const formatPriceWithCommas = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.0001)) { + return '<0.0001'; + } else { + const p = big.toFixed(4, 0); + const [whole, decimal] = p.split('.') + return `${formatWithCommas(whole)}${decimal ? '.' + decimal : ''}`; + } +}; export const formatToInternationalCurrencySystem$ = (v: string | number) => { if (isInvalid(v)) return '$-'; return '$' + toInternationalCurrencySystem(Big(v || 0).toFixed(), 2); From 450b47243f9f841e0d629a59e062b6395a64c902 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 5 Jul 2023 22:09:40 +0800 Subject: [PATCH 071/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 23 ++++++++------ src/components/pool/RemovePoolV3.tsx | 33 ++++++++++++++------ src/components/swap/SwapLimitOrderChart.tsx | 2 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 +-- 4 files changed, 39 insertions(+), 23 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index b5f9ff4eb..1d6cf4d51 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1184,6 +1184,9 @@ export default function DclChart({ setZoom(targetPercent); } } + const rangeGear = getConfig().rangeGear; + const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; + const is_in_min_zoom = zoom == rangeGear[0]; return (
{/* 控件按钮*/} -
-
+ {/*
-
+
*/}
-
-
+
*/}
@@ -1595,7 +1598,7 @@ function AddIcon(props: any) { y1="4.43054" x2="8.09615" y2="4.43054" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> @@ -1604,7 +1607,7 @@ function AddIcon(props: any) { y1="0.826904" x2="4.49268" y2="8.17306" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> @@ -1627,7 +1630,7 @@ function SubIcon(props: any) { y1="1.25" x2="8.09615" y2="1.25" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 2e0f07eca..1534ec45f 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -537,19 +537,32 @@ export const RemovePoolV3 = (props: any) => { } if (broken_deleted_nfts.length) { broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const [new_left_point, new_right_point] = get_deleted_range(l); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ + const { amount, left_point, right_point } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ left_point: new_left_point, right_point: new_right_point, - amount: l.amount, + amount, }); - total_token_x_amount = total_token_x_amount.plus( - min_token_x_amount || 0 - ); - total_token_y_amount = total_token_y_amount.plus( - min_token_y_amount || 0 - ); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + const broken_min_token_x_amount = Big(min_token_x_amount).minus(new_token_x_amount); + const broken_min_token_y_amount = Big(min_token_y_amount).minus(new_token_y_amount); + if (broken_min_token_x_amount.gt(0)) { + total_token_x_amount = total_token_x_amount.plus( + broken_min_token_x_amount || 0 + ); + } + if (broken_min_token_y_amount.gt(0)) { + total_token_y_amount = total_token_y_amount.plus( + broken_min_token_y_amount || 0 + ); + } }); } if (tokenPriceList && tokenMetadata_x_y) { diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index e4d64f4f4..d763e9e06 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -851,7 +851,7 @@ function OrderChart() { d3.select('.verticalDashLine').attr('d', ''); d3.select('.horizontalDashLine').attr('d', ''); } - // 找到离这个点最近的一个数据 + // 找到离这个点最近的一个数据 中文 function searchNearCoordinate( list: IOrderPointItem[], e: any, diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 8a0ca3c0a..de9237051 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2884,7 +2884,7 @@ function SetPointsComponent() { { @@ -2899,7 +2899,7 @@ function SetPointsComponent() { { From c35f7e4a01cd68e49ccc668abb38bad4c5485c9f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 6 Jul 2023 11:40:22 +0800 Subject: [PATCH 072/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 125 +++++++++++++------- 1 file changed, 82 insertions(+), 43 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index d763e9e06..4982630ea 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -38,7 +38,8 @@ export default function SwapLimitOrderChart() { const [sell_list, set_sell_list] = useState(); const [market_loading, set_market_loading] = useState(false); const { dcl_pool_id } = useContext(SwapProContext); - // todo + const GEARS = [15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + const [zoom, setZoom] = useState(GEARS[0]); const pool_id = dcl_pool_id; const left_point = -800000; const right_point = 600000; @@ -47,6 +48,7 @@ export default function SwapLimitOrderChart() { get_points_of_orders(); get_pool_detail(); set_switch_token('X'); + setZoom(GEARS[0]) } }, [pool_id]); useEffect(() => { @@ -277,6 +279,23 @@ export default function SwapLimitOrderChart() { function marketRefresh() { set_market_loading(true); } + // 缩小坐标轴区间范围 + function zoomOut() { + const targetPercent = GEARS.find((item) => item < zoom); + if (targetPercent) { + setZoom(targetPercent); + } + console.log('缩小中- targetPercent', targetPercent); + } + // 放大坐标轴区间范围 + function zoomIn() { + const GEARSCOPY:number[] = JSON.parse(JSON.stringify(GEARS)).reverse(); + const targetPercent = GEARSCOPY.find((item) => item > zoom); + if (targetPercent) { + setZoom(targetPercent); + } + console.log('放大中- targetPercent', targetPercent); + } return (
@@ -302,58 +323,61 @@ export default function SwapLimitOrderChart() { {/* base data */}
{get_rate_element()}
-
-
- { - set_switch_token('X'); - }} - className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ - switch_token == 'X' - ? 'bg-proTabBgColor text-white' - : 'text-primaryText' - }`} - > - {pool?.token_x_metadata?.symbol} - - { - set_switch_token('Y'); - }} - className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ - switch_token == 'Y' - ? 'bg-proTabBgColor text-white' - : 'text-primaryText' - }`} - > - {pool?.token_y_metadata?.symbol} - +
+
+
+ { + set_switch_token('X'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'X' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_x_metadata?.symbol} + + { + set_switch_token('Y'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'Y' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_y_metadata?.symbol} + +
-
-
+ {/*
-
+
*/}
-
-
+
*/}
@@ -465,6 +489,8 @@ function OrderChart() { pool, get_price_by_point, switch_token, + zoom, + GEARS }: { buy_list: IOrderPointItem[]; sell_list: IOrderPointItem[]; @@ -473,6 +499,8 @@ function OrderChart() { pool: PoolInfo; get_price_by_point: Function; switch_token: ISwitchToken; + zoom:number; + GEARS:number[]; } = useContext(LimitOrderChartData); const [foucsOrderPoint, setFoucsOrderPoint] = useState(); @@ -490,7 +518,7 @@ function OrderChart() { } else { clearChart(); } - }, [buy_list, sell_list]); + }, [buy_list, sell_list, zoom]); function drawChart() { clearChart(); const { price_range, amount_range, buy_list_new, sell_list_new } = @@ -709,7 +737,7 @@ function OrderChart() { }); } } - function get_data_for_drawing() { + function gte_price_range_by_zoom() { // 获取价格区间 let min_price: any; let max_price: any; @@ -733,6 +761,17 @@ function OrderChart() { } else { max_price = Big(sell_list[0].price).mul(1.1).toFixed(); } + let new_min_price: any; + let new_max_price: any; + const each_step_range = Big(max_price).minus(min_price).div(GEARS[0] * 2); + const total_step_range = each_step_range.mul(GEARS[0] - zoom); + new_min_price = Big(min_price).plus(total_step_range).toFixed(); + new_max_price = Big(max_price).minus(total_step_range).toFixed(); + return [new_min_price, new_max_price] + } + function get_data_for_drawing() { + // 获取价格区间 + const [min_price, max_price] = gte_price_range_by_zoom() // 获取 数量区间 const amounts: string[] = []; buy_list.concat(sell_list).forEach((item: IOrderPointItem) => { @@ -1128,7 +1167,7 @@ function AddIcon(props: any) { y1="4.43054" x2="8.09615" y2="4.43054" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> @@ -1137,7 +1176,7 @@ function AddIcon(props: any) { y1="0.826904" x2="4.49268" y2="8.17306" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> @@ -1160,7 +1199,7 @@ function SubIcon(props: any) { y1="1.25" x2="8.09615" y2="1.25" - stroke="#91A2AE" + stroke="currentColor" stroke-width="1.5" stroke-linecap="round" /> From 8dd26c988b24b9ff5ba29b37cf34b2be47c0b8e9 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 6 Jul 2023 15:42:27 +0800 Subject: [PATCH 073/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 16 +++-- src/components/d3Chart/interfaces.tsx | 1 + src/components/d3Chart/utils.ts | 2 +- src/components/pool/RemovePoolV3.tsx | 6 +- src/components/pool/YourLiquidityV2.tsx | 7 +- src/components/swap/SwapLimitOrderChart.tsx | 48 ++++++++----- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 +- src/pages/poolsV3/PoolDetailV3.tsx | 76 +++++++++----------- src/pages/poolsV3/YourLiquidityPageV3.tsx | 17 ----- src/services/commonV3.ts | 6 +- src/services/swapV3.ts | 6 +- 11 files changed, 90 insertions(+), 99 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 1d6cf4d51..fc0f06d13 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -32,7 +32,7 @@ import { formatPercentage, formatWithCommas_usd, formatWithCommas_number, - formatPriceWithCommas + formatPriceWithCommas, } from './utils'; import { get_custom_config_for_chart, @@ -1205,13 +1205,21 @@ export default function DclChart({
*/}
@@ -1494,7 +1502,7 @@ export default function DclChart({
- 24h APR + Trailing 24hr APR {user_liquidities_detail?.apr_24 || '-'} diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index e0de193d0..30b9cdd0c 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -78,6 +78,7 @@ export interface IDCLAccountFee { user_token: { token_x: string; token_y: string; + timestamp: number; }; change_log_data: IDclLogData[]; }; diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index 34422a0d8..c0fe8e242 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -70,7 +70,7 @@ export const formatPriceWithCommas = (v: string | number) => { return '<0.0001'; } else { const p = big.toFixed(4, 0); - const [whole, decimal] = p.split('.') + const [whole, decimal] = p.split('.'); return `${formatWithCommas(whole)}${decimal ? '.' + decimal : ''}`; } }; diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 1534ec45f..f6c070ea8 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -551,8 +551,10 @@ export const RemovePoolV3 = (props: any) => { right_point: right_point, amount, }); - const broken_min_token_x_amount = Big(min_token_x_amount).minus(new_token_x_amount); - const broken_min_token_y_amount = Big(min_token_y_amount).minus(new_token_y_amount); + const broken_min_token_x_amount = + Big(min_token_x_amount).minus(new_token_x_amount); + const broken_min_token_y_amount = + Big(min_token_y_amount).minus(new_token_y_amount); if (broken_min_token_x_amount.gt(0)) { total_token_x_amount = total_token_x_amount.plus( broken_min_token_x_amount || 0 diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index ac5e04b5d..78297d640 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -363,12 +363,7 @@ export function YourLiquidityV2(props: any) { >
-
- -
+
Trailing 24hr APR
(); const [market_loading, set_market_loading] = useState(false); const { dcl_pool_id } = useContext(SwapProContext); - const GEARS = [15,14,13,12,11,10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; - const [zoom, setZoom] = useState(GEARS[0]); + const GEARS = [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; + const [zoom, setZoom] = useState(GEARS[0]); const pool_id = dcl_pool_id; const left_point = -800000; const right_point = 600000; @@ -48,7 +48,7 @@ export default function SwapLimitOrderChart() { get_points_of_orders(); get_pool_detail(); set_switch_token('X'); - setZoom(GEARS[0]) + setZoom(GEARS[0]); } }, [pool_id]); useEffect(() => { @@ -289,7 +289,7 @@ export default function SwapLimitOrderChart() { } // 放大坐标轴区间范围 function zoomIn() { - const GEARSCOPY:number[] = JSON.parse(JSON.stringify(GEARS)).reverse(); + const GEARSCOPY: number[] = JSON.parse(JSON.stringify(GEARS)).reverse(); const targetPercent = GEARSCOPY.find((item) => item > zoom); if (targetPercent) { setZoom(targetPercent); @@ -307,7 +307,7 @@ export default function SwapLimitOrderChart() { get_price_by_point, switch_token, zoom, - GEARS + GEARS, }} >
@@ -323,7 +323,7 @@ export default function SwapLimitOrderChart() { {/* base data */}
{get_rate_element()}
-
+
*/}
@@ -490,7 +498,7 @@ function OrderChart() { get_price_by_point, switch_token, zoom, - GEARS + GEARS, }: { buy_list: IOrderPointItem[]; sell_list: IOrderPointItem[]; @@ -499,8 +507,8 @@ function OrderChart() { pool: PoolInfo; get_price_by_point: Function; switch_token: ISwitchToken; - zoom:number; - GEARS:number[]; + zoom: number; + GEARS: number[]; } = useContext(LimitOrderChartData); const [foucsOrderPoint, setFoucsOrderPoint] = useState(); @@ -761,17 +769,19 @@ function OrderChart() { } else { max_price = Big(sell_list[0].price).mul(1.1).toFixed(); } - let new_min_price: any; - let new_max_price: any; - const each_step_range = Big(max_price).minus(min_price).div(GEARS[0] * 2); - const total_step_range = each_step_range.mul(GEARS[0] - zoom); - new_min_price = Big(min_price).plus(total_step_range).toFixed(); - new_max_price = Big(max_price).minus(total_step_range).toFixed(); - return [new_min_price, new_max_price] + let new_min_price: any; + let new_max_price: any; + const each_step_range = Big(max_price) + .minus(min_price) + .div(GEARS[0] * 2); + const total_step_range = each_step_range.mul(GEARS[0] - zoom); + new_min_price = Big(min_price).plus(total_step_range).toFixed(); + new_max_price = Big(max_price).minus(total_step_range).toFixed(); + return [new_min_price, new_max_price]; } function get_data_for_drawing() { // 获取价格区间 - const [min_price, max_price] = gte_price_range_by_zoom() + const [min_price, max_price] = gte_price_range_by_zoom(); // 获取 数量区间 const amounts: string[] = []; buy_list.concat(sell_list).forEach((item: IOrderPointItem) => { diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index de9237051..15a4a5bee 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -291,7 +291,7 @@ export default function AddYourLiquidityPageV3() { ]); useEffect(() => { set_token_amount_tip(null); - }, [tokenXAmount, tokenYAmount ,currentSelectedPool]) + }, [tokenXAmount, tokenYAmount, currentSelectedPool]); async function getTopPairs() { const listPromise = listPool.map(async (p: PoolInfo) => { const token_x = p.token_x; @@ -2664,7 +2664,7 @@ function SetPointsComponent() { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { pointAndshapeChange(); } - }, [liquidityShape,leftPoint, rightPoint]); + }, [liquidityShape, leftPoint, rightPoint]); // 修改bin --> 合适的右点位 --->合适的bin function changeBin(bin: number) { diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index d119bc4dc..259352d51 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -1012,7 +1012,7 @@ function YourLiquidityBox(props: { - ~{getTotalLiquditiesTvl()} + {getTotalLiquditiesTvl()}
{/* chart area */} @@ -1087,12 +1087,7 @@ function YourLiquidityBox(props: {
- - - + Trailing 24hr APR
{accountAPR || '-'} @@ -1287,15 +1282,8 @@ function UnclaimedFeesBox(props: any) { - - {/* {liquidities?.length > 1 ? ( - - {liquidities.length} NFTs - - ) : null} */} - - ~{getTotalLiquditiesFee()} + {getTotalLiquditiesFee()}
@@ -2276,27 +2264,35 @@ export function RecentTransactions({ - - {displayInAmount} - - - - {toRealSymbol(swapIn.symbol)} - - - + - - - {displayOutAmount} - - - - {toRealSymbol(swapOut.symbol)} - + {Big(AmountIn || 0).gt(0) ? ( + <> + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + ) : null} + {Big(AmountIn || 0).gt(0) && Big(AmountOut || 0).gt(0) ? ( + + + ) : null} + {Big(AmountOut || 0).gt(0) ? ( + <> + {' '} + + {displayOutAmount} + + + {toRealSymbol(swapOut.symbol)} + + + ) : null} - { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); @@ -2323,7 +2319,7 @@ export function RecentTransactions({ const displayInAmount = Number(AmountIn) < 0.01 ? '<0.01' - : numberWithCommas(toPrecision(AmountIn, 6)); + : numberWithCommas(toPrecision(AmountIn, 3)); const price = pointToPrice({ tokenA: swapIn, @@ -2333,13 +2329,12 @@ export function RecentTransactions({ ? Number(tx.point) : -Number(tx.point), }); - - const AmountOut = new Big(AmountIn).mul(price).toFixed(0, 0); + const AmountOut = new Big(AmountIn).mul(price).toFixed(); const displayOutAmount = Number(AmountOut) < 0.01 ? '<0.01' - : numberWithCommas(toPrecision(AmountOut, 6)); + : numberWithCommas(toPrecision(AmountOut, 3)); const txLink = ( ); - console.log(' {tx}',tx) return ( - - {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && - 'Cancel'} + {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && 'Cancel'} {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Place'} @@ -2392,7 +2384,7 @@ export function RecentTransactions({ - { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index bc283e907..3dff7d142 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -382,23 +382,6 @@ export default function YourLiquidityPageV3() { DCL ({+v2LiquidityQuantity}) -
- - -

diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 3d6e50e5e..d40bc97b0 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1559,10 +1559,10 @@ export function get_account_24_apr( // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; - const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 + // const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 const second24 = 24 * 60 * 60; + const { token_x, token_y, timestamp: current_time } = user_token; const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 - const { token_x, token_y } = user_token; const token_x_NonDivisible = toNonDivisibleNumber( token_x_metadata.decimals, Big(token_x || 0).toFixed() @@ -1631,7 +1631,7 @@ export function get_account_24_apr( }); const principal = total_processed_log_value.div(second24); if (principal.gt(0)) { - apr_24 = total_fee_24_value.div(principal).mul(100).toFixed(); + apr_24 = total_fee_24_value.div(principal).mul(365).mul(100).toFixed(); } return apr_24; } diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 771d5e602..59b70a381 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1261,8 +1261,8 @@ export const batch_remove_liquidity_contract = async ({ batch_remove_liquidity, batch_update_liquidity, mint_liquidities, - // widthdraw_infos, -}: { +}: // widthdraw_infos, +{ token_x: TokenMetadata; token_y: TokenMetadata; batch_remove_liquidity: IRemoveLiquidityInfo[]; @@ -1346,7 +1346,7 @@ export const batch_remove_liquidity_contract = async ({ const widthdrawActions: any[] = []; widthdrawActions.push({ methodName: 'withdraw_asset', - args: { token_id: token_x.id}, + args: { token_id: token_x.id }, gas: '55000000000000', }); widthdrawActions.push({ From 0785a72a7ab874ef24569ceb8d91b6c2824d931d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 7 Jul 2023 14:37:14 +0800 Subject: [PATCH 074/204] update your liquidity --- src/components/pool/YourLiquidityV2.tsx | 51 ++------------- src/components/pool/utils.ts | 84 +++++++++++++++++++++++++ 2 files changed, 90 insertions(+), 45 deletions(-) create mode 100644 src/components/pool/utils.ts diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 78297d640..5da8a4e64 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -67,8 +67,8 @@ import { isMobile } from '~utils/device'; import Big from 'big.js'; import { useWalletSelector } from '~context/WalletSelectorContext'; import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; -import { formatPercentage } from '../../components/d3Chart/utils'; import { getDCLAccountFee } from '../../services/indexer'; +import { formatNumber, formatWithCommas_usd, formatPercentage } from './utils'; const is_mobile = isMobile(); const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const LiquidityContext = createContext(null); @@ -1770,12 +1770,7 @@ function UserLiquidityLineStyleGroup1({ (range) => current_point >= range[0] && current_point <= range[1] ); - const canClaim = groupYourLiquidityList.reduce((pre, cur) => { - const liquidityDetail = cur.liquidityDetail; - const { unclaimed_fee_x, unclaimed_fee_y } = liquidityDetail; - - return !!pre && !!(+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0); - }, true); + const canClaim = tokenFeeLeft.plus(tokenFeeRight).gt(0); // on farm not on myself const farmApr: Big = !publicData.related_seed_info.your_apr @@ -1816,17 +1811,9 @@ function UserLiquidityLineStyleGroup1({ return { ...publicData, - your_liquidity: - '$' + - (your_liquidity.toNumber() < 0.01 - ? '< 0.01' - : your_liquidity.toFixed(2, 0)), - tokenFeeLeft: - tokenFeeLeft.toNumber() < 0.01 ? '< 0.01' : tokenFeeLeft.toFixed(2, 0), - tokenFeeRight: - tokenFeeRight.toNumber() < 0.01 - ? '< 0.01' - : tokenFeeRight.toFixed(2, 0), + your_liquidity: formatWithCommas_usd(your_liquidity.toFixed()), + tokenFeeLeft: formatNumber(tokenFeeLeft.toFixed()), + tokenFeeRight: formatNumber(tokenFeeRight.toFixed()), intersectionRangeList: intersectionRangeList.map((range) => [ new Big(range[0]).toFixed(), new Big(range[1]).toFixed(), @@ -2141,18 +2128,12 @@ function UserLiquidityLineStyleGroup1({ { e.stopPropagation(); - // setShowAddBox(true); - history.push('/addLiquidityV2'); }} color="#fff" minWidth="5rem" - disabled={is_in_farming} borderRadius="8px" - btnClassName={is_in_farming ? 'cursor-not-allowed' : ''} - className={`px-3 w-24 h-8 text-center text-sm text-white focus:outline-none mr-2.5 ${ - is_in_farming ? 'opacity-40 ' : '' - }`} + className={`px-3 w-24 h-8 text-center text-sm text-white focus:outline-none mr-2.5`} > @@ -2450,26 +2431,6 @@ function UserLiquidityLineStyleGroup1({ }} > ) : null} - {/* { - setShowAddBox(false); - }} - tokenMetadata_x_y={tokenMetadata_x_y} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={liquidityDetail} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > */}

); } diff --git a/src/components/pool/utils.ts b/src/components/pool/utils.ts new file mode 100644 index 000000000..c0fe8e242 --- /dev/null +++ b/src/components/pool/utils.ts @@ -0,0 +1,84 @@ +import Big from 'big.js'; +import { + formatWithCommas, + toInternationalCurrencySystem, +} from '../../utils/numbers'; +export const formatWithCommas_usd = (v: string | number) => { + if (isInvalid(v)) return '$-'; + const big = Big(v); + if (big.eq(0)) { + return '$0'; + } else if (big.lt(0.01)) { + return '<$0.01'; + } else if (big.lt(10000)) { + return '$' + formatWithCommas(big.toFixed(2, 1)); + } else { + return '$' + formatWithCommas(big.toFixed(0, 1)); + } +}; + +export const formatPercentage = (v: string | number) => { + if (isInvalid(v)) return '-%'; + const big = Big(v); + if (big.eq(0)) { + return '0%'; + } else if (big.lt(0.01)) { + return '<0.01%'; + } else { + return big.toFixed(2, 1) + '%'; + } +}; +export const formatNumber = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return big.toFixed(2, 1); + } +}; +export const formatWithCommas_number = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.01)) { + return '<0.01'; + } else { + return formatWithCommas(big.toFixed(2, 1)); + } +}; +export const formatPrice = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.0001)) { + return '<0.0001'; + } else { + return big.toFixed(4, 0); + } +}; +export const formatPriceWithCommas = (v: string | number) => { + if (isInvalid(v)) return '-'; + const big = Big(v); + if (big.eq(0)) { + return '0'; + } else if (big.lt(0.0001)) { + return '<0.0001'; + } else { + const p = big.toFixed(4, 0); + const [whole, decimal] = p.split('.'); + return `${formatWithCommas(whole)}${decimal ? '.' + decimal : ''}`; + } +}; +export const formatToInternationalCurrencySystem$ = (v: string | number) => { + if (isInvalid(v)) return '$-'; + return '$' + toInternationalCurrencySystem(Big(v || 0).toFixed(), 2); +}; +export const isInvalid = function (v: any) { + if (v === '' || v === undefined || v === null) return true; + return false; +}; From 13ac98049e8605a0623299361f700c2b8980887b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 8 Jul 2023 18:51:06 +0800 Subject: [PATCH 075/204] update your liquidity page --- src/components/pool/YourLiquidityV2.tsx | 671 +++++++++++++++------- src/pages/poolsV3/YourLiquidityPageV3.tsx | 1 - src/services/commonV3.ts | 70 ++- tailwind.config.js | 2 +- 4 files changed, 546 insertions(+), 198 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 5da8a4e64..7e6d01fea 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -44,6 +44,10 @@ import { openUrl, sort_tokens_by_base, get_account_24_apr, + get_matched_all_seeds_of_a_dcl_pool, + whether_liquidity_can_farm_in_seed, + get_valid_range, + get_total_value_by_liquidity_amount_dcl, } from '../../services/commonV3'; import BigNumber from 'bignumber.js'; import { @@ -69,6 +73,7 @@ import { useWalletSelector } from '~context/WalletSelectorContext'; import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; import { getDCLAccountFee } from '../../services/indexer'; import { formatNumber, formatWithCommas_usd, formatPercentage } from './utils'; +import { FarmStamp, FarmStampNew } from '~components/icon/FarmStamp'; const is_mobile = isMobile(); const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const LiquidityContext = createContext(null); @@ -141,6 +146,68 @@ export function YourLiquidityV2(props: any) { } return temp_map; }, [liquidities_details_list]); + useEffect(() => { + if ( + !liquidities_list || + !all_pools_map || + !liquidities_tokens_metas || + !tokenPriceList || + !dcl_liquidities_details_map || + Object.keys(dcl_liquidities_details_map).length === 0 + ) { + return; + } + Promise.all( + liquidities_list.map((liquidity) => + getYourLiquidityData({ + liquidity, + all_seeds, + styleType, + tokenPriceList, + poolDetail: all_pools_map?.[liquidity.pool_id], + liquidityDetail: dcl_liquidities_details_map?.[liquidity.lpt_id], + liquidities_tokens_metas, + }) + ) + ).then((yourLiquidityList) => { + const groupedLiquidity = yourLiquidityList.reduce((acc, cur) => { + const { pool_id } = cur; + const [token_x, token_y] = pool_id.split('|'); + const poolDetail = all_pools_map[pool_id]; + const tokensMeta = [ + liquidities_tokens_metas[token_x], + liquidities_tokens_metas[token_y], + ]; + + if (acc[pool_id]) { + acc[pool_id].push(cur); + + return acc; + } else { + return { + ...acc, + [pool_id]: [ + { + ...cur, + poolDetail, + tokensMeta, + }, + ], + }; + } + }, {} as Record); + + setGroupYourLiquidity(groupedLiquidity); + }); + }, [ + liquidities_list, + all_seeds, + all_pools_map, + liquidities_tokens_metas, + tokenPriceList, + dcl_liquidities_details_map, + Object.keys(dcl_liquidities_details_map).length === 0, + ]); async function get_list_liquidities() { const list: UserLiquidityInfo[] = await list_liquidities(); if (list.length > 0) { @@ -278,73 +345,6 @@ export function YourLiquidityV2(props: any) { setYourLpValueV2 && setYourLpValueV2(total_value.toFixed()); } } - console.log('liquidities_list: ', liquidities_list); - console.log('groupYourLiquidity: ', groupYourLiquidity); - - useEffect(() => { - if ( - !liquidities_list || - !all_pools_map || - !liquidities_tokens_metas || - !tokenPriceList || - !dcl_liquidities_details_map || - Object.keys(dcl_liquidities_details_map).length === 0 - ) { - return; - } - - Promise.all( - liquidities_list.map((liquidity) => - getYourLiquidityData({ - liquidity, - all_seeds, - styleType, - tokenPriceList, - poolDetail: all_pools_map?.[liquidity.pool_id], - liquidityDetail: dcl_liquidities_details_map?.[liquidity.lpt_id], - liquidities_tokens_metas, - }) - ) - ).then((yourLiquidityList) => { - const groupedLiquidity = yourLiquidityList.reduce((acc, cur) => { - const { pool_id } = cur; - const [token_x, token_y] = pool_id.split('|'); - const poolDetail = all_pools_map[pool_id]; - const tokensMeta = [ - liquidities_tokens_metas[token_x], - liquidities_tokens_metas[token_y], - ]; - - if (acc[pool_id]) { - acc[pool_id].push(cur); - - return acc; - } else { - return { - ...acc, - [pool_id]: [ - { - ...cur, - poolDetail, - tokensMeta, - }, - ], - }; - } - }, {} as Record); - - setGroupYourLiquidity(groupedLiquidity); - }); - }, [ - liquidities_list, - all_seeds, - all_pools_map, - liquidities_tokens_metas, - tokenPriceList, - dcl_liquidities_details_map, - Object.keys(dcl_liquidities_details_map).length === 0, - ]); - return (
{liquidityLoadingDone && ( @@ -382,6 +382,7 @@ export function YourLiquidityV2(props: any) { groupYourLiquidityList={liquidity} liquidities_list={liquidity.map((l: any) => l.liquidityDetail)} tokenPriceList={tokenPriceList} + all_seeds={all_seeds} /> ); } @@ -792,49 +793,6 @@ async function getYourLiquidityData({ liquidities_tokens_metas: Record; liquidityDetail: UserLiquidityInfo; }) { - const { lpt_id, pool_id, left_point, right_point, amount: L } = liquidity; - const [token_x, token_y, fee] = pool_id.split('|'); - - const tokenMetadata_x_y = liquidities_tokens_metas - ? [liquidities_tokens_metas[token_x], liquidities_tokens_metas[token_y]] - : null; - - let rate_need_to_reverse_display: boolean; - - if (tokenMetadata_x_y) { - const [tokenX] = tokenMetadata_x_y; - if (TOKEN_LIST_FOR_RATE.indexOf(tokenX.symbol) > -1) - rate_need_to_reverse_display = true; - else rate_need_to_reverse_display = false; - } - - let your_liquidity: any; - - if (tokenMetadata_x_y && poolDetail && tokenPriceList) { - const { current_point } = poolDetail; - your_liquidity = get_your_liquidity(current_point, left_point, right_point); - } - - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - console.log('liquidity: ', liquidity); - - let related_farms: any; - - if (is_in_farming) { - const id = liquidity.mft_id.slice(1); - const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + id; - related_farms = await list_seed_farms(seed_id); - } - - const related_seed_info = get_detail_the_liquidity_refer_to_seed({ - liquidity, - all_seeds, - is_in_farming, - related_farms, - tokenPriceList, - }); - function get_your_liquidity_in_farm_range() { if (!related_seed_info.targetSeed || !related_seed_info.targetSeed.seed_id) return '0'; @@ -852,7 +810,6 @@ async function getYourLiquidityData({ right_point <= farmRangeRight ? right_point : farmRangeRight ); } - function get_your_liquidity( current_point: number, left_point: number, @@ -946,7 +903,6 @@ async function getYourLiquidityData({ ); return { amountx: amountX_read, amounty: amountY_read }; } - function getTokenFeeAmount(p: string) { if (liquidityDetail && tokenMetadata_x_y && tokenPriceList) { const [tokenX, tokenY] = tokenMetadata_x_y; @@ -978,11 +934,6 @@ async function getYourLiquidityData({ } } } - - const tokenFeeLeft = getTokenFeeAmount('l'); - - const tokenFeeRight = getTokenFeeAmount('r'); - function getRate(direction: string) { let value = ''; if (tokenMetadata_x_y) { @@ -1000,7 +951,6 @@ async function getYourLiquidityData({ } return value; } - function go_farm() { const [fixRange, pool_id, left_point, right_point] = liquidity.mft_id.split('&'); @@ -1018,11 +968,6 @@ async function getYourLiquidityData({ } openUrl(url); } - - const rangeMin = getRate(rate_need_to_reverse_display ? 'right' : 'left'); - - const rangeMax = getRate(rate_need_to_reverse_display ? 'left' : 'right'); - function getRateMapTokens() { if (tokenMetadata_x_y) { const [tokenX, tokenY] = tokenMetadata_x_y; @@ -1033,11 +978,46 @@ async function getYourLiquidityData({ } } } + const { lpt_id, pool_id, left_point, right_point, amount: L } = liquidity; + const [token_x, token_y, fee] = pool_id.split('|'); + const tokenMetadata_x_y = liquidities_tokens_metas + ? [liquidities_tokens_metas[token_x], liquidities_tokens_metas[token_y]] + : null; + let rate_need_to_reverse_display: boolean; + if (tokenMetadata_x_y) { + const [tokenX] = tokenMetadata_x_y; + if (TOKEN_LIST_FOR_RATE.indexOf(tokenX.symbol) > -1) + rate_need_to_reverse_display = true; + else rate_need_to_reverse_display = false; + } + // the value of the liquidity; + let your_liquidity: any; + if (tokenMetadata_x_y && poolDetail && tokenPriceList) { + const { current_point } = poolDetail; + your_liquidity = get_your_liquidity(current_point, left_point, right_point); + } + const is_in_farming = + liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; + let related_farms: any; + if (is_in_farming) { + const id = liquidity.mft_id.slice(1); + const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + id; + related_farms = await list_seed_farms(seed_id); + } + const related_seed_info = get_detail_the_liquidity_refer_to_seed({ + liquidity, + all_seeds, + is_in_farming, + related_farms, + tokenPriceList, + }); + const tokenFeeLeft = getTokenFeeAmount('l'); + const tokenFeeRight = getTokenFeeAmount('r'); + const rangeMin = getRate(rate_need_to_reverse_display ? 'right' : 'left'); + const rangeMax = getRate(rate_need_to_reverse_display ? 'left' : 'right'); const ratedMapTokens = getRateMapTokens(); - const your_liquidity_farm = get_your_liquidity_in_farm_range(); - return { fee, your_liquidity, @@ -1671,36 +1651,14 @@ function UserLiquidityLineStyleGroup1({ groupYourLiquidityList, liquidities_list, tokenPriceList, + all_seeds, }: { groupYourLiquidityList: any[]; liquidities_list: UserLiquidityInfo[]; tokenPriceList: any; + all_seeds: Seed[]; }) { - // const { - // goYourLiquidityDetailPage, - // Liquidity_icon, - // liquidity_link, - - // // liquidity_staked_farm_status, - // // setShowAddBox, - // // setShowRemoveBox, - // getTokenFeeAmount, - // // canClaim, - // // go_farm, - // mobile_ReferenceToken, - // // claimRewards, - // // claimLoading, - // showRemoveBox, - // poolDetail, - // tokenPriceList, - // liquidityDetail, - // showAddBox, - // } = useContext(LiquidityContext); - const [hover, setHover] = useState(false); const publicData = groupYourLiquidityList[0]; - const [showRemoveBox, setShowRemoveBox] = useState(false); - const [accountAPR, setAccountAPR] = useState(''); - const [claim_loading, set_claim_loading] = useState(false); const { tokenMetadata_x_y, fee, @@ -1711,14 +1669,185 @@ function UserLiquidityLineStyleGroup1({ poolDetail, go_farm, } = publicData; + const [hover, setHover] = useState(false); + const [showRemoveBox, setShowRemoveBox] = useState(false); + const [accountAPR, setAccountAPR] = useState(''); + const [claim_loading, set_claim_loading] = useState(false); + + // new + const [farm_icon, set_farm_icon] = useState<'single' | 'muti'>(); + const [tip_seed, set_tip_seed] = useState(); + const [joined_seeds, set_joined_seeds] = useState(); + const tokens = sort_tokens_by_base(tokenMetadata_x_y); const { accountId } = useWalletSelector(); const history = useHistory(); useEffect(() => { - if (poolDetail && tokenPriceList) { + if (poolDetail && tokenPriceList && tokenMetadata_x_y) { get_24_apr(); } - }, [poolDetail, tokenPriceList]); + }, [poolDetail, tokenPriceList, tokenMetadata_x_y]); + // todo + useEffect(() => { + if ( + all_seeds.length && + groupYourLiquidityList.length && + liquidities_list.length + ) { + // get all seeds of the dcl pool + const [activeSeeds, endedSeeds, matchedSeeds] = + get_matched_all_seeds_of_a_dcl_pool({ + seeds: all_seeds, + pool_id, + }); + // farm icon + if (activeSeeds.length) { + const muti = activeSeeds.find((seed: Seed) => { + return seed.farmList.length > 1; + }); + if (muti) { + set_farm_icon('muti'); + } else { + set_farm_icon('single'); + } + } + // tip + const latest_seed = activeSeeds[0]; + if (latest_seed) { + const exist = groupYourLiquidityList.find((l: any) => { + const liquidity: UserLiquidityInfo = l.liquidity; + const { part_farm_ratio } = liquidity; + const is_in_farming = part_farm_ratio && +part_farm_ratio > 0; + if (!is_in_farming) { + if (whether_liquidity_can_farm_in_seed(liquidity, latest_seed)) { + return true; + } + } + }); + if (exist) { + set_tip_seed({ + seed: latest_seed, + seed_apr: formatPercentage(getSeedApr(latest_seed)), + go_farm_url_link: get_go_seed_link_url(latest_seed), + }); + } + } + // get all seed that user had joined + let joined_seeds: IUserJoinedSeed; + const in_farming_liquidities = groupYourLiquidityList.filter((l: any) => { + return l.is_in_farming; + }); + debugger; + in_farming_liquidities.forEach((l: any) => { + const [fixRange_l, pool_id_l, left_point_l, right_point_l] = + l.liquidity.mft_id.split('&'); + const targetSeed = matchedSeeds.find((seed: Seed) => { + const [contractId, mft_id] = seed.seed_id.split('@'); + if (contractId == REF_UNI_V3_SWAP_CONTRACT_ID) { + const [ + fixRange, + pool_id_from_seed, + left_point_seed, + right_point_seed, + ] = mft_id.split('&'); + return ( + left_point_l == left_point_seed && + right_point_l == right_point_seed + ); + } + }); + if (targetSeed) { + if (!joined_seeds) { + joined_seeds = { + [targetSeed.seed_id]: { + seed: targetSeed, + liquidities: [l.liquidity], + }, + }; + } else if (!joined_seeds[targetSeed.seed_id]) { + joined_seeds[targetSeed.seed_id] = { + seed: targetSeed, + liquidities: [l.liquidity], + }; + } else { + joined_seeds[targetSeed.seed_id].liquidities = [l.liquidity]; + } + } + }); + + // get farm apr and value of user's investment + joined_seeds && + Object.values(joined_seeds).forEach( + (joined_seed_info: IUserJoinedSeedDetail) => { + const { seed, liquidities } = joined_seed_info; + joined_seed_info.seed_apr = formatPercentage(getSeedApr(seed)); + joined_seed_info.value_of_investment = formatWithCommas_usd( + getTotalValueInFarmsOfLiquidities(seed, liquidities) + ); + if (seed.farmList[0].status == 'Ended') { + joined_seed_info.seed_status == 'ended'; + joined_seed_info.seed_status_num == 3; + } else { + if (latest_seed.seed_id == seed.seed_id) { + joined_seed_info.seed_status = 'run'; + joined_seed_info.seed_status_num == 1; + } else { + joined_seed_info.seed_status = 'would_ended'; + joined_seed_info.seed_status_num == 2; + } + } + joined_seed_info.go_farm_url_link = get_go_seed_link_url(seed); + } + ); + set_joined_seeds(joined_seeds); + console.log( + 'pool_id, farm_icon,tip_seed, joined_seeds', + pool_id, + farm_icon, + tip_seed, + joined_seeds + ); + } + }, [groupYourLiquidityList, liquidities_list, tokenPriceList, all_seeds]); + + function get_go_seed_link_url(seed: Seed) { + const [fixRange, dcl_pool_id, left_point_seed, right_point_seed] = + seed.seed_id.split('@')[1].split('&'); + const link_params = `${get_pool_name( + dcl_pool_id + )}[${left_point_seed}-${right_point_seed}]`; + if (seed.farmList[0].status == 'Ended') { + return `/v2farms/${link_params}-e`; + } else { + return `/v2farms/${link_params}-r`; + } + } + function getSeedApr(seed: Seed) { + const farms = seed.farmList; + let apr = 0; + const allPendingFarms = isPending(seed); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { + apr = +new BigNumber(apr).plus(item.apr).toFixed(); + } + }); + return apr * 100; + } + function getTotalValueInFarmsOfLiquidities( + seed: Seed, + liquidities: UserLiquidityInfo[] + ) { + let total_value = Big(0); + liquidities.forEach((l: UserLiquidityInfo) => { + let v = get_range_part_value(l, seed); + if (Big(l.unfarm_part_amount || 0).gt(0)) { + v = Big(l.part_farm_ratio).div(100).mul(v).toFixed(); + } + total_value = total_value.plus(v || 0); + }); + return total_value.toFixed(); + } async function get_24_apr() { let apr_24 = ''; const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ @@ -1727,6 +1856,8 @@ function UserLiquidityLineStyleGroup1({ }); if (dcl_fee_result) { // 24h 利润 中文 + poolDetail.token_x_metadata = tokenMetadata_x_y[0]; + poolDetail.token_y_metadata = tokenMetadata_x_y[1]; apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); } setAccountAPR(formatPercentage(apr_24)); @@ -1910,7 +2041,47 @@ function UserLiquidityLineStyleGroup1({ } return pending; } - + function get_range_part_value(liquidity: UserLiquidityInfo, seed: Seed) { + const [left_point, right_point] = get_valid_range(liquidity, seed.seed_id); + const v = get_liquidity_value_of_range( + liquidity, + seed, + left_point, + right_point + ); + return v; + } + function get_liquidity_value_of_range( + liquidity: UserLiquidityInfo, + seed: Seed, + leftPoint: number, + rightPoint: number + ) { + const { amount } = liquidity; + const poolDetail = seed.pool; + const { token_x, token_y } = poolDetail; + const v = get_total_value_by_liquidity_amount_dcl({ + left_point: leftPoint || liquidity.left_point, + right_point: rightPoint || liquidity.right_point, + poolDetail, + amount, + price_x_y: { + [token_x]: tokenPriceList[token_x]?.price || '0', + [token_y]: tokenPriceList[token_y]?.price || '0', + }, + metadata_x_y: { + [token_x]: tokenMetadata_x_y[0], + [token_y]: tokenMetadata_x_y[1], + }, + }); + return v; + } + function sort_joined_seeds( + b: IUserJoinedSeedDetail, + a: IUserJoinedSeedDetail + ) { + return b.seed_status_num - a.seed_status_num; + } return (
{+fee / 10000}%
+ {farm_icon ? ( +
+ +
+ ) : null}
- {/* 2/3 */} - + {/* Price Range */}
{intersectionRangeList.map((range: string[], i: number) => { @@ -2003,31 +2178,125 @@ function UserLiquidityLineStyleGroup1({
- + {/* Trailing 24hr APR */}
- {accountAPR} - - {related_seed_info.your_apr && ( + {accountAPR || '-'} + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { seed_apr, seed_status } = joined_seed_info; + if (seed_status == 'ended') return null; + if (length == 1) { + return ( +
+ + + + {seed_apr} +
+ ); + } else { + return ( +
+ + {seed_status == 'run' ? 'new' : 'pre.'} APR + + {seed_apr} +
+ ); + } + })} +
+ ) : tip_seed ? ( +
- )} - - {related_seed_info.your_apr - ? !is_in_farming || related_seed_info.status == 'end' - ? '0%' - : getTotalAprForSeed() - : '-'} - + 0% +
+ ) : null}
- + {/* Your Liquidity */}
{your_liquidity} - - {related_seed_info.your_apr ? ( + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { + seed_status, + value_of_investment, + go_farm_url_link, + } = joined_seed_info; + if (length == 1) { + return ( + + ); + } else { + return ( + + ); + } + })} +
+ ) : tip_seed ? ( + + ) : null} + {/* {related_seed_info.your_apr ? (
{in_farm_percent} @@ -2053,37 +2322,34 @@ function UserLiquidityLineStyleGroup1({
) : ( - - )} + )} */}
- {farmApr && (!is_in_farming || related_seed_info.status == 'end') ? ( -
- - - {related_seed_info.status == 'end' ? ( - - ) : ( - - )}{' '} - {getTotalAprForSeed()} - -
{ - openUrl(related_seed_info.link); - }} - > - - {related_seed_info.status == 'end' ? ( - - ) : ( + {/* tip */} + {tip_seed ? ( + ) : null} +
); } +interface IUserJoinedSeed { + seed_id?: IUserJoinedSeedDetail; +} +interface IUserJoinedSeedDetail { + seed: Seed; + liquidities: UserLiquidityInfo[]; + seed_apr?: string; + value_of_investment?: string; + seed_status?: 'run' | 'would_ended' | 'ended'; + seed_status_num?: number; + go_farm_url_link?: string; +} +interface ILatestSeedTip { + seed: Seed; + seed_apr: string; + go_farm_url_link: string; +} diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index 3dff7d142..5257fd946 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -624,7 +624,6 @@ export function get_detail_the_liquidity_refer_to_seed({ if (condition1 && condition2 && condition3) return true; }); const targetSeed = canFarmSeed || active_seeds[0]; - console.log('targetSeed: ', targetSeed); if (targetSeed) { const { seed_id } = targetSeed; const [fixRange, dcl_pool_id, left_point_seed, right_point_seed] = seed_id diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index d40bc97b0..00a18edc6 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -790,6 +790,46 @@ export function get_matched_seeds_for_dcl_pool({ } return activeSeeds; } +// contain run and ended seed +export function get_matched_all_seeds_of_a_dcl_pool({ + seeds, + pool_id, +}: { + seeds: Seed[]; + pool_id: string; +}) { + const matchedSeeds = seeds.filter((seed: Seed) => { + const { seed_id, farmList } = seed; + const [contractId, mft_id] = seed_id.split('@'); + if (contractId == REF_UNI_V3_SWAP_CONTRACT_ID) { + const [fixRange, pool_id_from_seed, left_point, right_point] = + mft_id.split('&'); + return pool_id_from_seed == pool_id; + } + }); + + // sort by the latest + matchedSeeds.sort((b: Seed, a: Seed) => { + const b_latest = getLatestStartTime(b); + const a_latest = getLatestStartTime(a); + if (b_latest == 0) return -1; + if (a_latest == 0) return 1; + return a_latest - b_latest; + }); + const activeSeeds: Seed[] = []; + const endedSeeds: Seed[] = []; + matchedSeeds.forEach((seed: Seed) => { + const { farmList } = seed; + if (farmList[0].status != 'Ended') { + activeSeeds.push(seed); + } else { + endedSeeds.push(seed); + } + }); + + return [activeSeeds, endedSeeds, matchedSeeds]; +} + export function isPending(seed: Seed) { let pending: boolean = true; const farms = seed.farmList; @@ -1535,8 +1575,8 @@ export function get_account_24_apr( tokenPriceList: any ) { const { token_x_metadata, token_y_metadata } = pool; - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const price_x = tokenPriceList?.[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList?.[token_y_metadata.id]?.price || 0; let apr_24 = ''; const { apr } = dclAccountFee; // 24小时平均利润 @@ -1672,3 +1712,29 @@ function process_log_data( token_y: Big(token_y).toFixed(), }; } + +export function whether_liquidity_can_farm_in_seed( + liquidity: UserLiquidityInfo, + seed: Seed +) { + const { mft_id, left_point, right_point, amount } = liquidity; + const { min_deposit, seed_id } = seed; + const [fixRange, dcl_pool_id, left_point_seed, right_point_seed] = seed_id + .split('@')[1] + .split('&'); + const v_liquidity = mint_liquidity(liquidity, seed_id); + const radio = get_intersection_radio({ + left_point_liquidity: left_point, + right_point_liquidity: right_point, + left_point_seed, + right_point_seed, + }); + const condition1 = new BigNumber(v_liquidity).isGreaterThanOrEqualTo( + min_deposit + ); + const condition2 = +radio > 0; + const condition3 = + mft_id || + (!mft_id && new BigNumber(amount).isGreaterThanOrEqualTo(1000000)); + if (condition1 && condition2 && condition3) return true; +} diff --git a/tailwind.config.js b/tailwind.config.js index 12c86688d..984dbf810 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -115,7 +115,7 @@ module.exports = { dclBannerColor: 'linear-gradient(90deg, #00C6A2 2.54%, rgba(91, 64, 255, 0.5) 70%, rgba(91, 64, 255, 0) 100%)', dclIconBgColor: 'linear-gradient(180deg, #00C6A2 0%, #5B40FF 100%)', - dclFarmTipColor: 'linear-gradient(270deg, #00C6A2 0%, #5B40FF 68%)', + dclFarmTipColor: 'linear-gradient(270deg, #00C6A2 0%, rgba(91, 64, 255, 0) 68.86%)', sellGradientRed: 'linear-gradient(180deg, #944A8C 0%, #D26060 100%)', swapCardGradient: 'linear-gradient(180deg, #213441 0%, #15242F 100%)', sellGradientRedReverse: From 38418c9201542443625be0e9bf6255ae3ca4de2a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 8 Jul 2023 19:14:32 +0800 Subject: [PATCH 076/204] update --- src/components/pool/YourLiquidityV2.tsx | 57 +++---------------------- 1 file changed, 7 insertions(+), 50 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 7e6d01fea..d35c0c89c 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1980,12 +1980,9 @@ function UserLiquidityLineStyleGroup1({ your_liquidity, tokenFeeLeft, tokenFeeRight, - is_in_farming, intersectionRangeList, isInRange, - canClaim, - farmApr, - in_farm_percent, + canClaim } = groupedData; function goDetailV2() { @@ -2011,25 +2008,6 @@ function UserLiquidityLineStyleGroup1({ lpt_ids, }); } - - function getTotalAprForSeed() { - const farms = related_seed_info.targetSeed.farmList; - let apr = 0; - const allPendingFarms = isPending(related_seed_info.targetSeed); - farms.forEach(function (item: FarmBoost) { - const pendingFarm = item.status == 'Created' || item.status == 'Pending'; - if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { - apr = +new BigNumber(item.apr).plus(apr).toFixed(); - } - }); - if (apr == 0) { - return '-'; - } else { - apr = +new BigNumber(apr).multipliedBy(100).toFixed(); - return toPrecision(apr.toString(), 2) + '%'; - } - } - function isPending(seed: Seed) { let pending: boolean = true; const farms = seed.farmList; @@ -2191,7 +2169,7 @@ function UserLiquidityLineStyleGroup1({ if (seed_status == 'ended') return null; if (length == 1) { return ( -
+
+
{seed_status == 'run' ? 'new' : 'pre.'} APR @@ -2226,10 +2204,10 @@ function UserLiquidityLineStyleGroup1({ ) : null}
{/* Your Liquidity */} -
+
{your_liquidity} {joined_seeds ? ( -
+
{Object.values(joined_seeds) .sort(sort_joined_seeds) .map((joined_seed_info: IUserJoinedSeedDetail) => { @@ -2409,37 +2387,16 @@ function UserLiquidityLineStyleGroup1({ setShowRemoveBox(true); }} rounded="rounded-lg" - disabled={is_in_farming} + disabled={!!joined_seeds} px="px-0" py="py-1" style={{ minWidth: '5rem' }} className={`flex-grow w-24 text-sm text-greenColor h-8 ${ - is_in_farming ? 'opacity-40' : '' + !!joined_seeds ? 'opacity-40' : '' }`} > - {/* {is_in_farming ? ( -
- -
{ - e.stopPropagation(); - go_farm(); - }} - > - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - - -
-
- ) : null} */}
From b8921e61d15c5b5cf622a1d46a9c1a631eadebf2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 8 Jul 2023 19:32:02 +0800 Subject: [PATCH 077/204] update ui --- src/components/d3Chart/DclChart.tsx | 2 +- src/components/swap/SwapLimitOrderChart.tsx | 6 +++--- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index fc0f06d13..a7db59820 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1373,7 +1373,7 @@ export default function DclChart({ {/* hover到柱子(bin)上的悬浮框 */}
- Fee APR (24h) + Trailing 24hr APR {binDetail?.feeApr} diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 2ce44b380..01645217d 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -403,11 +403,11 @@ export default function SwapLimitOrderChart() {
Price - {cur_pairs} + {cur_pairs}
Qty - + {cur_token_symbol}
@@ -415,7 +415,7 @@ export default function SwapLimitOrderChart() { Total Qty - + {cur_token_symbol}
From c6cdfd33649e24fe47607cdfe91fdeeb720fd59a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 8 Jul 2023 21:12:05 +0800 Subject: [PATCH 078/204] update color --- tailwind.config.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/tailwind.config.js b/tailwind.config.js index 984dbf810..df862ffcf 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -351,7 +351,7 @@ module.exports = { positionLineHoverBgColor: '#0B1922', chartTabBgColor: '#172534', toolTipBoxBorderColor: '#293844', - toolTipBoxBgColor: 'rgba(29, 41, 50, 0.8)', + toolTipBoxBgColor: 'rgba(29, 41, 50, 1)', dclFarmGreenColor: '#4DCB70', dclFarmBlueColor: '#4D9FFF', dclFarmYellowColor: '#FFC56D', From 2e17f681b3b2890ba0ef4270341aa5ad5b1d1479 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 9 Jul 2023 11:42:08 +0800 Subject: [PATCH 079/204] update --- src/components/pool/YourLiquidityV2.tsx | 2 +- src/components/pool/utils.ts | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index d35c0c89c..d9f0eb9b2 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1770,7 +1770,7 @@ function UserLiquidityLineStyleGroup1({ liquidities: [l.liquidity], }; } else { - joined_seeds[targetSeed.seed_id].liquidities = [l.liquidity]; + joined_seeds[targetSeed.seed_id].liquidities.push(l.liquidity); } } }); diff --git a/src/components/pool/utils.ts b/src/components/pool/utils.ts index c0fe8e242..63621b50d 100644 --- a/src/components/pool/utils.ts +++ b/src/components/pool/utils.ts @@ -25,7 +25,7 @@ export const formatPercentage = (v: string | number) => { } else if (big.lt(0.01)) { return '<0.01%'; } else { - return big.toFixed(2, 1) + '%'; + return big.toFixed(2, 0) + '%'; } }; export const formatNumber = (v: string | number) => { From 39c2ad60f0369dac28e4ca55154744e7c57ebae8 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 9 Jul 2023 13:17:43 +0800 Subject: [PATCH 080/204] update --- src/components/d3Chart/DclChart.tsx | 10 +++++----- src/components/pool/YourLiquidityV2.tsx | 2 +- src/components/swap/SwapLimitOrderChart.tsx | 8 +++++--- 3 files changed, 11 insertions(+), 9 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index a7db59820..84961561a 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -467,7 +467,7 @@ export default function DclChart({ function drawChart() { const data: IChartData[] = process_chart_data_for_display(); const scale = scaleAxis(); - const scaleBar = scaleAxisY(); + const scaleBar = scaleAxisY(data); // down bars draw_down_bars({ data, scale, scaleBar }); // up bars @@ -1059,16 +1059,16 @@ export default function DclChart({ .domain(range) .range([0, svgWidth - svgPaddingX * 2]); } - function scaleAxisY() { + function scaleAxisY(data: IChartData[]) { if (chartType == 'USER') { return scaleAxisY_User(); } else { - return scaleAxisY_Pool(); + return scaleAxisY_Pool(data); } } - function scaleAxisY_Pool() { + function scaleAxisY_Pool(data: IChartData[]) { const L: number[] = []; - chartDataList.forEach((o: IChartData) => { + data.forEach((o: IChartData) => { const { liquidity, order_liquidity } = o; L.push( +liquidity, diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index d9f0eb9b2..4ebd7eace 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1982,7 +1982,7 @@ function UserLiquidityLineStyleGroup1({ tokenFeeRight, intersectionRangeList, isInRange, - canClaim + canClaim, } = groupedData; function goDetailV2() { diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 01645217d..842dd0173 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -403,11 +403,13 @@ export default function SwapLimitOrderChart() {
Price - {cur_pairs} + + {cur_pairs} +
Qty - + {cur_token_symbol}
@@ -415,7 +417,7 @@ export default function SwapLimitOrderChart() { Total Qty - + {cur_token_symbol}
From cc0d38550af1a264a1ae6dfbeac80e910f74277e Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 9 Jul 2023 19:45:35 +0800 Subject: [PATCH 081/204] fix bug --- src/components/pool/YourLiquidityV2.tsx | 13 +++++-------- 1 file changed, 5 insertions(+), 8 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 4ebd7eace..9da0acbdc 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1725,9 +1725,10 @@ function UserLiquidityLineStyleGroup1({ } }); if (exist) { + const seed_apr = getSeedApr(latest_seed); set_tip_seed({ seed: latest_seed, - seed_apr: formatPercentage(getSeedApr(latest_seed)), + seed_apr: seed_apr == 0 ? '-' : formatPercentage(seed_apr), go_farm_url_link: get_go_seed_link_url(latest_seed), }); } @@ -2219,7 +2220,7 @@ function UserLiquidityLineStyleGroup1({ } = joined_seed_info; if (length == 1) { return ( -
+
{value_of_investment} in{' '} +
{value_of_investment} in{' '} { e.stopPropagation(); openUrl(go_farm_url_link); From 5d7a27f89b533156be937b5f37abc6ad2a5e70bf Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 9 Jul 2023 23:26:04 +0800 Subject: [PATCH 082/204] fix bug --- src/components/pool/YourLiquidityV2.tsx | 16 ++++------------ 1 file changed, 4 insertions(+), 12 deletions(-) diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 9da0acbdc..cfb848390 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -1738,7 +1738,6 @@ function UserLiquidityLineStyleGroup1({ const in_farming_liquidities = groupYourLiquidityList.filter((l: any) => { return l.is_in_farming; }); - debugger; in_farming_liquidities.forEach((l: any) => { const [fixRange_l, pool_id_l, left_point_l, right_point_l] = l.liquidity.mft_id.split('&'); @@ -1786,8 +1785,8 @@ function UserLiquidityLineStyleGroup1({ getTotalValueInFarmsOfLiquidities(seed, liquidities) ); if (seed.farmList[0].status == 'Ended') { - joined_seed_info.seed_status == 'ended'; - joined_seed_info.seed_status_num == 3; + joined_seed_info.seed_status = 'ended'; + joined_seed_info.seed_status_num = 3; } else { if (latest_seed.seed_id == seed.seed_id) { joined_seed_info.seed_status = 'run'; @@ -1801,13 +1800,6 @@ function UserLiquidityLineStyleGroup1({ } ); set_joined_seeds(joined_seeds); - console.log( - 'pool_id, farm_icon,tip_seed, joined_seeds', - pool_id, - farm_icon, - tip_seed, - joined_seeds - ); } }, [groupYourLiquidityList, liquidities_list, tokenPriceList, all_seeds]); @@ -2161,7 +2153,7 @@ function UserLiquidityLineStyleGroup1({
{accountAPR || '-'} {joined_seeds ? ( -
+
{Object.values(joined_seeds) .sort(sort_joined_seeds) .map((joined_seed_info: IUserJoinedSeedDetail) => { @@ -2184,7 +2176,7 @@ function UserLiquidityLineStyleGroup1({ return (
- {seed_status == 'run' ? 'new' : 'pre.'} APR + ({seed_status == 'run' ? 'new' : 'pre.'}) APR {seed_apr}
From c0c5b724b07fa66533e64736a531f5b955a0d7df Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 9 Jul 2023 23:38:15 +0800 Subject: [PATCH 083/204] update the name of a function --- src/components/d3Chart/DclChart.tsx | 6 +++--- src/services/commonV3.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 84961561a..d43f8ee0e 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -8,7 +8,7 @@ import { getPointByPrice, POINTLEFTRANGE, POINTRIGHTRANGE, - divide_liquidities_into_bins, + divide_liquidities_into_bins_user, UserLiquidityInfo, getBinPointByPoint, get_x_y_amount_by_condition, @@ -355,7 +355,7 @@ export default function DclChart({ const { token_x_metadata, token_y_metadata } = pool; const { bin: bin_final } = getConfig(); const nfts = user_liquidities.concat(newlyAddedLiquidities || []); - const list = divide_liquidities_into_bins({ + const list = divide_liquidities_into_bins_user({ liquidities: nfts, slot_number_in_a_bin: bin_final, tokenX: token_x_metadata, @@ -401,7 +401,7 @@ export default function DclChart({ return l.pool_id == pool_id; }); set_user_liquidities(nfts); - list = divide_liquidities_into_bins({ + list = divide_liquidities_into_bins_user({ liquidities: nfts, slot_number_in_a_bin: bin_final, tokenX: token_x_metadata, diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 00a18edc6..0335fc65e 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1326,9 +1326,9 @@ export function openUrl(url: string) { * @param nfts * @param slot_number_in_a_bin * step1 遍历每个nft,按照slot拆分,把每个slot的 liquidity 和 tokenx amount token y amount 放入 map集合里 - * step2 根据step1 得到的 几个,再按照bin划分,每个bin的宽度根据参数拿到,高度 根据 这个bin 里token x token y的数量 应用公式拿到 + * step2 根据step1 得到的slot,再按照bin划分,每个bin的宽度根据参数拿到,高度 根据 这个bin 里token x token y的数量 应用公式拿到 */ -export function divide_liquidities_into_bins({ +export function divide_liquidities_into_bins_user({ liquidities, slot_number_in_a_bin, tokenX, From 01b4cad9cca2db8a5dfa2f3e36e152101f8b7d5c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 10 Jul 2023 19:00:14 +0800 Subject: [PATCH 084/204] update --- src/components/d3Chart/DclChart.tsx | 131 ++++++++++++- src/services/commonV3.ts | 273 ++++++++++++++++++++++++++++ 2 files changed, 401 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index d43f8ee0e..1690cf5c5 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -2,7 +2,12 @@ import React, { useState, useRef, useEffect } from 'react'; import { FormattedMessage } from 'react-intl'; import { isMobile } from '../../utils/device'; import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; -import { get_pool, PoolInfo, list_liquidities } from '../../services/swapV3'; +import { + get_pool, + PoolInfo, + list_liquidities, + get_pool_marketdepth, +} from '../../services/swapV3'; import { getPriceByPoint, getPointByPrice, @@ -13,6 +18,7 @@ import { getBinPointByPoint, get_x_y_amount_by_condition, get_account_24_apr, + divide_liquidities_into_bins_pool, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy, debounce } from 'lodash'; @@ -43,6 +49,7 @@ import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; +import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; export default function DclChart({ pool_id, leftPoint, @@ -409,16 +416,134 @@ export default function DclChart({ poolDetail: pool, }); } else { - const result = await getDclPoolPoints( + // todo + const pointsData_apr = await getDclPoolPoints( pool_id, bin_final, point_l, point_r ); - list = result.point_data || []; + const marketdepthData = await get_pool_marketdepth(pool_id); + const { liquidities, orders } = marketdepthData; + let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); + + // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 + let range_contain_current_point: any; + liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { + const { left_point, right_point, amount_l } = l; + if (right_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['left_point'] = left_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + if (left_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['right_point'] = right_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + return true; + }); + if (range_contain_current_point) { + liquidities_array.push(range_contain_current_point); + } + const orders_array: IOrderInfoPool[] = Object.values(orders); + const pointsData_l = divide_liquidities_into_bins_pool({ + liquidities: liquidities_array, + orders: orders_array, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + console.log('pointsData_l', pointsData_l); + console.log('pointsData_apr', pointsData_apr.point_data); + list = combine_data(pointsData_apr?.point_data, pointsData_l); + // list = pointsData_apr.point_data; } return list; } + function combine_data( + pointsData_apr: IChartData[], + pointsData_l: IChartData[] + ) { + const pointsData: IChartData[] = []; + const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + const pointsData_l_map = pointsData_l?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + debugger; + if (pointsData_apr_map && pointsData_l_map) { + // 以 pointsData_apr 为base, 组合两组数据 + Object.keys(pointsData_apr_map).forEach((point_l: string) => { + const { fee, total_liquidity } = pointsData_apr_map[point_l]; + const { + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + pool_id, + point, + } = pointsData_l_map[point_l] || {}; + pointsData.push({ + fee, + total_liquidity, + pool_id, + point, + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + }); + pointsData.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + }); + } else if (pointsData_l_map) { + Object.keys(pointsData_l_map).forEach((point_l: string) => { + const { + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + point, + pool_id, + } = pointsData_l_map[point_l] || {}; + pointsData.push({ + fee: '0', + total_liquidity: '0', + pool_id, + point, + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + }); + pointsData.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + }); + } + + return pointsData; + } function getChartDataListInRange() { const point_l = get_point_by_price(price_range[0].toString()); const point_r = get_point_by_price( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 0335fc65e..b45521dc6 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -127,6 +127,16 @@ export interface UserLiquidityInfo { less_than_min_deposit?: boolean; farmList?: FarmBoost[]; } +export interface ILiquidityInfoPool { + left_point: number; + right_point: number; + amount_l: string; +} +export interface IOrderInfoPool { + point: number; + amount_x: string; + amount_y: string; +} export function useAddAndRemoveUrlHandle() { const history = useHistory(); @@ -209,6 +219,31 @@ export function getYAmount_per_point_by_Ly(L: string, point: number) { return yAmount; } +/** + * + * @param amount NonDivisible + * @param point + * @returns + */ +export function get_Lx_per_point_by_XAmount(amount: string, point: number) { + const L = Big(amount) + .mul(Math.pow(Math.sqrt(CONSTANT_D), point)) + .toFixed(); + return L; +} + +/** + * + * @param amount NonDivisible + * @param point + * @returns + */ +export function get_Ly_per_point_by_YAmount(amount: string, point: number) { + const L = Big(amount) + .div(Math.pow(Math.sqrt(CONSTANT_D), point)) + .toFixed(); + return L; +} export function drawChartData({ depthData, chartDom, @@ -1568,6 +1603,56 @@ export function get_l_amount_by_condition({ } return L; } +/** + * 根据区间和这段区间里的 order的数据,获得order在这段区间的平均高度 + * @param param0 + * @returns + */ +export function get_o_l_amount_by_condition({ + left_point, + right_point, + token_x_amount, + token_y_amount, + poolDetail, + orders, + binWidth, +}: { + left_point: number; + right_point: number; + token_x_amount: string; + token_y_amount: string; + poolDetail: PoolInfo; + orders?: IOrderInfoPool[]; + binWidth?: number; +}) { + let L; + const { current_point } = poolDetail; + // in range + if (current_point >= left_point && right_point > current_point) { + // 算出这个bin里每一个order的高度,直接加权求平均 + let L_O_temp = Big(0); + orders.forEach((order: IOrderInfoPool) => { + const { amount_x, amount_y, point } = order; + if (amount_x) { + const lx = get_Lx_per_point_by_XAmount(amount_x, point); + L_O_temp.plus(lx || 0); + } else if (amount_y) { + const ly = get_Ly_per_point_by_YAmount(amount_y, point); + L_O_temp.plus(ly || 0); + } + }); + L = L_O_temp.div(binWidth).toFixed(); + } + // only y token + if (current_point >= right_point) { + L = getLByTokenY(left_point, right_point, token_y_amount); + } + // only x token + if (left_point > current_point) { + L = getLByTokenX(left_point, right_point, token_x_amount); + } + return L; +} export function get_account_24_apr( dclAccountFee: IDCLAccountFee | any, @@ -1738,3 +1823,191 @@ export function whether_liquidity_can_farm_in_seed( (!mft_id && new BigNumber(amount).isGreaterThanOrEqualTo(1000000)); if (condition1 && condition2 && condition3) return true; } + +/** + * 中文 + * @param nfts + * @param slot_number_in_a_bin + * 获取这个池子的所有liquidity 和 order数据 + * liquidity划分成slot。 + * 找到这个slot和order的最小point 和最大point + * 根据 区间范围 划分bin + * + * + * step1 遍历每个nft,按照slot拆分,把每个slot的 liquidity 和 tokenx amount token y amount 放入 map集合里 + * step2 根据step1 得到的slot,再按照bin划分,每个bin的宽度根据参数拿到,高度 根据 这个bin 里token x token y的数量 应用公式拿到 + */ +export function divide_liquidities_into_bins_pool({ + liquidities, + orders, + slot_number_in_a_bin, + tokenX, + tokenY, + poolDetail, +}: { + liquidities: ILiquidityInfoPool[]; + orders: IOrderInfoPool[]; + slot_number_in_a_bin: number; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + poolDetail: PoolInfo; +}) { + if (!liquidities.length && !orders.length) return []; + // split data to slots + const liquidities_in_slot_unit: { [point: number]: IChartData } = {}; + const { point_delta, pool_id } = poolDetail; + liquidities.forEach((liquidity: ILiquidityInfoPool, index) => { + const { left_point, right_point, amount_l } = liquidity; + const slots_in_a_nft = (right_point - left_point) / point_delta; + for (let i = 0; i < slots_in_a_nft; i++) { + const left_point_i = left_point + i * point_delta; + const right_point_i = left_point_i + point_delta; + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point: left_point_i, + right_point: right_point_i, + amount: amount_l, + tokenX, + tokenY, + poolDetail, + }); + const { + token_x: token_x_amount, + token_y: token_y_amount, + liquidity: L, + } = liquidities_in_slot_unit[left_point_i] || {}; + liquidities_in_slot_unit[left_point_i] = { + pool_id, + point: left_point_i, + liquidity: Big(amount_l || 0) + .plus(L || 0) + .toFixed(), + token_x: Big(total_x || 0) + .plus(token_x_amount || 0) + .toFixed(), + token_y: Big(total_y || 0) + .plus(token_y_amount || 0) + .toFixed(), + }; + } + }); + // split slots to bin + const liquidities_in_bin_unit: IChartData[] = []; + const slots: IChartData[] = Object.values(liquidities_in_slot_unit); + slots.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + orders.sort((b: IOrderInfoPool, a: IOrderInfoPool) => { + return b.point - a.point; + }); + const min_point = Math.min(slots[0].point, orders[0].point); + const max_point = Math.max( + slots[slots.length - 1].point + point_delta, + orders[orders.length - 1].point + point_delta + ); + + const min_bin_point = getBinPointByPoint( + point_delta, + slot_number_in_a_bin, + min_point, + 'floor' + ); + const max_bin_point = getBinPointByPoint( + point_delta, + slot_number_in_a_bin, + max_point, + 'ceil' + ); + const binWidth = slot_number_in_a_bin * point_delta; + const bins_number = (max_bin_point - min_bin_point) / binWidth; + for (let i = 0; i < bins_number; i++) { + // search slots and orders in this bin + const bin_i_point_start = min_bin_point + i * binWidth; + const bin_i_point_end = min_bin_point + (i + 1) * binWidth; + const slots_in_bin_i = slots.filter((slot: IChartData) => { + const { point } = slot; + const point_start = point; + const point_end = point + point_delta; + return point_start >= bin_i_point_start && point_end <= bin_i_point_end; + }); + const orders_in_bin_i = orders.filter((order: IOrderInfoPool) => { + const { point } = order; + return point >= bin_i_point_start && point < bin_i_point_end; + }); + // get tokenx tokeny amount in this bin ===>liquidity + let total_x_amount_in_bin_i = Big(0); + let total_y_amount_in_bin_i = Big(0); + slots_in_bin_i.forEach((slot: IChartData) => { + const { token_x, token_y } = slot; + total_x_amount_in_bin_i = total_x_amount_in_bin_i.plus(token_x); + total_y_amount_in_bin_i = total_y_amount_in_bin_i.plus(token_y); + }); + + // get L in this bin =====>liquidity + const bin_i_L = get_l_amount_by_condition({ + left_point: bin_i_point_start, + right_point: bin_i_point_end, + token_x_amount: toNonDivisibleNumber( + tokenX.decimals, + total_x_amount_in_bin_i.toFixed() + ), + token_y_amount: toNonDivisibleNumber( + tokenY.decimals, + total_y_amount_in_bin_i.toFixed() + ), + poolDetail, + slots: slots_in_bin_i, + binWidth, + }); + + // get tokenx tokeny amount in this bin ===>order + let total_o_x_amount_in_bin_i = Big(0); + let total_o_y_amount_in_bin_i = Big(0); + orders_in_bin_i.forEach((order: IOrderInfoPool) => { + const { amount_x, amount_y } = order; + total_o_x_amount_in_bin_i = total_o_x_amount_in_bin_i.plus(amount_x); + total_o_y_amount_in_bin_i = total_o_y_amount_in_bin_i.plus(amount_y); + }); + + const o_bin_i_L = get_o_l_amount_by_condition({ + left_point: bin_i_point_start, + right_point: bin_i_point_end, + token_x_amount: total_o_x_amount_in_bin_i.toFixed(), + token_y_amount: total_o_y_amount_in_bin_i.toFixed(), + orders: orders_in_bin_i, + poolDetail, + binWidth, + }); + + liquidities_in_bin_unit.push({ + pool_id, + point: bin_i_point_start, + liquidity: bin_i_L, + token_x: total_x_amount_in_bin_i.toFixed(), + token_y: total_y_amount_in_bin_i.toFixed(), + order_x: toReadableNumber( + tokenX.decimals, + total_o_x_amount_in_bin_i.toFixed() + ), + order_y: toReadableNumber( + tokenY.decimals, + total_o_y_amount_in_bin_i.toFixed() + ), + order_liquidity: o_bin_i_L, + }); + } + // filter empty bin + const bins_final = liquidities_in_bin_unit.filter((bin: IChartData) => { + const { token_x, token_y, order_x, order_y } = bin; + return ( + Big(token_x || 0).gt(0) || + Big(token_y || 0).gt(0) || + Big(order_x || 0).gt(0) || + Big(order_y || 0).gt(0) + ); + }); + bins_final.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + // last bins + return bins_final; +} From 1bb2440f21585fb027cfe738dd010053c775d7aa Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 11 Jul 2023 16:45:56 +0800 Subject: [PATCH 085/204] update --- src/components/d3Chart/DclChart.tsx | 6 ++---- src/components/d3Chart/interfaces.tsx | 1 - 2 files changed, 2 insertions(+), 5 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 1690cf5c5..1bb482ad3 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -651,7 +651,6 @@ export default function DclChart({ const point_r = +point + point_delta * bin_final; const point_r_close = +point + point_delta * bin_final + 1; const price_r = getPriceByPoint(point_r, decimalRate_price); - const price_r_close = getPriceByPoint(point_r_close, decimalRate_price); return { ...o, @@ -666,7 +665,6 @@ export default function DclChart({ price: price_l.toString(), price_r: price_r.toString(), point_r: point_r.toString(), - price_r_close: price_r_close.toString(), }; }); return data; @@ -884,7 +882,7 @@ export default function DclChart({ .transition() .attr('width', function (d) { return ( - scale(Big(d.price_r_close).toNumber()) - + scale(Big(d.point_r).toNumber()) - scale(Big(d.price).toNumber()) ); }) @@ -1184,7 +1182,7 @@ export default function DclChart({ .domain(range) .range([0, svgWidth - svgPaddingX * 2]); } - function scaleAxisY(data: IChartData[]) { + function scaleAxisY(data?: IChartData[]) { if (chartType == 'USER') { return scaleAxisY_User(); } else { diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 30b9cdd0c..8475cc3dd 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -7,7 +7,6 @@ export interface IChartData { price?: string; price_r?: string; point_r?: string; - price_r_close?: string; order_x?: string; order_y?: string; order_liquidity?: string; From 918d75842e032e453b90b439554922580a506ee5 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 11 Jul 2023 16:50:44 +0800 Subject: [PATCH 086/204] update --- src/components/d3Chart/DclChart.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 1bb482ad3..859e4cbdf 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -882,7 +882,7 @@ export default function DclChart({ .transition() .attr('width', function (d) { return ( - scale(Big(d.point_r).toNumber()) - + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) ); }) From 07f54b48a4f0d37265453e6da7d6f153f2db01a6 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 11 Jul 2023 16:53:49 +0800 Subject: [PATCH 087/204] update --- src/components/d3Chart/DclChart.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 859e4cbdf..871126329 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -649,7 +649,6 @@ export default function DclChart({ const { point } = o; const price_l = getPriceByPoint(+point, decimalRate_price); const point_r = +point + point_delta * bin_final; - const point_r_close = +point + point_delta * bin_final + 1; const price_r = getPriceByPoint(point_r, decimalRate_price); return { From bde0a794f5ded68a16d2b7f50ff13dc8da5f09d6 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 11 Jul 2023 19:11:02 +0800 Subject: [PATCH 088/204] update --- src/components/d3Chart/DclChart.tsx | 34 ++++++++++++++++++----------- 1 file changed, 21 insertions(+), 13 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 871126329..6964af480 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -416,7 +416,6 @@ export default function DclChart({ poolDetail: pool, }); } else { - // todo const pointsData_apr = await getDclPoolPoints( pool_id, bin_final, @@ -481,11 +480,21 @@ export default function DclChart({ [cur.point]: cur, }; }, {}); - debugger; if (pointsData_apr_map && pointsData_l_map) { // 以 pointsData_apr 为base, 组合两组数据 Object.keys(pointsData_apr_map).forEach((point_l: string) => { - const { fee, total_liquidity } = pointsData_apr_map[point_l]; + const { + fee, + total_liquidity, + point: point_apr, + liquidity: liquidity_apr, + token_x: token_x_apr, + token_y: token_y_apr, + order_liquidity: order_liquidity_apr, + order_x: order_x_apr, + order_y: order_y_apr, + pool_id: pool_id_apr, + } = pointsData_apr_map[point_l]; const { liquidity, token_x, @@ -499,14 +508,14 @@ export default function DclChart({ pointsData.push({ fee, total_liquidity, - pool_id, - point, - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, + pool_id: pool_id || pool_id_apr, + point: point || point_apr, + liquidity: liquidity || liquidity_apr, + token_x: token_x || token_x_apr, + token_y: token_y || token_y_apr, + order_liquidity: order_liquidity || order_liquidity_apr, + order_x: order_x || order_x_apr, + order_y: order_y || order_y_apr, }); pointsData.sort((b: IChartData, a: IChartData) => { return b.point - a.point; @@ -881,8 +890,7 @@ export default function DclChart({ .transition() .attr('width', function (d) { return ( - scale(Big(d.price_r).toNumber()) - - scale(Big(d.price).toNumber()) + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) ); }) .attr('height', function (d) { From 66aabc362ee3f531551e9e2b595bc017d69f9c25 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 12 Jul 2023 12:08:57 +0800 Subject: [PATCH 089/204] update icon --- src/components/d3Chart/DclChart.tsx | 53 +------ src/pages/Orderly/components/Common/Icons.tsx | 132 +++++++++++++----- 2 files changed, 105 insertions(+), 80 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 6964af480..460b9ac53 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -480,48 +480,7 @@ export default function DclChart({ [cur.point]: cur, }; }, {}); - if (pointsData_apr_map && pointsData_l_map) { - // 以 pointsData_apr 为base, 组合两组数据 - Object.keys(pointsData_apr_map).forEach((point_l: string) => { - const { - fee, - total_liquidity, - point: point_apr, - liquidity: liquidity_apr, - token_x: token_x_apr, - token_y: token_y_apr, - order_liquidity: order_liquidity_apr, - order_x: order_x_apr, - order_y: order_y_apr, - pool_id: pool_id_apr, - } = pointsData_apr_map[point_l]; - const { - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - pool_id, - point, - } = pointsData_l_map[point_l] || {}; - pointsData.push({ - fee, - total_liquidity, - pool_id: pool_id || pool_id_apr, - point: point || point_apr, - liquidity: liquidity || liquidity_apr, - token_x: token_x || token_x_apr, - token_y: token_y || token_y_apr, - order_liquidity: order_liquidity || order_liquidity_apr, - order_x: order_x || order_x_apr, - order_y: order_y || order_y_apr, - }); - pointsData.sort((b: IChartData, a: IChartData) => { - return b.point - a.point; - }); - }); - } else if (pointsData_l_map) { + if (pointsData_l_map) { Object.keys(pointsData_l_map).forEach((point_l: string) => { const { liquidity, @@ -532,10 +491,13 @@ export default function DclChart({ order_y, point, pool_id, - } = pointsData_l_map[point_l] || {}; + } = pointsData_l_map[point_l]; + + const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; + pointsData.push({ - fee: '0', - total_liquidity: '0', + fee: fee || '0', + total_liquidity: total_liquidity || '0', pool_id, point, liquidity, @@ -550,7 +512,6 @@ export default function DclChart({ }); }); } - return pointsData; } function getChartDataListInRange() { diff --git a/src/pages/Orderly/components/Common/Icons.tsx b/src/pages/Orderly/components/Common/Icons.tsx index ca4d34c81..6c0062e11 100644 --- a/src/pages/Orderly/components/Common/Icons.tsx +++ b/src/pages/Orderly/components/Common/Icons.tsx @@ -1982,54 +1982,74 @@ export function CurveShape() { export function BidAskShape() { return ( + + + + @@ -2037,10 +2057,10 @@ export function BidAskShape() { @@ -2048,10 +2068,10 @@ export function BidAskShape() { @@ -2059,10 +2079,10 @@ export function BidAskShape() { @@ -2070,10 +2090,32 @@ export function BidAskShape() { + + + + + + + + @@ -2081,16 +2123,38 @@ export function BidAskShape() { + + + + + + + + ); From 5cc1054e1eff9485bd7c162379694371c449072b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 12 Jul 2023 16:40:27 +0800 Subject: [PATCH 090/204] fix bug --- src/components/d3Chart/DclChart.tsx | 19 ++++++++++++++++--- 1 file changed, 16 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 460b9ac53..08af46976 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -458,14 +458,24 @@ export default function DclChart({ }); console.log('pointsData_l', pointsData_l); console.log('pointsData_apr', pointsData_apr.point_data); - list = combine_data(pointsData_apr?.point_data, pointsData_l); + const binWidth = bin_final * point_delta; + list = combine_data( + pointsData_apr?.point_data, + pointsData_l, + point_l, + point_r, + binWidth + ); // list = pointsData_apr.point_data; } return list; } function combine_data( pointsData_apr: IChartData[], - pointsData_l: IChartData[] + pointsData_l: IChartData[], + min_point: number, + max_point: number, + binWidth: number ) { const pointsData: IChartData[] = []; const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { @@ -474,7 +484,10 @@ export default function DclChart({ [cur.point]: cur, }; }, {}); - const pointsData_l_map = pointsData_l?.reduce((acc, cur) => { + const pointsData_l_inRange = pointsData_l?.filter((d: IChartData) => { + return d.point >= min_point && d.point + binWidth <= max_point; + }); + const pointsData_l_map = pointsData_l_inRange?.reduce((acc, cur) => { return { ...acc, [cur.point]: cur, From 34133cd47662124e38319940ec10a5a84261011a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 12 Jul 2023 18:23:02 +0800 Subject: [PATCH 091/204] update --- src/components/d3Chart/DclChart.tsx | 40 ++++++++--------------------- 1 file changed, 10 insertions(+), 30 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 08af46976..24a0259f1 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -532,33 +532,12 @@ export default function DclChart({ const point_r = get_point_by_price( price_range[price_range.length - 1].toString() ); - let start_index; - for (let i = 0; i < chartDataList.length - 1; i++) { - if (+chartDataList[i].point == point_l) { - start_index = i; - break; - } - if ( - +chartDataList[i].point > point_l && - +chartDataList[Math.max(i - 1, 0)].point < point_l - ) { - start_index = Math.max(i - 1, 0); - break; - } - } - let end_index = chartDataList.findIndex((d: IChartData) => { - return +d.point >= point_r; + const bin_width = getConfig().bin * pool.point_delta; + const chartDataListInRange = chartDataList.filter((d: IChartData) => { + const { point } = d; + const point_right = point + bin_width; + return point >= point_l && point_right <= point_r; }); - if (!isValid(start_index)) { - start_index = 0; - } - if (end_index == -1) { - end_index = chartDataList.length - 1; - } - const chartDataListInRange = chartDataList.slice( - start_index, - end_index + 1 - ); return chartDataListInRange; } function get_price_range_by_percent(percent: number): [string, string] { @@ -575,7 +554,7 @@ export default function DclChart({ function drawChart() { const data: IChartData[] = process_chart_data_for_display(); const scale = scaleAxis(); - const scaleBar = scaleAxisY(data); + const scaleBar = scaleAxisY(); // down bars draw_down_bars({ data, scale, scaleBar }); // up bars @@ -1163,14 +1142,15 @@ export default function DclChart({ .domain(range) .range([0, svgWidth - svgPaddingX * 2]); } - function scaleAxisY(data?: IChartData[]) { + function scaleAxisY() { if (chartType == 'USER') { return scaleAxisY_User(); } else { - return scaleAxisY_Pool(data); + return scaleAxisY_Pool(); } } - function scaleAxisY_Pool(data: IChartData[]) { + function scaleAxisY_Pool() { + const data: IChartData[] = process_chart_data_for_display(); const L: number[] = []; data.forEach((o: IChartData) => { const { liquidity, order_liquidity } = o; From c31ba206fdfb9713ddeb6922eb9379b5eb0665d3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 13 Jul 2023 16:34:08 +0800 Subject: [PATCH 092/204] fix bug --- src/services/commonV3.ts | 19 ++++++++++++++----- 1 file changed, 14 insertions(+), 5 deletions(-) diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index b45521dc6..d3bc8214a 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1899,11 +1899,20 @@ export function divide_liquidities_into_bins_pool({ orders.sort((b: IOrderInfoPool, a: IOrderInfoPool) => { return b.point - a.point; }); - const min_point = Math.min(slots[0].point, orders[0].point); - const max_point = Math.max( - slots[slots.length - 1].point + point_delta, - orders[orders.length - 1].point + point_delta - ); + let min_point, max_point; + if (slots.length && orders.length) { + min_point = Math.min(slots[0].point, orders[0].point); + max_point = Math.max( + slots[slots.length - 1].point + point_delta, + orders[orders.length - 1].point + point_delta + ); + } else if (slots.length) { + min_point = slots[0].point; + max_point = slots[slots.length - 1].point + point_delta; + } else if (orders.length) { + min_point = orders[0].point; + max_point = orders[orders.length - 1].point + point_delta; + } const min_bin_point = getBinPointByPoint( point_delta, From c2cd902d78161740dc6638fc5bc0b989705aa4e9 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 01:22:21 +0800 Subject: [PATCH 093/204] update --- src/components/d3Chart/DclChart.tsx | 25 ++++++----- src/components/pool/YourLiquidityV2.tsx | 18 ++++++-- src/pages/poolsV3/PoolDetailV3.tsx | 48 ++++++++++----------- src/services/commonV3.ts | 56 +++++++++++++++++++++++-- 4 files changed, 106 insertions(+), 41 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 24a0259f1..afb1f952f 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -19,6 +19,7 @@ import { get_x_y_amount_by_condition, get_account_24_apr, divide_liquidities_into_bins_pool, + get_token_amount_in_user_liquidities, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy, debounce } from 'lodash'; @@ -325,21 +326,23 @@ export default function DclChart({ ); } user_liquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point, amount } = l; + const { left_point, right_point } = l; points.push(left_point, right_point); - const { total_x, total_y } = get_x_y_amount_by_condition({ - left_point, - right_point, - amount, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - total_x_amount = total_x_amount.plus(total_x); - total_y_amount = total_y_amount.plus(total_y); }); + const [total_token_x_amount, total_token_y_amount] = + get_token_amount_in_user_liquidities({ + user_liquidities, + pool, + token_x_metadata, + token_y_metadata, + }); + + total_x_amount = total_x_amount.plus(total_token_x_amount); + total_y_amount = total_y_amount.plus(total_token_y_amount); + const total_x_value = Big(price_x).mul(total_x_amount); const total_y_value = Big(price_y).mul(total_y_amount); + total_value = total_x_value.plus(total_y_value); points.sort((b: number, a: number) => { return b - a; diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index cfb848390..2fe1f1c78 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -48,6 +48,7 @@ import { whether_liquidity_can_farm_in_seed, get_valid_range, get_total_value_by_liquidity_amount_dcl, + get_token_amount_in_user_liquidities, } from '../../services/commonV3'; import BigNumber from 'bignumber.js'; import { @@ -1857,9 +1858,20 @@ function UserLiquidityLineStyleGroup1({ } const groupList = () => { - const your_liquidity = groupYourLiquidityList.reduce((prev, cur) => { - return new Big(prev || '0').plus(new Big(cur.your_liquidity || '0')); - }, new Big(0)); + const [total_x, total_y] = get_token_amount_in_user_liquidities({ + user_liquidities: liquidities_list, + pool: poolDetail, + token_x_metadata: tokenMetadata_x_y[0], + token_y_metadata: tokenMetadata_x_y[1], + }); + + const price_x = tokenPriceList?.[tokenMetadata_x_y[0].id]?.price || 0; + const price_y = tokenPriceList?.[tokenMetadata_x_y[1].id]?.price || 0; + + const total_x_value = Big(price_x).mul(total_x); + const total_y_value = Big(price_y).mul(total_y); + + const your_liquidity = total_x_value.plus(total_y_value); const tokenFeeLeft = groupYourLiquidityList.reduce((prev, cur) => { return new Big(prev || '0').plus(new Big(cur.tokenFeeLeft || '0')); diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 259352d51..761f3f242 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -51,6 +51,7 @@ import { get_pool_name, openUrl, get_account_24_apr, + get_token_amount_in_user_liquidities, } from '~services/commonV3'; import { ftGetTokensMetadata } from '../../services/ft-contract'; import { @@ -826,42 +827,41 @@ function YourLiquidityBox(props: { return { amountx: amountX_read, amounty: amountY_read }; } function getTotalLiquditiesTvl() { - let total = 0; - user_liquidities_detail.forEach((liquidityDetail: UserLiquidityDetail) => { - const { total_liqudities_price } = liquidityDetail; - total += +total_liqudities_price; + const [total_x, total_y] = get_tokens_amount_liquidities(); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_x_value = Big(price_x).mul(total_x); + const total_y_value = Big(price_y).mul(total_y); + const total_value = total_x_value.plus(total_y_value).toFixed(); + const total_value_display = formatWithCommas_usd(total_value); + return total_value_display; + } + function get_tokens_amount_liquidities() { + const [total_x, total_y] = get_token_amount_in_user_liquidities({ + user_liquidities: liquidities, + pool: poolDetail, + token_x_metadata, + token_y_metadata, }); - if (total == 0) { - return '$0'; - } else if (total < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total.toString(), 2)); - } + return [total_x, total_y]; } function getTotalTokenAmount() { - let total_x = 0; - let total_y = 0; - user_liquidities_detail.forEach((liquidityDetail: UserLiquidityDetail) => { - const { amount_x, amount_y } = liquidityDetail; - total_x += +amount_x; - total_y += +amount_y; - }); + const [total_x, total_y] = get_tokens_amount_liquidities(); let display_total_x = '0'; let display_total_y = '0'; - if (total_x == 0) { + if (+total_x == 0) { display_total_x = '0'; - } else if (total_x < 0.01) { + } else if (+total_x < 0.01) { display_total_x = '<0.01'; } else { - display_total_x = toInternationalCurrencySystem(total_x.toString(), 3); + display_total_x = toInternationalCurrencySystem(total_x, 3); } - if (total_y == 0) { + if (+total_y == 0) { display_total_y = '0'; - } else if (total_y < 0.01) { + } else if (+total_y < 0.01) { display_total_y = '<0.01'; } else { - display_total_y = toInternationalCurrencySystem(total_y.toString(), 3); + display_total_y = toInternationalCurrencySystem(total_y, 3); } return { total_x: display_total_x, diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index d3bc8214a..1e04a4092 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1376,10 +1376,10 @@ export function divide_liquidities_into_bins_user({ tokenY: TokenMetadata; poolDetail: PoolInfo; }) { - if (!liquidities.length) return []; + if (!liquidities?.length) return []; // split data to slots const liquidities_in_slot_unit: { [point: number]: IChartData } = {}; - const { point_delta, pool_id } = poolDetail; + const { point_delta, pool_id, current_point } = poolDetail; liquidities.forEach((liquidity: UserLiquidityInfo) => { const { left_point, right_point, amount } = liquidity; const slots_in_a_nft = (right_point - left_point) / point_delta; @@ -1414,6 +1414,27 @@ export function divide_liquidities_into_bins_user({ }; } }); + // 如果有包含当前点位的slot,这个slot里面的 token_x token_y 累计的amount,重新计算 token的数量 + const contain_current_point = getBinPointByPoint( + point_delta, + 1, + current_point, + 'floor' + ); + const contain_current_point_slot = + liquidities_in_slot_unit[contain_current_point]; + if (contain_current_point_slot) { + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point: contain_current_point, + right_point: contain_current_point + point_delta, + amount: contain_current_point_slot.liquidity, + tokenX, + tokenY, + poolDetail, + }); + liquidities_in_slot_unit[contain_current_point]['token_x'] = total_x; + liquidities_in_slot_unit[contain_current_point]['token_y'] = total_y; + } // split slots to bin const liquidities_in_bin_unit: IChartData[] = []; const slots: IChartData[] = Object.values(liquidities_in_slot_unit); @@ -1852,7 +1873,7 @@ export function divide_liquidities_into_bins_pool({ tokenY: TokenMetadata; poolDetail: PoolInfo; }) { - if (!liquidities.length && !orders.length) return []; + if (!liquidities?.length && !orders?.length) return []; // split data to slots const liquidities_in_slot_unit: { [point: number]: IChartData } = {}; const { point_delta, pool_id } = poolDetail; @@ -2020,3 +2041,32 @@ export function divide_liquidities_into_bins_pool({ // last bins return bins_final; } + +export function get_token_amount_in_user_liquidities({ + user_liquidities, + pool, + token_x_metadata, + token_y_metadata, +}: { + user_liquidities: UserLiquidityInfo[]; + pool: PoolInfo; + token_x_metadata: TokenMetadata; + token_y_metadata: TokenMetadata; +}) { + let total_x_amount = Big(0); + let total_y_amount = Big(0); + const list = divide_liquidities_into_bins_user({ + liquidities: user_liquidities, + slot_number_in_a_bin: 1, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + + list.forEach((l: IChartData) => { + const { token_x, token_y } = l; + total_x_amount = total_x_amount.plus(token_x); + total_y_amount = total_y_amount.plus(token_y); + }); + return [total_x_amount.toFixed(), total_y_amount.toFixed()]; +} From 484ddeb60c9a248a12c79bfaf10765d8b1b4bf42 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 09:38:13 +0800 Subject: [PATCH 094/204] update --- src/services/commonV3.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 1e04a4092..c38679eaf 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1705,7 +1705,6 @@ export function get_account_24_apr( // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; - // const current_time = Big(new Date().getTime()).div(1000).toFixed(0); // 秒 const second24 = 24 * 60 * 60; const { token_x, token_y, timestamp: current_time } = user_token; const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 @@ -1731,7 +1730,9 @@ export function get_account_24_apr( processed_change_log.push(current_processed); if (change_log_data?.length) { - change_log_data.reverse(); + change_log_data.sort((b: IDclLogData, a: IDclLogData) => { + return Big(a.timestamp).minus(b.timestamp).toNumber(); + }); change_log_data.forEach((log: IDclLogData) => { const pre_processed_log = processed_change_log[processed_change_log.length - 1]; From 8e63de7e0d77a059cf3011d25c8d950f0d37b464 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 09:59:27 +0800 Subject: [PATCH 095/204] add log --- src/services/commonV3.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index c38679eaf..9958f629a 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1768,6 +1768,7 @@ export function get_account_24_apr( } }); } + console.log('processed_change_log', processed_change_log); // 24小时apr let total_processed_log_value = Big(0); processed_change_log.forEach((log: IProcessedLogData) => { From 041c4372617589bb785e7ce9bb52fee460913147 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 10:20:56 +0800 Subject: [PATCH 096/204] add log --- src/services/commonV3.ts | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 9958f629a..8c6f35a66 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1768,7 +1768,6 @@ export function get_account_24_apr( } }); } - console.log('processed_change_log', processed_change_log); // 24小时apr let total_processed_log_value = Big(0); processed_change_log.forEach((log: IProcessedLogData) => { @@ -1778,6 +1777,8 @@ export function get_account_24_apr( ); }); const principal = total_processed_log_value.div(second24); + console.log('processed_change_log', processed_change_log); + console.log('processed_change_log-principal', principal.toFixed()); if (principal.gt(0)) { apr_24 = total_fee_24_value.div(principal).mul(365).mul(100).toFixed(); } From e8defc4e658de878338aa7fb99757129c8670314 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 17:51:59 +0800 Subject: [PATCH 097/204] add reverse logic --- src/components/d3Chart/DclChart.tsx | 1735 +++++++++- src/components/d3Chart/interfaces.tsx | 27 +- src/components/pool/RemovePoolV3.tsx | 848 ++++- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 3153 ++++++++++++++++-- src/services/commonV3.ts | 4 + 5 files changed, 5378 insertions(+), 389 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index afb1f952f..d632061f9 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -20,6 +20,7 @@ import { get_account_24_apr, divide_liquidities_into_bins_pool, get_token_amount_in_user_liquidities, + reverse_price, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy, debounce } from 'lodash'; @@ -32,6 +33,7 @@ import { IUserLiquiditiesDetail, IDCLAccountFee, IRMTYPE, + IDclChartProps } from './interfaces'; import { formatPrice, @@ -51,7 +53,14 @@ import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; -export default function DclChart({ +export default function DclChart(props:IDclChartProps) { + if (props.reverse) { + return + } else { + return + } +} +function DclChartForward({ pool_id, leftPoint, rightPoint, @@ -61,22 +70,9 @@ export default function DclChart({ chartType, removeParams, newlyAddedLiquidities, -}: { - pool_id: string; - leftPoint?: number; - rightPoint?: number; - setLeftPoint?: Function; - setRightPoint?: Function; - config?: IPoolChartConfig; - chartType?: 'POOL' | 'USER'; - removeParams?: { - fromLeft?: boolean; - fromRight?: boolean; - point?: number; - all?: boolean; - }; - newlyAddedLiquidities?: UserLiquidityInfo[]; -}) { +}:IDclChartProps) { + + const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); const [chartDataList, setChartDataList] = useState(); @@ -628,7 +624,7 @@ export default function DclChart({ fee: Big(o.fee || 0).toFixed(), price: price_l.toString(), price_r: price_r.toString(), - point_r: point_r.toString(), + point_r: point_r, }; }); return data; @@ -1243,10 +1239,6 @@ export default function DclChart({ } return '-'; } - function reverse_price(price: string) { - if (Big(price).eq(0)) return 0; - return Big(1).div(price).toFixed(); - } function zoomOut() { const { rangeGear } = getConfig(); const targetPercent = rangeGear.find((item) => item < zoom); @@ -1635,84 +1627,1633 @@ export default function DclChart({
); } -function isValid(n: number) { - if (n !== undefined && n !== null) return true; - return false; -} -function LeftArrowIcon(props: any) { - return ( - - - +// todo +function DclChartReverse({ + pool_id, + leftPoint, + rightPoint, + setLeftPoint, + setRightPoint, + config, + chartType, + removeParams, + newlyAddedLiquidities +}: { + pool_id: string; + leftPoint?: number; + rightPoint?: number; + setLeftPoint?: Function; + setRightPoint?: Function; + config?: IPoolChartConfig; + chartType?: 'POOL' | 'USER'; + removeParams?: { + fromLeft?: boolean; + fromRight?: boolean; + point?: number; + all?: boolean; + }; + newlyAddedLiquidities?: UserLiquidityInfo[] +}) { + const [pool, setPool] = useState(); + const [price_range, set_price_range] = useState(); + const [chartDataList, setChartDataList] = useState(); + const [binDetail, setBinDetail] = useState(); + const [dragLeftPoint, setDragLeftPoint] = useState(); + const [dragRightPoint, setDragRightPoint] = useState(); + const [zoom, setZoom] = useState(); + const [randomId, setRandomId] = useState('.' + createRandomString()); + const [drawChartDone, setDrawChartDone] = useState(false); + const [user_liquidities, set_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + const [user_liquidities_detail, set_user_liquidities_detail] = + useState(); + const [tokenPriceList, setTokenPriceList] = useState>(); + /** constant start */ + const appearanceConfig: IPoolChartConfig = config || {}; + let [timerObj, setTimerObj] = useState({ + timer: '', + }); + const dragBarWidth = 28; + const percentBoxWidth = 44; + const min_bar_height = 2; + const svgWidth = +(appearanceConfig.svgWidth || 520); + const svgHeight = +(appearanceConfig.svgHeight || 250); + const axisHeight = appearanceConfig.axisHidden + ? appearanceConfig.controlHidden + ? 0 + : 5 + : 21; + const wholeBarHeight = svgHeight - axisHeight; + const disFromHoverBoxToPointer = +( + appearanceConfig.disFromPercentBoxToDragBar || 20 ); -} -function RightArrowIcon(props: any) { - return ( - - - + const disFromPercentBoxToDragBar = +( + appearanceConfig.disFromPercentBoxToDragBar || 2 ); -} - -function AddIcon(props: any) { - return ( - - - - + const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); + const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 + const whole_bars_background_padding = +( + appearanceConfig.whole_bars_background_padding || 20 ); -} + /** constant end */ + const { accountId } = useWalletSelector(); + useEffect(() => { + // get all token prices + getBoostTokenPrices().then((result) => { + setTokenPriceList(result); + }); + }, []); + // init + useEffect(() => { + clearTimeout(timerObj.timer); + if (pool_id) { + timerObj.timer = setTimeout(() => { + get_pool_detail(pool_id); + }, 500); + } + }, [pool_id]); + // init 从后端获取数据 + useEffect(() => { // =========>ok + if (pool) { + get_chart_data(); + } + }, [pool, accountId]); + // 更新用户数据 + useEffect(() => { // ==========>todo 等会看 + if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { + const new_list = get_latest_user_chart_data(); + setChartDataList(new_list); + } + }, [newlyAddedLiquidities, user_liquidities]); + // 绘制图表 + useEffect(() => { // =========>ok + if ( + (chartType !== 'USER' && price_range && chartDataList) || + (chartType == 'USER' && chartDataList?.length) + ) { + drawChart(); + setDrawChartDone(true); + } + }, [price_range, chartDataList]); + useEffect(() => { // =========> ok + if ( + isValid(dragLeftPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragLeftPoint; + const newPrice = reverse_price(get_price_by_point(newPoint)); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - + (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const W = rightX - x - dragBarWidth / 2; + d3.select(`${randomId} .overlap rect`) + .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) + .attr('width', W); + } + }, [dragLeftPoint, price_range, drawChartDone]); + useEffect(() => { + if (isValid(leftPoint)) { + setDragLeftPoint(leftPoint); + } + }, [leftPoint]); + useEffect(() => { // =========> ok + if ( + isValid(dragRightPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragRightPoint; + const newPrice = reverse_price(get_price_by_point(newPoint)); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr( + 'transform', + `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); -function SubIcon(props: any) { - return ( - { + if (isValid(rightPoint)) { + setDragRightPoint(rightPoint); + } + }, [rightPoint]); + useEffect(() => { // ========> todo 待看 + if (config?.radiusMode && config?.targetPoint && drawChartDone) { + // hide drag bar and show target price bar + draw_radius_mode_bar(); + d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); + d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); + } else { + d3.select(`${randomId} .leftBar`).attr('style', ''); + d3.select(`${randomId} .rightBar`).attr('style', ''); + d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); + } + }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); + /** + * 中文 + * remove 流动性,当参数改变,重新绘制删除区域的北京框 + */ + useEffect(() => { // =======> ok + if (removeParams && drawChartDone) { + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + draw_background_bars_for_select_area({ scale, scaleBar }); + } + }, [ + removeParams?.all, + removeParams?.fromLeft, + removeParams?.fromRight, + removeParams?.point, + drawChartDone, + ]); + /** + * 获取个人流动性图表详情信息 + * 用户 hover 框里数据展示 + */ + useEffect(() => { // =======> ok + if ( + user_liquidities.length && + pool && + chartType == 'USER' && + tokenPriceList + ) { + get_user_liquidities_detail(); + } + }, [user_liquidities, pool, chartType, tokenPriceList]); + async function get_user_liquidities_detail() { + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + + const points: number[] = []; + let total_x_amount = Big(0); + let total_y_amount = Big(0); + let total_value = Big(0); + let total_fee_earned = Big(0); + let apr_24 = ''; + if (dcl_fee_result) { + const dclAccountFee: IDCLAccountFee = dcl_fee_result; + const { total_earned_fee } = dclAccountFee; + // 总共赚到的fee + const { total_fee_x, total_fee_y } = total_earned_fee || {}; + const total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + const total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value.plus( + total_earned_fee_y_value + ); + // 24h 利润 + apr_24 = formatPercentage( + get_account_24_apr(dcl_fee_result, pool, tokenPriceList) + ); + } + user_liquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point, amount } = l; + points.push(left_point, right_point); + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point, + right_point, + amount, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + total_x_amount = total_x_amount.plus(total_x); + total_y_amount = total_y_amount.plus(total_y); + }); + const total_x_value = Big(price_x).mul(total_x_amount); + const total_y_value = Big(price_y).mul(total_y_amount); + total_value = total_x_value.plus(total_y_value); + points.sort((b: number, a: number) => { + return b - a; + }); + const min_point = points[0]; + const max_point = points[points.length - 1]; + const min_price = reverse_price(get_price_by_point(max_point)); + const max_price = reverse_price(get_price_by_point(min_point)); + set_user_liquidities_detail({ + total_value: formatWithCommas_usd(total_value.toFixed()), + min_price: formatPrice(min_price), + max_price: formatPrice(max_price), + total_x_amount: formatNumber(total_x_amount.toFixed()), + total_y_amount: formatNumber(total_y_amount.toFixed()), + apr_24, + total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), + }); + } + function get_latest_user_chart_data() { + const { token_x_metadata, token_y_metadata } = pool; + const { bin: bin_final } = getConfig(); + const nfts = user_liquidities.concat(newlyAddedLiquidities || []); + const list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + return list; + } + /** + * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 + */ + async function get_pool_detail(pool_id: string) { + const p: PoolInfo = await get_pool(pool_id); + const { token_x, token_y } = p; + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + setPool(p); + } + async function get_chart_data() { + const { range } = getConfig(); + const list = await get_data_from_back_end(); + if (chartType !== 'USER') { + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + } + setChartDataList(list); + } + async function get_data_from_back_end() { + const {token_x_metadata, token_y_metadata, pool_id } = pool; + const { bin: bin_final, rangeGear } = getConfig(); + const [price_l, price_r] = get_price_range_by_percent(rangeGear[0], true); + const point_l = get_point_by_price(price_l); + const point_r = get_point_by_price(price_r); + let list = []; + if (chartType == 'USER' && accountId) { + const liquidities = await list_liquidities(); + const nfts = liquidities.filter((l: UserLiquidityInfo) => { + return l.pool_id == pool_id; + }); + set_user_liquidities(nfts); + list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + } else { + const pointsData_apr = await getDclPoolPoints( + pool_id, + bin_final, + point_l, + point_r + ); + const marketdepthData = await get_pool_marketdepth(pool_id); + const { liquidities, orders } = marketdepthData; + let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); + + // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 + let range_contain_current_point: any; + liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { + const { left_point, right_point, amount_l } = l; + if (right_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['left_point'] = left_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + if (left_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['right_point'] = right_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + return true; + }); + if (range_contain_current_point) { + liquidities_array.push(range_contain_current_point); + } + const orders_array: IOrderInfoPool[] = Object.values(orders); + const pointsData_l = divide_liquidities_into_bins_pool({ + liquidities: liquidities_array, + orders: orders_array, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + list = combine_data( + pointsData_apr?.point_data, + pointsData_l + ); + } + return list; + } + function combine_data( + pointsData_apr: IChartData[], + pointsData_l: IChartData[] + ) { + const pointsData: IChartData[] = []; + const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + const pointsData_l_map = pointsData_l?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + if (pointsData_l_map) { + Object.keys(pointsData_l_map).forEach((point_l: string) => { + const { + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + point, + pool_id, + } = pointsData_l_map[point_l]; + + const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; + + pointsData.push({ + fee: fee || '0', + total_liquidity: total_liquidity || '0', + pool_id, + point, + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + }); + pointsData.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + }); + } + return pointsData; + } + function getChartDataListInRange() { + const price_range_min = price_range[0].toString(); + const price_range_max = price_range[price_range.length - 1].toString(); + const price_range_point_max = get_point_by_price(reverse_price(price_range_min)); + const price_range_point_min = get_point_by_price(reverse_price(price_range_max)); + const bin_width = getConfig().bin * pool.point_delta; + const chartDataListInRange = chartDataList.filter((d: IChartData) => { + const { point } = d; + const point_right = point + bin_width; + return point >= price_range_point_min && point_right <= price_range_point_max; + }); + return chartDataListInRange; + } + function get_price_range_by_percent(percent: number, forward?:boolean): [string, string] { + const p_l_r = percent / 100; + const price = get_current_price(forward); + const price_l_temp = Big(1 - p_l_r).mul(price); + const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); + const price_r = Big(1 + p_l_r) + .mul(price) + .toFixed(); + + return [price_l, price_r]; + } + function drawChart() { + const data: IChartData[] = process_back_end_data_in_range(); // ======>ok + const scale = scaleAxis(); // ======== ok + const scaleBar = scaleAxisY(); // ====== ok + // down bars + draw_down_bars({ data, scale, scaleBar }); // ======== ok + // up bars + if (chartType !== 'USER') { + draw_up_bars({ data, scale, scaleBar }); // ========ok + } + // 创建横坐标轴 + if (appearanceConfig.axisHidden) { + d3.select(`${randomId} .axis`).remove(); + } else { + draw_axis({ scale }); + } + // background bars + if (appearanceConfig.hoverBoxHidden) { + d3.select(`${randomId} .bars_background`).remove(); + d3.select(`${randomId} .overBox`).remove(); + d3.select(`${randomId} .whole_bars_background`).remove(); + d3.select(`${randomId} .wholeOverBox`).remove(); + } else { + draw_background_bars({ data, scale, scaleBar }); // ======= ok + } + // remove select area + if (chartType == 'USER' && removeParams) { + draw_background_bars_for_select_area({ scale, scaleBar }); // ======== ok + } else { + d3.select('.remove_bars_background').remove(); + } + + // current line + if (appearanceConfig.currentBarHidden) { + d3.select(`${randomId} .currentLine`).remove(); + } else { + draw_current_bar({ scale }); // ======== ok + } + if (appearanceConfig.controlHidden) { + remove_control(); + } else { + // init + // drag left + draw_drag_left({ scale }); // ======== ok + // drag right + draw_drag_right({ scale }); // ======= ok + } + } + function process_back_end_data_in_range() { + const { bin: bin_final } = getConfig(); + const { point_delta } = pool; + const list = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const data: IChartData[] = list.map((o: IChartData) => { + const { point } = o; + const price_r = reverse_price(get_price_by_point(+point)); + const point_max = +point + point_delta * bin_final; + const price_l = reverse_price(get_price_by_point(point_max)); + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + + price_l, + price_r, + point_l: point_max, + point_r: point, + }; + }); + return data; + } + function hoverBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = + d; + const { colors } = getConfig(); + + const total_token_x = Big(token_x).plus(order_x); + const total_token_y = Big(token_y).plus(order_y); + const price_by_token_x = get_price_by_point(+point); + const price_by_token_y = reverse_price(price_by_token_x); + const apr = Big(total_liquidity).gt(0) + ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() + : '0'; + setBinDetail({ + feeApr: formatPercentage(apr), + colors, + ...(total_token_x.gt(0) + ? { + token_x_amount: formatWithCommas_number(total_token_x.toFixed()), + token_x_amount_in_liquidity: formatWithCommas_number(token_x), + token_x_amount_in_order: formatWithCommas_number(order_x), + } + : {}), + ...(total_token_y.gt(0) + ? { + token_y_amount: formatWithCommas_number(total_token_y.toFixed()), + token_y_amount_in_liquidity: formatWithCommas_number(token_y), + token_y_amount_in_order: formatWithCommas_number(order_y), + } + : {}), + price_by_token_x: formatPrice(price_by_token_x), + price_by_token_y: formatPrice(price_by_token_y), + }); + } + function LeaveBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function hoverUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function LeaveUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function remove_control() { + d3.select(`${randomId} .control`).remove(); + d3.select(`${randomId} .overlap`).remove(); + d3.select(`${randomId} .rightBar`).remove(); + d3.select(`${randomId} .leftBar`).remove(); + } + function draw_axis({ scale }: { scale: any }) { + const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); + if (appearanceConfig.ticks || chartType == 'USER') { + axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { + const dBig = new Big(d); + if (dBig.gte(10000)) { + return dBig.toFixed(0); + } else { + return d; + } + }); + } + d3.select(`${randomId} svg .axis`) + .call(axis) + .selectAll('text') + .attr('fill', '#7E8A93'); + d3.select(`${randomId} svg .axis`) + .attr('transform', `translate(0, ${svgHeight - axisHeight})`) + .select('.domain') + .attr('stroke', 'transparent'); + } + function draw_down_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { current_point } = pool; + const { colors } = getConfig(); + d3.select(`${randomId} .bars_liquidity`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }); + } + function draw_up_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { colors } = getConfig(); + const { current_point } = pool; + d3.select(`${randomId} .bars_order`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.order_liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return ( + wholeBarHeight - + get_final_bar_height(scaleBar(+d.liquidity)) - + get_final_bar_height(scaleBar(+d.order_liquidity)) + ); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }) + .attr('opacity', '0.7'); + } + function get_final_bar_height(h: number) { + if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) + return min_bar_height; + return h; + } + function draw_background_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + if (chartType == 'USER') { + draw_background_bars_user({ + scale, + scaleBar, + }); + } else { + draw_background_bars_pool({ + data, + scale, + }); + } + } + function draw_background_bars_pool({ + data, + scale, + }: { + data: IChartData[]; + scale: Function; + }) { + d3.select(`${randomId} .bars_background`) + .selectAll('rect') + .data(data) + .join('rect') + .on('mousemove', function (e, d) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverBox(e, d); + }) + .on('mouseleave', function (e, d) { + d3.select(this).attr('fill', 'transparent'); + LeaveBox(e, d); + }) + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - + scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return wholeBarHeight; + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return 0; + }) + .attr('rx', 2) + .attr('fill', 'transparent'); + } + function draw_background_bars_user({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + d3.select(`${randomId} .whole_bars_background`) + .on('mousemove', function (e) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverUserBox(e); + }) + .on('mouseleave', function (e) { + d3.select(this).attr('fill', 'transparent'); + LeaveUserBox(e); + }) + .transition() + .attr('width', function () { + return ( + scale(sortP[sortP.length - 1]) - + scale(sortP[0]) + + whole_bars_background_padding * 2 + ); + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + return scale(sortP[0]) - whole_bars_background_padding; + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'transparent'); + } + function draw_background_bars_for_select_area({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + const min_bin_price = sortP[0]; + const max_bin_price = sortP[sortP.length - 1]; + const { fromLeft, fromRight, all, point } = removeParams; + const remove_to_price = reverse_price(get_price_by_point(point)); + + d3.select(`${randomId} .remove_bars_background`) + .attr('width', function () { + if (all) { + return scale(max_bin_price) - scale(min_bin_price); + } else if (fromLeft) { + return scale(remove_to_price) - scale(min_bin_price); + } else if (fromRight) { + return scale(max_bin_price) - scale(remove_to_price); + } + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + if (fromRight) { + return scale(remove_to_price); + } else { + return scale(min_bin_price); + } + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'rgba(255,255,255,0.1)'); + } + function draw_current_bar({ scale }: { scale: Function }) { + d3.select(`${randomId} .currentLine`).attr( + 'style', + `transform:translate(${ + scale(+get_current_price()) + svgPaddingX + }px, -${axisHeight}px)` + ); + } + function draw_drag_left({ scale }: { scale: any }) { + // 初始化左的位置 + const price = get_current_price(); + let price_l; + if (dragLeftPoint) { + price_l = reverse_price(get_price_by_point(dragLeftPoint)); + } else { + const price_l_temp = Big(1 - defaultPercent / 100) + .mul(price) + .toFixed(); + + const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(reverse_price(price_l_temp))); + price_l = reverse_price(get_price_by_point(newLeftPoint)); + + setDragLeftPoint(newLeftPoint); + } + const x = scale(price_l) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(`${diffPrices(price_l)}%`) + .attr('fill', 'white'); + const dragLeft = d3.drag().on('drag', function (e) { + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + if (rightX < e.x || e.x < dragBarWidth / 2) return; + let p = reverse_price(scale.invert(e.x)); + const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(p)); + setDragLeftPoint(newLeftPoint); + setLeftPoint && setLeftPoint(newLeftPoint); + }); + d3.select(`${randomId} .drag-left`).call(dragLeft); + } + function draw_drag_right({ scale }: { scale: any }) { + // 初始化右的位置 + const price = get_current_price(); + let price_r; + if (dragRightPoint) { + price_r = reverse_price(get_price_by_point(dragRightPoint)); + } else { + const price_r_temp = Big(1 + defaultPercent / 100) + .mul(price) + .toFixed(); + + const newRightPoint = get_nearby_bin_left_point( + get_point_by_price(reverse_price(price_r_temp)) + ) + price_r = reverse_price(get_price_by_point(newRightPoint)); + setDragRightPoint(newRightPoint); + } + const x = scale(price_r); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) + .select('text') + .text(`${diffPrices(price_r)}%`) + .attr('fill', 'white'); + const dragRight = d3.drag().on('drag', (e) => { + const leftX = Number( + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); + if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; + const p = scale.invert(e.x); + const newRightPoint = get_nearby_bin_left_point(get_point_by_price(reverse_price(p))); + setDragRightPoint(newRightPoint); + setRightPoint && setRightPoint(newRightPoint); + }); + d3.select(`${randomId} .drag-right`).call(dragRight); + } + function draw_radius_mode_bar() { + const scale: any = scaleAxis(); + const { targetPoint } = config; + const price = get_price_by_point(targetPoint); + const x = scale(price); + d3.select(`${randomId} .radiusBar`) + .attr('transform', `translate(${x}, -${axisHeight})`) + .attr('style', 'display:block'); + } + function get_current_price(forward?:boolean) { + const current_price = get_price_by_point(pool.current_point); + if (forward) { + return current_price; + } else { + return reverse_price(current_price); + } + } + function get_point_by_price(price: string) { + const { point_delta, token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_nearby_bin_right_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin + slots; + } + function get_nearby_bin_left_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin; + } + function clickToLeft() { + const { bin } = getConfig(); + const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); + setDragLeftPoint(newPoint_nearby_bin); + setLeftPoint && setLeftPoint(newPoint_nearby_bin); + } + function clickToRight() { + const { bin } = getConfig(); + const newPoint = dragRightPoint + pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); + setDragRightPoint(newPoint_nearby_bin); + setRightPoint && setRightPoint(newPoint_nearby_bin); + } + function get_bin_point_by_point(point: number, type?: IRMTYPE) { + const { point_delta } = pool; + const slot_num = getConfig().bin; + return getBinPointByPoint(point_delta, slot_num, point, type); + } + function scaleAxis() { + if (chartType == 'USER') { + return scaleAxis_User(); + } else { + return scaleAxis_Pool(); + } + } + function scaleAxis_User() { + const { sortP } = get_price_and_liquidity_range(); + const range = [+sortP[0], +sortP[sortP.length - 1]]; + return d3 + .scaleLinear() + .domain(range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxis_Pool() { + return d3 + .scaleLinear() + .domain(price_range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxisY() { + if (chartType == 'USER') { + return scaleAxisY_User(); + } else { + return scaleAxisY_Pool(); + } + } + function scaleAxisY_Pool() { + const data: IChartData[] = process_back_end_data_in_range(); + const L: number[] = []; + data.forEach((o: IChartData) => { + const { liquidity, order_liquidity } = o; + L.push( + +liquidity, + +order_liquidity, + Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() + ); + }); + const sortL = sortBy(L); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); + } + function scaleAxisY_User() { + const { sortY: sortL } = get_price_and_liquidity_range(); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true); + } + function get_bin_width() { + const slot_num_in_a_bin = getConfig().bin; + const { point_delta } = pool; + return point_delta * slot_num_in_a_bin; + } + function get_price_and_liquidity_range() { + const Y: number[] = []; + const X: number[] = []; + const list = process_back_end_data_in_range(); + list.forEach((o: IChartData) => { + const { liquidity, price_l, price_r } = o; + Y.push(+liquidity); + X.push(+price_l, +price_r) + }); + const sortY = sortBy(Y); + const sortX = sortBy(X); + return { sortP: sortX, sortY }; + } + function diffPrices(newPrice: string, peferencePrice?: string) { + let movePercent; + const price = peferencePrice || get_current_price(); + if (+price > +newPrice) { + movePercent = -Big(1) + .minus(Big(newPrice).div(price)) + .mul(100) + .toFixed(0, 1); + } else { + movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); + } + return movePercent; + } + function getConfig(): IChartItemConfig { + const { bin, range, colors, rangeGear } = + get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const percent_final = custom_config[pool_id]?.range || range; + const bin_final = custom_config[pool_id]?.bin || bin; + const colors_final = custom_config[pool_id]?.colors || colors; + const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; + return { + bin: bin_final, + range: percent_final, + colors: colors_final, + rangeGear: rangeGear_final, + }; + } + function get_current_price_by_token_x() { + if (pool) { + return formatPriceWithCommas(get_current_price()); + } + return '-'; + } + function get_current_price_by_token_y() { + if (pool) { + return formatPriceWithCommas(reverse_price(get_current_price())); + } + return '-'; + } + function zoomOut() { + const { rangeGear } = getConfig(); + const targetPercent = rangeGear.find((item) => item < zoom); + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + function zoomIn() { + const { rangeGear } = getConfig(); + const index = rangeGear.findIndex((item) => item == zoom); + let targetPercent; + if (index !== 0) { + targetPercent = rangeGear[index - 1]; + } + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + const rangeGear = getConfig().rangeGear; + const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; + const is_in_min_zoom = zoom == rangeGear[0]; + return ( +
+ {/* 控件按钮*/} +
+ {/*
+ +
*/} +
+ +
+
+ +
+ {/*
+ +
*/} +
+ + + + + + + + {/* 横坐标轴 */} + + {/* 拖拽线 left */} + + + + + + + + + + + + + + {/* 拖拽线 right */} + + + + + + + + + + + + + + {/* 左右坐标轴中间的重叠区域 */} + + + + {/* radius 模式下 target price 对应的柱子 */} + + + + + + + + + {/* hover到柱子(bin)上的悬浮框 */} +
+
+ Trailing 24hr APR + + {binDetail?.feeApr} + +
+
+ Price + + {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} /{' '} + {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} + +
+ {binDetail?.token_x_amount ? ( + <> +
+ + {pool?.token_x_metadata?.symbol} Amount + + + {binDetail.token_x_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_x_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_x_amount_in_order} + +
+ + ) : null} + {binDetail?.token_y_amount ? ( + <> +
+ + {pool?.token_y_metadata?.symbol} Amount + + + {binDetail.token_y_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_y_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_y_amount_in_order} + +
+ + ) : null} +
+
+
+ Your Liquidity + + {user_liquidities_detail?.total_value || '-'} + +
+
+ Price Range + + {user_liquidities_detail?.min_price} -{' '} + {user_liquidities_detail?.max_price} + +
+
+ Position + + {user_liquidities_detail?.total_x_amount}{' '} + {pool?.token_x_metadata.symbol} +{' '} + {user_liquidities_detail?.total_y_amount}{' '} + {pool?.token_y_metadata.symbol} + +
+
+ Trailing 24hr APR + + {user_liquidities_detail?.apr_24 || '-'} + +
+
+ Total Earned Fee + + {user_liquidities_detail?.total_earned_fee || '-'} + +
+
+ {/* current 价格 */} +
+
+
+
+ + {pool?.token_x_metadata?.symbol}:{' '} + + + + {pool?.token_y_metadata?.symbol} + +
+
+ + {pool?.token_y_metadata?.symbol}:{' '} + + + + {pool?.token_x_metadata?.symbol} + +
+
+
+
+ ); +} +function isValid(n: number) { + if (n !== undefined && n !== null) return true; + return false; +} +function LeftArrowIcon(props: any) { + return ( + + + + ); +} +function RightArrowIcon(props: any) { + return ( + + + + ); +} +function AddIcon(props: any) { + return ( + + + + + ); +} +function SubIcon(props: any) { + return ( + { + + return + // return + +}; +/** + * + * @param props 正向操作 + * @returns + */ +export const RemovePoolV3Forward = ({ props }: any) => { const { tokenMetadata_x_y, poolDetail, @@ -934,6 +945,841 @@ export const RemovePoolV3 = (props: any) => { ); }; +/** + * + * @param props 逆向的操作思路是:展示层价格 是反的,数据层,点位是不变的。 + * @returns + */ +export const RemovePoolV3Reverse = ({ props }: any) => { + const { + tokenMetadata_x_y, + poolDetail, + tokenPriceList, + isLegacy, + listLiquidities, + ...restProps + }: { + tokenMetadata_x_y: TokenMetadata[]; + poolDetail: PoolInfo; + tokenPriceList: any; + isLegacy?: boolean; + restProps: any; + listLiquidities: UserLiquidityInfo[]; + } = props; + const SLOT_NUMBER = get_slot_number_in_a_bin(); + const [slippageTolerance, setSlippageTolerance] = useState(0.5); + + const tokens = tokenMetadata_x_y; + const { decimals: token_x_decimals } = tokens[0]; + const { decimals: token_y_decimals } = tokens[1]; + const [removeLoading, setRemoveLoading] = useState(false); + const [removeType, setRemoveType] = useState('all'); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + + // new + const [minPoint, setMinPoint] = useState(); + const [maxPoint, setMaxPoint] = useState(); + const [minPrice, setMinPrice] = useState(''); + const [maxPrice, setMaxPrice] = useState(''); + const [maxBinAmount, setMaxBinAmount] = useState(); + + const [minBoxPrice, setMinBoxPrice] = useState(''); + const [maxBoxPrice, setMaxBoxPrice] = useState(''); + const [minBoxPoint, setMinBoxPoint] = useState(); + const [maxBoxPoint, setMaxBoxPoint] = useState(); + const [binBoxAmount, setBinBoxAmount] = useState(''); + + useEffect(() => { // =======》ok + // init + if (tokens && poolDetail && listLiquidities) { + get_user_points_range(); + } + }, [tokens, poolDetail, listLiquidities]); + useEffect(() => { // =======》ok + if (minBoxPoint && maxBoxPoint) { + const bin_amount = get_bin_amount_by_points(maxBoxPoint, minBoxPoint); + setBinBoxAmount(bin_amount); + } + }, [minBoxPoint, maxBoxPoint]); + useEffect(() => { + if (binBoxAmount !== '') { // =======》ok + handleBinAmountToAppropriateAmount(+binBoxAmount); + } + }, [binBoxAmount]); + const [ + min_received_x_amount, + min_received_y_amount, + min_received_total_value, + ] = useMemo(() => { // ========> ok + if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { + const { total_token_x_amount, total_token_y_amount, total_value } = + get_minimum_received_data(); + return [ + formatNumber(total_token_x_amount), + formatNumber(total_token_y_amount), + formatWithCommas_usd(total_value), + ]; + } + return ['0', '0', '$0']; + }, [ + tokenPriceList, + tokenMetadata_x_y, + minBoxPoint, + maxBoxPoint, + slippageTolerance, + ]); + /** + * NOTE 删除一个点的场景暂时不考虑 + * @returns + */ + function get_will_deleted_nfts() { + let whole_deleted_nfts: UserLiquidityInfo[] = []; + let broken_deleted_nfts: UserLiquidityInfo[] = []; + if (removeType == 'all') { + whole_deleted_nfts = [].concat(listLiquidities); + } else if (removeType == 'left') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (right_point > maxBoxPoint) { + if (left_point >= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } else if (removeType == 'right') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (left_point < minBoxPoint) { + if (right_point <= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } + return { + whole_deleted_nfts, + broken_deleted_nfts, + }; + } + function get_slot_number_in_a_bin() { + const pool_id = poolDetail?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + function get_user_points_range() { + const user_points: number[] = []; + listLiquidities.forEach((l: UserLiquidityInfo) => { + user_points.push(l.left_point, l.right_point); + }); + user_points.sort((b, a) => b - a); + const min_point = get_bin_point_by_point(user_points[0], 'floor'); + const max_point = get_bin_point_by_point( + user_points[user_points.length - 1], + 'ceil' + ); + const min_price = reverse_price(get_bin_price_by_point(max_point)); + const max_price = reverse_price(get_bin_price_by_point(min_point)); + const max_bin_amount = get_bin_amount_by_points(min_point, max_point); + setMinPoint(min_point); + setMaxPoint(max_point); + setMinPrice(min_price); + setMaxPrice(max_price); + setMaxBinAmount(max_bin_amount); + + setMinBoxPrice(min_price); + setMaxBoxPrice(max_price); + setMinBoxPoint(max_point); + setMaxBoxPoint(min_point); + setBinBoxAmount(max_bin_amount); + } + function reverse_price(price:string) { + if (Big(price).eq(0)) return '0'; + return Big(1).div(price).toFixed(); + } + function get_bin_amount_by_points(left_point: number, right_point: number) { + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + const bin_amount = Big(right_point - left_point) + .div(binWidth) + .toFixed(); + return bin_amount; + } + function get_bin_price_by_point(point: number) { + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(point, decimalRate); + return price; + } + function get_bin_point_by_price(price: string) { + const point_delta = poolDetail.point_delta; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return point; + } + function get_bin_point_by_point( + point: number, + type: 'round' | 'floor' | 'ceil' + ) { + const point_delta = poolDetail.point_delta; + const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); + return bin_point; + } + function handleMinBoxPriceToAppropriatePoint() { + /** + * min price <= price < max box price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(minBoxPrice || 0); + if (big_price.lt(minPrice)) { + appropriate_price = minPrice; + appropriate_point = maxPoint; + } else if (big_price.gt(maxBoxPrice)) { + appropriate_price = maxBoxPrice; + appropriate_point = maxBoxPoint; + } else { + appropriate_point = get_bin_point_by_price(reverse_price(minBoxPrice)); + appropriate_price = reverse_price(get_bin_price_by_point(appropriate_point)); + } + setMinBoxPrice(appropriate_price); + setMinBoxPoint(appropriate_point); + } + function handleMaxBoxPriceToAppropriatePoint() { + /** + * min box price <= price <= max price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(maxBoxPrice || 0); + if (big_price.lt(minBoxPrice)) { + appropriate_price = minBoxPrice; + appropriate_point = minBoxPoint; + } else if (big_price.gt(maxPrice)) { + appropriate_price = maxPrice; + appropriate_point = minPoint; + } else { + appropriate_point = get_bin_point_by_price(reverse_price(maxBoxPrice)); + appropriate_price = reverse_price(get_bin_price_by_point(appropriate_point)); + } + setMaxBoxPrice(appropriate_price); + setMaxBoxPoint(appropriate_point); + } + /** + * 左右点位改变会触发bin amount随之更改 + * bin amount 修改会改变可以修改的点位 + * 0 <= bin amount < max bin amount + */ + function handleBinAmountToAppropriateAmount(binAmount: number) { + const amount_int = binAmount || +binBoxAmount; + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + let appropriate_amount = amount_int; + if (amount_int > +maxBinAmount) { + appropriate_amount = +maxBinAmount; + } + if (removeType == 'left') { + const right_box_point = maxPoint - binWidth * appropriate_amount; + const right_box_price = reverse_price(get_bin_price_by_point(right_box_point)); + setMaxBoxPoint(right_box_point); + setMaxBoxPrice(right_box_price); + } else if (removeType == 'right') { + const left_box_point = minPoint + binWidth * appropriate_amount; + const left_box_price = reverse_price(get_bin_price_by_point(left_box_point)); + setMinBoxPoint(left_box_point); + setMinBoxPrice(left_box_price); + } + setBinBoxAmount(appropriate_amount.toString()); + } + function getY( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + function batch_remove_nfts() { + setRemoveLoading(true); + const [tokenX, tokenY] = tokenMetadata_x_y; + sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); + let batch_remove_liquidity: IRemoveLiquidityInfo[]; + let batch_update_liquidity: IBatchUpdateiquidityInfo; + let mint_liquidities: UserLiquidityInfo[] = []; + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + const { pool_id } = poolDetail; + /** + * step1 找到被截断的nft的 未截断的区间 + * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 + * step3 未截断的区间 和 token 数量作为 添加nft的参数 + */ + if (broken_deleted_nfts.length) { + const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + addLiquidityInfoList.push({ + pool_id, + left_point: new_left_point, + right_point: new_right_point, + amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), + amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), + min_amount_x: '0', + min_amount_y: '0', + }); + removeLiquidityInfos.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + + batch_update_liquidity = { + remove_liquidity_infos: removeLiquidityInfos, + add_liquidity_infos: addLiquidityInfoList, + }; + } + if (whole_deleted_nfts.length) { + const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + batchRemoveLiquidity.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + batch_remove_liquidity = batchRemoveLiquidity; + } + batch_remove_liquidity_contract({ + token_x: tokenX, + token_y: tokenY, + batch_remove_liquidity, + batch_update_liquidity, + mint_liquidities, + // widthdraw_infos, + }); + } + function get_minimum_received_data() { + /** + * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 + * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 + * step3 把上述step1, step2 得到的x,y 累加起来即可 + */ + let total_token_x_amount = Big(0); + let total_token_y_amount = Big(0); + let minimum_total_value = Big(0); + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + if (whole_deleted_nfts.length) { + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + total_token_x_amount = total_token_x_amount.plus( + min_token_x_amount || 0 + ); + total_token_y_amount = total_token_y_amount.plus( + min_token_y_amount || 0 + ); + }); + } + if (broken_deleted_nfts.length) { + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + const broken_min_token_x_amount = + Big(min_token_x_amount).minus(new_token_x_amount); + const broken_min_token_y_amount = + Big(min_token_y_amount).minus(new_token_y_amount); + if (broken_min_token_x_amount.gt(0)) { + total_token_x_amount = total_token_x_amount.plus( + broken_min_token_x_amount || 0 + ); + } + if (broken_min_token_y_amount.gt(0)) { + total_token_y_amount = total_token_y_amount.plus( + broken_min_token_y_amount || 0 + ); + } + }); + } + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + const token_x_value = total_token_x_amount.mul(priceX); + const token_y_value = total_token_y_amount.mul(priceY); + minimum_total_value = token_x_value.plus(token_y_value); + } + const rate = (100 - slippageTolerance) / 100; + return { + total_token_x_amount: total_token_x_amount.toFixed(), + total_token_y_amount: total_token_y_amount.toFixed(), + minimum_total_value: minimum_total_value.toFixed(), + total_value: minimum_total_value.div(rate).toFixed(), + }; + } + function get_un_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.min(right_point, minBoxPoint); + const intersection_r = Math.max(left_point, maxBoxPoint); + // intersection part + let un_intersection_l; + let un_intersection_r; + if (removeType == 'left') { + un_intersection_l = intersection_r; + un_intersection_r = left_point; + } else if (removeType == 'right') { + un_intersection_l = right_point; + un_intersection_r = intersection_l; + } + return [un_intersection_r, un_intersection_l]; + } + function get_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { left_point, right_point, amount: L } = liquidity; + const { current_point } = poolDetail; + let curTokenXAmount = '0'; + let curTokenYAmount = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); + curTokenYAmount = getY(left_point, current_point, L, tokenY); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + curTokenXAmount = Big(amountx || '0') + .plus(curTokenXAmount || '0') + .toFixed(); + curTokenYAmount = Big(amounty || '0') + .plus(curTokenYAmount || '0') + .toFixed(); + } + // only y token + if (current_point >= right_point) { + curTokenYAmount = getY(left_point, right_point, L, tokenY); + } + // only x token + if (left_point > current_point) { + curTokenXAmount = getX(left_point, right_point, L, tokenX); + } + return [curTokenXAmount, curTokenYAmount]; + } + function get_min_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const rate = (100 - slippageTolerance) / 100; + const [token_x_amount, token_y_amount] = + get_x_y_amount_of_liquidity(liquidity); + const min_token_x_amount = Big(token_x_amount || 0) + .mul(rate) + .toFixed(); + const min_token_y_amount = Big(token_y_amount || 0) + .mul(rate) + .toFixed(); + return [min_token_x_amount, min_token_y_amount]; + } + const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; + return ( + + + {/* Title */} +
+ + + +
+ +
+
+ {/* Symbol pairs */} +
+
+
+ + +
+ + {tokens[1]?.symbol}/{tokens[0]?.symbol} + +
+ + {min_received_total_value} + +
+
+ {maxPoint && ( + + )} +
+ {/* Removing way */} +
+
+ +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('left'); + setMinBoxPrice(minPrice); + setMinBoxPoint(maxPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('right'); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(minPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('all'); + setMinBoxPrice(minPrice); + setMinBoxPoint(maxPoint); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(minPoint); + }} + > + +
+
+
+ {/* remove slider */} + {/* binBoxAmount 控制 */} + { + setBinBoxAmount(v.toString()); + }} + value={+binBoxAmount} + min={0} + max={+maxBinAmount} + step={1} + > + {/* Set points */} +
+ {/* min price */} +
+ + + + + { + const value = e.target.value; + setMinBoxPrice(value); + }} + inputMode="decimal" + onBlur={() => { + handleMinBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'right'} + > +
+ {/* max price */} +
+ + + + { + const value = e.target.value; + setMaxBoxPrice(value); + }} + value={maxBoxPrice} + inputMode="decimal" + onBlur={() => { + handleMaxBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'left'} + > +
+ {/* bin amount */} +
+ + + + + +
+
+ {/* Slippage */} +
+ +
+ {/* Minimum received */} +
+
+ + + + +
+
+ + + + {min_received_y_amount} + +
+
+ + + + {min_received_x_amount} + +
+
+
+ {/* Button */} + {isSignedIn ? ( + + } + /> + + ) : ( +
+ +
+ )} + + + ); +}; export function IntegerInputComponent({ value, setValue, @@ -985,4 +1831,4 @@ export function IntegerInputComponent({ />
); -} +} \ No newline at end of file diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 15a4a5bee..ec9cb9bd2 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -78,6 +78,7 @@ import { getBinPointByPoint, get_l_amount_by_condition, UserLiquidityInfo, + reverse_price, } from '../../services/commonV3'; import { formatWithCommas, @@ -130,6 +131,10 @@ import { import { isInvalid } from '../../components/d3Chart/utils'; const LiquidityProviderData = createContext(null); export default function AddYourLiquidityPageV3() { + // return + return +} +function AddYourLiquidityPageV3Forward() { const [tokenX, setTokenX] = useState(null); const [tokenY, setTokenY] = useState(null); const [tokenXAmount, setTokenXAmount] = useState(''); @@ -2331,77 +2336,2260 @@ export default function AddYourLiquidityPageV3() { ); } -/** - * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 传递的时候只传实际使用值 - * @returns - */ -function AddLiquidityButton() { - const { - currentSelectedPool, - tokenX, - tokenY, - binNumber, - SLOT_NUMBER, +// todo +function AddYourLiquidityPageV3Reverse() { + const [tokenX, setTokenX] = useState(null); + const [tokenY, setTokenY] = useState(null); + const [tokenXAmount, setTokenXAmount] = useState(''); + const [tokenYAmount, setTokenYAmount] = useState(''); + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + const [currentPoint, setCurrentPoint] = useState(); + const [onlyAddYToken, setOnlyAddYToken] = useState(false); + const [onlyAddXToken, setOnlyAddXToken] = useState(false); + const [invalidRange, setInvalidRange] = useState(false); + const [currentSelectedPool, setCurrentSelectedPool] = + useState(null); + + const [listPool, setListPool] = useState([]); + const [tokenPriceList, setTokenPriceList] = useState>({}); + const [currentPools, setCurrentPools] = + useState>(null); + const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); + const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); + + const [feeBoxStatus, setFeeBoxStatus] = useState(true); + const [buttonSort, setButtonSort] = useState(false); + const [selectHover, setSelectHover] = useState(false); + const [hoverFeeBox, setHoverFeeBox] = useState(false); + + // abandon + const [seed_list, set_seed_list] = useState(); + const [related_seeds, set_related_seeds] = useState([]); + + // new + const [binNumber, setBinNumber] = useState(); + const [liquidityShape, setLiquidityShape] = useState('Spot'); + const [topPairs, setTopPairs] = useState([]); + const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); + const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); + const [token_amount_tip, set_token_amount_tip] = + useState(); + const [only_suppport_spot_shape, set_only_suppport_spot_shape] = + useState(false); + const [switch_pool_loading, set_switch_pool_loading] = + useState(true); + const [new_user_liquidities, set_new_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + + // callBack handle + useAddAndRemoveUrlHandle(); + const history = useHistory(); + const triTokenIds = useTriTokenIdsOnRef(); + const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); + const triTokens = useTriTokens(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const nearBalance = useDepositableBalance('NEAR'); + const intl = useIntl(); + const OPEN_CREATE_POOL_ENTRY = false; + + useEffect(() => { + getBoostTokenPrices().then(setTokenPriceList); + get_list_pools(); + get_seeds(); + }, []); + useEffect(() => { + if (tokenX) { + const tokenXId = tokenX.id; + if (tokenXId) { + if (isSignedIn) { + ftGetBalance(tokenXId).then((available: string) => + setTokenXBalanceFromNear( + toReadableNumber( + tokenX.decimals, + tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + if (tokenY) { + const tokenYId = tokenY.id; + if (tokenYId) { + if (isSignedIn) { + ftGetBalance(tokenYId).then((available: string) => + setTokenYBalanceFromNear( + toReadableNumber( + tokenY.decimals, + tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + }, [tokenX, tokenY, isSignedIn, nearBalance]); + useEffect(() => { + if (listPool.length > 0) { + get_init_pool(); + } + }, [listPool]); + useEffect(() => { + if (currentSelectedPool && tokenX && tokenY) { + const { fee } = currentSelectedPool; + const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); + history.replace(`#${link}`); + if (seed_list && currentSelectedPool.pool_id) { + get_optional_seeds(); + } + } + }, [currentSelectedPool, tokenX, tokenY, seed_list]); + useEffect(() => { + if (tokenX && tokenY) { + searchPools(); + } + }, [tokenX, tokenY, tokenPriceList, listPool]); + useEffect(() => { + if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { + getTopPairs(); + } + }, [listPool, tokenPriceList]); + // new + useEffect(() => { + // init + if (currentSelectedPool?.pool_id) { + const { current_point, point_delta } = currentSelectedPool; + const n = get_slot_number_in_a_bin(); + const bin_width = n * point_delta; + SET_SLOT_NUMBER(n); + SET_BIN_WIDTH(bin_width); + setCurrentPoint(current_point); + set_switch_pool_loading(false); + } + }, [currentSelectedPool]); + // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 + useEffect(() => { + set_only_suppport_spot_shape(false); + if (currentSelectedPool) { + const { point_delta } = currentSelectedPool; + if (leftPoint <= currentPoint && rightPoint > currentPoint) { + // inrange + const binWidth = SLOT_NUMBER * point_delta; + const binNumber = (rightPoint - leftPoint) / binWidth; + if (binNumber == 1) { + setLiquidityShape('Spot'); + set_only_suppport_spot_shape(true); + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount); + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount); + } + } + } + } + }, [ leftPoint, rightPoint, currentPoint, + currentSelectedPool, liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - getLiquiditySpot, - getLiquidityForCurveAndBidAskMode, - } = useContext(LiquidityProviderData); - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + ]); + useEffect(() => { + set_token_amount_tip(null); + }, [tokenXAmount, tokenYAmount, currentSelectedPool]); + async function getTopPairs() { + const listPromise = listPool.map(async (p: PoolInfo) => { + const token_x = p.token_x; + const token_y = p.token_y; - function addLiquiditySpot() { - setAddLiquidityButtonLoading(true); - const new_liquidity = getLiquiditySpot(); - add_liquidity(new_liquidity); + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged) + .toFixed(); + const tvlx = + Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * + Number(pricex); + const tvly = + Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * + Number(pricey); + + p.tvl = tvlx + tvly; + + return p; + }); + const list: PoolInfo[] = await Promise.all(listPromise); + list.sort((b: PoolInfo, a: PoolInfo) => { + return a.tvl - b.tvl; + }); + const top3 = list.slice(0, 3); + setTopPairs(top3); } - function addLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - nftList = getLiquidityForCurveAndBidAskMode(); - if (!nftList) { - setAddLiquidityButtonLoading(false); - return; - } - /** - * 计算出 nftList token x tokeny 的数量,这是需要的总数量 - * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 - * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 - */ - let last_total_needed_token_x_amount = Big(0); + if (!refTokens || !triTokens || !triTokenIds) return ; + async function get_seeds() { + const seeds = await get_all_seeds(); + set_seed_list(seeds); + } + function get_optional_seeds() { + const optional_seeds = get_matched_seeds_for_dcl_pool({ + seeds: seed_list, + pool_id: currentSelectedPool.pool_id, + }); + if (optional_seeds.length) { + set_related_seeds(optional_seeds); + } else { + set_related_seeds([]); + } + } + async function get_init_pool() { + let tokenx_id, tokeny_id, pool_fee; + const hash = decodeURIComponent(location.hash); + if (hash.indexOf('<>') > -1) { + // new link + [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); + } else { + // old link + [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); + } + if (tokenx_id && tokeny_id && pool_fee) { + const tokenx = await ftGetTokenMetadata(tokenx_id); + const tokeny = await ftGetTokenMetadata(tokeny_id); + setTokenX(tokenx); + setTokenY(tokeny); + } + } + function goYourLiquidityPage() { + if (history.length == 2) { + history.push('/yourliquidity'); + } else { + history.goBack(); + } + } + async function get_list_pools() { + const list: PoolInfo[] = await list_pools(); + if (list.length > 0) { + setListPool(list); + } + } + function searchPools() { + const hash = decodeURIComponent(location.hash); + let url_fee; + if (hash.indexOf('<>') > -1) { + url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; + } else { + url_fee = +hash.slice(1).split('|')[2]; + } + const currentPoolsMap = {}; + if (listPool.length > 0 && tokenX && tokenY) { + set_switch_pool_loading(true); + const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { + // TODO 增加pool 状态的判断 + const { token_x, token_y, state } = pool; + if ( + (token_x == tokenX.id && token_y == tokenY.id) || + (token_x == tokenY.id && token_y == tokenX.id) + ) + return true; + }); + if (availablePools.length > 0) { + /*** percent start */ + const tvlList: number[] = []; + availablePools.map((p: PoolInfo) => { + const { + total_x, + total_y, + token_x, + token_y, + total_fee_x_charged, + total_fee_y_charged, + } = p; + const firstToken = tokenX.id == token_x ? tokenX : tokenY; + const secondToken = tokenY.id == token_y ? tokenY : tokenX; + const firstTokenPrice = + (tokenPriceList && + tokenPriceList[firstToken.id] && + tokenPriceList[firstToken.id].price) || + '0'; + const secondTokenPrice = + (tokenPriceList && + tokenPriceList[secondToken.id] && + tokenPriceList[secondToken.id].price) || + '0'; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged || 0) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged || 0) + .toFixed(); + const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) + .times(firstTokenPrice) + .toNumber(); + const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) + .times(secondTokenPrice) + .toNumber(); + const totalTvl = tvlx + tvly; + p.tvl = totalTvl; + tvlList.push(totalTvl); + return p; + }); + const sumOfTvlList = _.sum(tvlList); + const tvlPercents = + sumOfTvlList === 0 + ? ['0', '0', '0', '0'] + : availablePools.map((p: PoolInfo) => + scientificNotationToString( + ((p.tvl / sumOfTvlList) * 100).toString() + ) + ); + const nonZeroIndexes: number[] = []; + tvlPercents.forEach((p, index) => { + if (Number(p) > 0) { + nonZeroIndexes.push(index); + } + }); + const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); + const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); + const finalPercents = tvlPercents.map((p, index) => { + if (nonZeroIndexes.includes(index)) { + const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; + return newP; + } + return p; + }); + const maxPercent = _.max(finalPercents); + let maxPercentPool; + availablePools.forEach((pool: PoolInfo, index) => { + const f = pool.fee; + const temp: PoolInfo = { + ...pool, + percent: finalPercents[index], + tvl: tvlList[index], + }; + currentPoolsMap[f] = temp; + if (finalPercents[index] == maxPercent) { + maxPercentPool = temp; + } + }); + // url-fee-pool + const urlFeePool = url_fee + ? currentPoolsMap[url_fee] || { fee: url_fee } + : null; + setCurrentPools(currentPoolsMap); + setCurrentSelectedPool(urlFeePool || maxPercentPool); + } else { + setCurrentPools({}); + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } else { + setCurrentPools({}); + if (tokenX && tokenY) { + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } + } + function switchSelectedFee(fee: number) { + if (tokenX && tokenY && currentPools) { + const pool = currentPools[fee]; + setCurrentSelectedPool(pool || { fee }); + if (!pool) { + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setInvalidRange(false); + } + } + } + function changeTokenXAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenXAmount(amount); + /*if (sort) {*/ + if (!onlyAddXToken && liquidityShape === 'Spot') { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenYAmount(amount_result); + } + /*} else { + if (!onlyAddYToken && liquidityShape === 'Spot') { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } + }*/ + } + function changeTokenYAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenYAmount(amount); + /*if (sort) {*/ + if (!onlyAddYToken && liquidityShape === 'Spot') { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + /*} else { + if (!onlyAddXToken && liquidityShape === 'Spot') { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenXAmount(amount_result); + } + }*/ + } + function getTokenYAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + // X-->L + const L = + +amount * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); + // L-->current Y + const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); + // L--> Y + const Y = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1)); + const decimalsRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + + const Y_result = (Y + Yc) * decimalsRate; + return Y_result.toString(); + } + } + function getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + let L; + // Yc-->L + if (leftPoint == currentPoint) { + L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); + } else { + // Y-->L + L = + +amount * + ((Math.sqrt(CONSTANT_D) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); + } + const X = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); + const decimalsRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const X_result = X * decimalsRate; + return X_result.toString(); + } + } + function pointChange({ + leftPoint, + rightPoint, + currentPoint, + }: { + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setInvalidRange(false); + setOnlyAddXToken(false); + setOnlyAddYToken(false); + // invalid point + if (leftPoint >= rightPoint) { + setInvalidRange(true); + setTokenXAmount(''); + setTokenYAmount(''); + return; + } + // can only add x token + if (leftPoint > currentPoint) { + setOnlyAddXToken(true); + if (sort) { + setTokenYAmount(''); + } else { + setTokenXAmount(''); + } + return; + } + // can only add y token + if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { + setOnlyAddYToken(true); + if (sort) { + setTokenXAmount(''); + } else { + setTokenYAmount(''); + } + return; + } + + if (liquidityShape !== 'Spot') return; + if (sort) { + if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } else { + if (tokenXAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + } + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; + } + } + // start + function get_slot_number_in_a_bin() { + const pool_id = currentSelectedPool?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + /** + * step1 当数据发生改变 + * leftPoint, rightPoint 有效 + * tokenXAmount, tokenYAmount 至少有一个有值 + * ===> 可以触发 + * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 + * step3 把新增的liquidity传递给Chart组件, + * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 + * step5 疑问;?实时修改图表 会导致效率什么问题吗? + */ + function generate_new_user_chart() { + if ( + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + (+tokenXAmount > 0 || +tokenYAmount > 0) + ) { + let new_nfts: any; + if (liquidityShape == 'Spot') { + const new_nft = getLiquiditySpot(); + new_nfts = [new_nft]; + } else { + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; + } + const processed_new_nfts = process_liquidities(new_nfts); + set_new_user_liquidities(processed_new_nfts); + } else { + set_new_user_liquidities([]); + } + } + function getLiquiditySpot() { + const { pool_id } = currentSelectedPool; + return { + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + // amount_x: tokenSort + // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + // amount_y: tokenSort + // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + // token_x: tokenSort ? tokenX : tokenY, + // token_y: tokenSort ? tokenY : tokenX, + }; + } + function formatWithCommas_for_tip(v: string, commas?: boolean) { + const v_big = Big(v || 0); + let v_temp; + if (v_big.lt(0.001)) { + v_temp = v_big.toFixed(6, 3); + } else { + if (commas) { + v_temp = formatWithCommas(v_big.toFixed(3, 3)); + } else { + v_temp = v_big.toFixed(3, 3); + } + } + return v_temp; + } + function getLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'ceil' + ); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(null); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 1 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least +
{ + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 2 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 3 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 4 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } + } + return nftList; + } + /** + * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == exclude_cur_total_nft_number) { + amount_x = dis + .mul(exclude_cur_total_nft_number + 1) + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 1).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == exclude_cur_total_nft_number) { + amount_y = dis + .mul(exclude_cur_total_nft_number + 1) + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 2).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == 0) { + amount_y = dis + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + // 不同点1 + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + // 不同点2 + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 2).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == 0) { + amount_x = dis + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 和 bid ask 上升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_rise_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + const left_i_point = left_point + nftWidth * i; + let right_i_point; + if (i == total_nft_number - 1) { + right_i_point = right_point; + } else { + right_i_point = left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + /** + * curve 和 bid ask 下降升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_decline_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐下降模式 + * nft 从右往左计算 + * e.g. + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + let left_i_point; + let right_i_point; + if (i == total_nft_number - 1) { + left_i_point = left_point; + } else { + left_i_point = right_point - nftWidth * (i + 1); + } + right_i_point = right_point - nftWidth * i; + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + /** + * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 + */ + function process_liquidities(liquidities: IAddLiquidityInfo[]) { + const { pool_id } = currentSelectedPool; + const new_liquidities: UserLiquidityInfo[] = []; + liquidities.forEach((l: IAddLiquidityInfo) => { + const { left_point, right_point, amount_x, amount_y } = l; + const L = get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount: amount_x, + token_y_amount: amount_y, + poolDetail: currentSelectedPool, + }); + new_liquidities.push({ + pool_id, + left_point, + right_point, + amount: L, + }); + }); + return new_liquidities; + } + function pointAndshapeChange() { + set_token_amount_tip(null); + if (liquidityShape == 'Spot') { + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount); + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount); + } + } + } + const tokenSort = tokenX?.id == currentSelectedPool?.token_x; + const mobileDevice = isMobile(); + // todo + return ( + + {/* mobile head */} +
+
+ +
+ + + +
+
+ {/* pc head */} +
{ + history.goBack(); + }} + > +
+ +
+ + + +
+ + {/* content */} +
+
+ {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + {/* right area */} +
+
+
+ +
+ + { + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ + + + {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null} + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + setHoverFeeBox(false); + }} + onMouseEnter={() => { + setHoverFeeBox(true); + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ +
+ +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1 && !only_suppport_spot_shape) + setLiquidityShape('Curve'); + else if (index == 2 && !only_suppport_spot_shape) + setLiquidityShape('BidAsk'); + }} + > + + + + {index === 0 && ( + + )} + {index === 1 && ( + + )} + + {index === 2 && ( + + )} + +
+ ); + } + )} +
+ {/* new user chart part */} + {isSignedIn && currentSelectedPool ? ( +
+
+
+ +
+
+ Generate +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ {/* */} +
+ )} +
+ ) : null} + + {currentSelectedPool && currentSelectedPool.pool_id && ( + + )} +
+ + +
+
+
+
+
+
+ ); +} +/** + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 传递的时候只传实际使用值 + * @returns + */ +function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + binNumber, + SLOT_NUMBER, + leftPoint, + rightPoint, + currentPoint, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + getLiquiditySpot, + getLiquidityForCurveAndBidAskMode, + } = useContext(LiquidityProviderData); + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const new_liquidity = getLiquiditySpot(); + add_liquidity(new_liquidity); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + nftList = getLiquidityForCurveAndBidAskMode(); + if (!nftList) { + setAddLiquidityButtonLoading(false); + return; + } + /** + * 计算出 nftList token x tokeny 的数量,这是需要的总数量 + * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 + * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 + */ + let last_total_needed_token_x_amount = Big(0); let last_total_needed_token_y_amount = Big(0); nftList.forEach((nft: IAddLiquidityInfo) => { const { amount_x, amount_y } = nft; @@ -2491,94 +4679,569 @@ function AddLiquidityButton() { /> ); } - return txt; + return txt; + } + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + if (tokenSort) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + } else if (onlyAddYToken) { + if (tokenSort) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + return !(condition1 && condition2); + } + const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquiditySpot + : addLiquidityForCurveAndBidAskMode; + + return ( +
+ {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} +
+ ); +} +function SetPointsComponent() { + const { + binNumber, + setBinNumber, + currentSelectedPool, + tokenX, + tokenY, + tokenXAmount, + tokenYAmount, + pointAndshapeChange, + + pointChange, + currentPoint, + liquidityShape, + + leftPoint, + setLeftPoint, + rightPoint, + setRightPoint, + + SLOT_NUMBER, + BIN_WIDTH, + + switch_pool_loading, + + isSignedIn, + } = useContext(LiquidityProviderData); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const [radius, setRadius] = useState(); + const [targetCustomPrice, setTargetCustomPrice] = useState(''); + const [leftCustomPrice, setLeftCustomPrice] = useState(''); + const [rightCustomPrice, setRightCustomPrice] = useState(''); + const [targetPoint, setTargetPoint] = useState(); + + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + + const [slider_point_min, set_slider_point_min] = useState(); + const [slider_point_max, set_slider_point_max] = useState(); + + const [slider_left_value, set_slider_left_value] = useState(); + const [slider_right_value, set_slider_right_value] = useState(); + + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + const tokenSort = tokenX.id == currentSelectedPool.token_x; + + // init + useEffect(() => { + if (currentSelectedPool?.pool_id && !switch_pool_loading) { + const { current_point } = currentSelectedPool; + const right_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + setTargetPoint(current_point); + setRadius(RADIUS_DEFAULT_NUMBER); + setLeftPoint(left_point); + setRightPoint(right_point); + set_slider_point_range(); + setPriceRangeMode('by_range'); + setChartTab('liquidity'); + } + }, [currentSelectedPool, switch_pool_loading]); + + // 中文 左侧改变===》点位 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + // effect bin + const diff = rightPoint - leftPoint; + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect slider + const slider_left_value = get_slider_value_by_point(leftPoint); + const slider_right_value = get_slider_value_by_point(rightPoint); + + set_slider_left_value(slider_left_value); + set_slider_right_value(slider_right_value); + // effect right area + pointChange({ leftPoint, rightPoint, currentPoint }); + } + }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); + // 数据有变动==》去掉token 太少提示 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + pointAndshapeChange(); + } + }, [liquidityShape, leftPoint, rightPoint]); + + // 修改bin --> 合适的右点位 --->合适的bin + function changeBin(bin: number) { + let appropriate_right_point = leftPoint + BIN_WIDTH * bin; + if (appropriate_right_point > POINTRIGHTRANGE) { + appropriate_right_point = POINTRIGHTRANGE; + } + const appropriate_bin_number = + (appropriate_right_point - leftPoint) / BIN_WIDTH; + setRightPoint(appropriate_right_point); + setBinNumber(appropriate_bin_number); + } + + // 修改radius-->合适的左右点位 --->合适的radius + function changeRadius(radius: number) { + let appropriate_right_point = handlePointToAppropriatePoint( + targetPoint + BIN_WIDTH * radius + ); + let appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + const appropriate_radius = + (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + setRadius(appropriate_radius); + } + // 修改 targetPrice-->合适的左右点位--->合适的targetPrice + function handleTargetPriceToAppropriatePoint(price: string) { + let appropriate_target_point = handlePriceToAppropriatePoint(price); + const appropriate_right_point = handlePointToAppropriatePoint( + appropriate_target_point + BIN_WIDTH * radius + ); + const appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + return appropriate_target_point; + } + // 设置slider可以操作的point 左右点位区间 + function set_slider_point_range() { + const { current_point } = currentSelectedPool; + const max_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) + ); + const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; + set_slider_point_min(min_point); + set_slider_point_max(max_point); + } + function get_slider_value_by_point(point: number) { + const value = (point - slider_point_min) / BIN_WIDTH; + return value; + } + function get_point_by_slider_value(v: number) { + const new_point = slider_point_min + v * BIN_WIDTH; + return new_point; + } + function handlePointToAppropriatePoint(point: number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } + function handlePriceToAppropriatePoint(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; + } + function getLeftPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(leftPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } } - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - if (tokenSort) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); + function getRightPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(rightPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; } else { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); + return toPrecision(price.toString(), 8); } - } else if (onlyAddYToken) { - if (tokenSort) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + return ''; + } + } + function getTargetPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(targetPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; } else { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); + return toPrecision(price.toString(), 8); } - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + return ''; } - return !(condition1 && condition2); } - const isAddLiquidityDisabled = getButtonStatus(); + function getPair() { + if (tokenSort) { + return `(${tokenX.symbol}/${tokenY.symbol})`; + } else { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } + } + return ( +
+ {/* chart area */} +
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + setChartTab('yours'); + }} + > + Yours + +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )} +
+ {isSignedIn && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ {/* set price range area */} +
+ {/* price range mode area */} +
+
+ + + + {getPair()} + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + changeRadius(radius); + }} + > + + +
+
+ {/* content */} + {/*
+ +
*/} +
+ {/* target price input box */} +
+ + + + +
+ + {/* radius input box */} +
+ + + + +
- const add_lp_func = - liquidityShape === 'Spot' - ? addLiquiditySpot - : addLiquidityForCurveAndBidAskMode; + {/* min price input box */} +
+ + + + +
- return ( -
- {isSignedIn ? ( - + + + + +
+ + {/* bin number input box */} +
+ + + + +
+
+ {/* tip in foot */} +
- <>{getButtonText()}} - /> - - ) : ( - - )} + *Only NEAR is needed in the price range you choose. +
+
); } -function SetPointsComponent() { +// todo +function SetPointsComponentReverse() { const { binNumber, setBinNumber, currentSelectedPool, tokenX, tokenY, - tokenXAmount, - tokenYAmount, pointAndshapeChange, pointChange, @@ -2611,54 +5274,37 @@ function SetPointsComponent() { const [targetInputStatus, setTargetInputStatus] = useState(false); const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - const [slider_point_min, set_slider_point_min] = useState(); - const [slider_point_max, set_slider_point_max] = useState(); - - const [slider_left_value, set_slider_left_value] = useState(); - const [slider_right_value, set_slider_right_value] = useState(); - - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - const tokenSort = tokenX.id == currentSelectedPool.token_x; + const token_x_decimals = tokenX.decimals + const token_y_decimals = tokenY.decimals // init useEffect(() => { if (currentSelectedPool?.pool_id && !switch_pool_loading) { const { current_point } = currentSelectedPool; - const right_point = handlePointToAppropriatePoint( + const left_point = get_bin_point_by_point( current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER ); - const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + const right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; setTargetPoint(current_point); setRadius(RADIUS_DEFAULT_NUMBER); setLeftPoint(left_point); setRightPoint(right_point); - set_slider_point_range(); setPriceRangeMode('by_range'); setChartTab('liquidity'); } }, [currentSelectedPool, switch_pool_loading]); - // 中文 左侧改变===》点位 + // 中文 左侧点位改变 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin - const diff = rightPoint - leftPoint; + const diff = leftPoint - rightPoint; const bin_number_temp = diff / BIN_WIDTH; setBinNumber(bin_number_temp); - // effect slider - const slider_left_value = get_slider_value_by_point(leftPoint); - const slider_right_value = get_slider_value_by_point(rightPoint); - - set_slider_left_value(slider_left_value); - set_slider_right_value(slider_right_value); // effect right area pointChange({ leftPoint, rightPoint, currentPoint }); } - }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); + }, [leftPoint, rightPoint, BIN_WIDTH]); // 数据有变动==》去掉token 太少提示 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { @@ -2668,87 +5314,47 @@ function SetPointsComponent() { // 修改bin --> 合适的右点位 --->合适的bin function changeBin(bin: number) { - let appropriate_right_point = leftPoint + BIN_WIDTH * bin; - if (appropriate_right_point > POINTRIGHTRANGE) { - appropriate_right_point = POINTRIGHTRANGE; + let appropriate_right_point = leftPoint - BIN_WIDTH * bin; + if (appropriate_right_point < POINTLEFTRANGE) { + appropriate_right_point = POINTLEFTRANGE; } const appropriate_bin_number = - (appropriate_right_point - leftPoint) / BIN_WIDTH; + (leftPoint - appropriate_right_point) / BIN_WIDTH; setRightPoint(appropriate_right_point); setBinNumber(appropriate_bin_number); } // 修改radius-->合适的左右点位 --->合适的radius function changeRadius(radius: number) { - let appropriate_right_point = handlePointToAppropriatePoint( + let appropriate_left_point = get_bin_point_by_point( targetPoint + BIN_WIDTH * radius ); - let appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 + let appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 ); const appropriate_radius = - (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); setLeftPoint(appropriate_left_point); setRightPoint(appropriate_right_point); setRadius(appropriate_radius); } // 修改 targetPrice-->合适的左右点位--->合适的targetPrice function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_target_point = handlePriceToAppropriatePoint(price); - const appropriate_right_point = handlePointToAppropriatePoint( + let appropriate_target_point = get_bin_point_by_price(reverse_price(price)); + const appropriate_left_point = get_bin_point_by_point( appropriate_target_point + BIN_WIDTH * radius ); - const appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 + const appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 ); - appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; setLeftPoint(appropriate_left_point); setRightPoint(appropriate_right_point); return appropriate_target_point; } - // 设置slider可以操作的point 左右点位区间 - function set_slider_point_range() { - const { current_point } = currentSelectedPool; - const max_point = handlePointToAppropriatePoint( - current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) - ); - const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; - set_slider_point_min(min_point); - set_slider_point_max(max_point); - } - function get_slider_value_by_point(point: number) { - const value = (point - slider_point_min) / BIN_WIDTH; - return value; - } - function get_point_by_slider_value(v: number) { - const new_point = slider_point_min + v * BIN_WIDTH; - return new_point; - } - function handlePointToAppropriatePoint(point: number) { - const { point_delta } = currentSelectedPool; - return getBinPointByPoint(point_delta, SLOT_NUMBER, point); - } - function handlePriceToAppropriatePoint(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const appropriate_point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return appropriate_point; - } function getLeftPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(leftPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } + if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(leftPoint)) { + const price = reverse_price(get_price_by_point(leftPoint)); if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -2759,14 +5365,8 @@ function SetPointsComponent() { } } function getRightPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(rightPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } + if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(rightPoint)) { + const price = reverse_price(get_price_by_point(rightPoint)) if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -2777,14 +5377,8 @@ function SetPointsComponent() { } } function getTargetPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(targetPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } + if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(targetPoint)) { + let price = reverse_price(get_price_by_point(targetPoint)); if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -2794,12 +5388,36 @@ function SetPointsComponent() { return ''; } } + function get_point_by_price(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate_point = Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const decimalRate_price = + Math.pow(10, token_x_decimals) / + Math.pow(10, token_y_decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_bin_point_by_price(price:string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; + } + function get_bin_point_by_point(point:number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } function getPair() { - if (tokenSort) { - return `(${tokenX.symbol}/${tokenY.symbol})`; - } else { - return `(${tokenY.symbol}/${tokenX.symbol})`; - } + return `(${tokenY.symbol}/${tokenX.symbol})`; } return (
)}
@@ -2857,11 +5476,12 @@ function SetPointsComponent() { !isInvalid(rightPoint) && !switch_pool_loading && (
- + reverse={true} + > */}
)}
@@ -2915,20 +5535,6 @@ function SetPointsComponent() {
{/* content */} - {/*
- -
*/}
{/* target price input box */}
- {/* {tokenSort ? ( */} { + return get_bin_point_by_price(reverse_price(price)); + }} disbaled={priceRangeMode === 'by_radius'} customPrice={leftCustomPrice} getPrice={getLeftPrice} @@ -2995,19 +5602,6 @@ function SetPointsComponent() { setPoint={setLeftPoint} point={leftPoint} > - {/* ) : ( - - )} */}
{/* max price input box */} @@ -3018,9 +5612,10 @@ function SetPointsComponent() { defaultMessage="Max Price" > - {/* {tokenSort ? ( */} { + return get_bin_point_by_price(reverse_price(price)); + }} customPrice={rightCustomPrice} getPrice={getRightPrice} setCustomPrice={setRightCustomPrice} @@ -3030,16 +5625,6 @@ function SetPointsComponent() { point={rightPoint} disbaled={priceRangeMode === 'by_radius'} > - {/* ) : ( - - )} */}
{/* bin number input box */} @@ -3670,16 +6255,4 @@ function InputAmount({ ) : null}
); -} -function getAllTokens(refTokens: TokenMetadata[], triTokens: TokenMetadata[]) { - triTokens.forEach((tk) => { - const tokenInRef = refTokens.find((token) => token.id === tk.id); - if (tokenInRef) { - tokenInRef.onTri = true; - } else { - refTokens.push(tk); - } - }); - - return refTokens; -} +} \ No newline at end of file diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 8c6f35a66..42b4565b7 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -2073,3 +2073,7 @@ export function get_token_amount_in_user_liquidities({ }); return [total_x_amount.toFixed(), total_y_amount.toFixed()]; } +export function reverse_price(price: string) { + if (Big(price).eq(0)) return '999999999999999999999'; + return Big(1).div(price).toFixed(); +} From a4dcc7a086a4a0ae0e5eb1eef133ee6cb7302bb8 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 14 Jul 2023 23:55:21 +0800 Subject: [PATCH 098/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 4 ++-- src/services/commonV3.ts | 14 +++++--------- tailwind.config.js | 2 +- 4 files changed, 9 insertions(+), 13 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index afb1f952f..a82a099ef 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1458,7 +1458,7 @@ export default function DclChart({ {/* hover到柱子(bin)上的悬浮框 */} -
+
Trailing 24hr APR diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 761f3f242..1134fbc82 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -694,8 +694,8 @@ function YourLiquidityBox(props: { } }, [poolDetail, tokenPriceList]); async function get_24_apr_and_fee() { - let apr_24 = ''; - let total_fee_earned = ''; + let apr_24 = '0'; + let total_fee_earned = '0'; const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ pool_id, account_id: accountId, diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 8c6f35a66..2c7f971c0 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1683,7 +1683,7 @@ export function get_account_24_apr( const { token_x_metadata, token_y_metadata } = pool; const price_x = tokenPriceList?.[token_x_metadata.id]?.price || 0; const price_y = tokenPriceList?.[token_y_metadata.id]?.price || 0; - let apr_24 = ''; + let apr_24 = '0'; const { apr } = dclAccountFee; // 24小时平均利润 const { fee_data, user_token, change_log_data } = apr; @@ -1737,15 +1737,11 @@ export function get_account_24_apr( const pre_processed_log = processed_change_log[processed_change_log.length - 1]; const { token_x, token_y, timestamp } = log; // timestamp 纳秒 - const real_token_x = Big(pre_processed_log.token_x) - .minus(token_x) - .toFixed(); - const real_token_y = Big(pre_processed_log.token_y) - .minus(token_y) - .toFixed(); + const real_token_x = Big(pre_processed_log.token_x).minus(token_x); + const real_token_y = Big(pre_processed_log.token_y).minus(token_y); const new_log = { - token_x: real_token_x, - token_y: real_token_y, + token_x: real_token_x.lt(0) ? '0' : real_token_x.toFixed(), + token_y: real_token_y.lt(0) ? '0' : real_token_y.toFixed(), timestamp: Big(timestamp).div(1000000000).toFixed(0), }; const processed_log: IProcessedLogData = process_log_data( diff --git a/tailwind.config.js b/tailwind.config.js index df862ffcf..3ae0e1965 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -405,7 +405,7 @@ module.exports = { overviewMaskColor: 'rgba(13, 29, 39, 0.5)', overviewPurpleColor: '#816CFF', overviewGreyColor: '#314758', - chartHoverBoxBg: 'rgba(26, 39, 48, 0.9)', + chartHoverBoxBg: 'rgba(26, 39, 48, 1)', chartBorderColor: '#344451', proTabBgColor: '#324451', dclTabBorderColor:'#3F4A52' From 55d09eef229705beb63dec3eba1de5f0f0412523 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 15 Jul 2023 14:16:42 +0800 Subject: [PATCH 099/204] fix bug --- src/components/d3Chart/interfaces.tsx | 2 - src/pages/poolsV3/PoolDetailV3.tsx | 2 +- src/services/commonV3.ts | 123 ++++++++++++++++---------- 3 files changed, 75 insertions(+), 52 deletions(-) diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 8475cc3dd..60e22112c 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -92,7 +92,5 @@ export interface IDclLogData { export interface IProcessedLogData { total_value: string; distance_from_24: string; - token_x: string; - token_y: string; } export type IRMTYPE = 'round' | 'floor' | 'ceil'; diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 1134fbc82..65d2da64a 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -689,7 +689,7 @@ function YourLiquidityBox(props: { } }, [liquidities, Object.keys(tokenPriceList).length]); useEffect(() => { - if (poolDetail && tokenPriceList) { + if (poolDetail && tokenPriceList && Object.keys(tokenPriceList).length) { get_24_apr_and_fee(); } }, [poolDetail, tokenPriceList]); diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 2c7f971c0..06cf530f8 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1675,6 +1675,7 @@ export function get_o_l_amount_by_condition({ return L; } +const second24 = 24 * 60 * 60; export function get_account_24_apr( dclAccountFee: IDCLAccountFee | any, pool: PoolInfo, @@ -1705,51 +1706,27 @@ export function get_account_24_apr( // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; - const second24 = 24 * 60 * 60; - const { token_x, token_y, timestamp: current_time } = user_token; - const before24Time = Big(current_time).minus(second24).toFixed(0); // 秒 - const token_x_NonDivisible = toNonDivisibleNumber( - token_x_metadata.decimals, - Big(token_x || 0).toFixed() - ); - const token_y_NonDivisible = toNonDivisibleNumber( - token_y_metadata.decimals, - Big(token_y || 0).toFixed() - ); - const current_user_token: IDclLogData = { - token_x: token_x_NonDivisible, - token_y: token_y_NonDivisible, - timestamp: current_time, - }; - const current_processed = process_log_data( - current_user_token, - before24Time, + const user_token_processed = process_user_token({ + user_token, pool, - tokenPriceList - ); - processed_change_log.push(current_processed); + tokenPriceList, + }); + const before24Time = Big(user_token.timestamp).minus(second24).toFixed(0); // 秒 + processed_change_log.push(user_token_processed); if (change_log_data?.length) { change_log_data.sort((b: IDclLogData, a: IDclLogData) => { return Big(a.timestamp).minus(b.timestamp).toNumber(); }); change_log_data.forEach((log: IDclLogData) => { - const pre_processed_log = - processed_change_log[processed_change_log.length - 1]; - const { token_x, token_y, timestamp } = log; // timestamp 纳秒 - const real_token_x = Big(pre_processed_log.token_x).minus(token_x); - const real_token_y = Big(pre_processed_log.token_y).minus(token_y); - const new_log = { - token_x: real_token_x.lt(0) ? '0' : real_token_x.toFixed(), - token_y: real_token_y.lt(0) ? '0' : real_token_y.toFixed(), - timestamp: Big(timestamp).div(1000000000).toFixed(0), - }; - const processed_log: IProcessedLogData = process_log_data( - new_log, + const preLog = processed_change_log[processed_change_log.length - 1]; + const processed_log: IProcessedLogData = process_log_data({ + preLog, + log, before24Time, pool, - tokenPriceList - ); + tokenPriceList, + }); processed_change_log.push(processed_log); }); // for 加权 @@ -1788,33 +1765,81 @@ export function get_account_24_apr( * timestamp 秒 * @returns */ -function process_log_data( - log: IDclLogData, - before24Time: string, - pool: PoolInfo, - tokenPriceList: any -) { +function process_log_data({ + preLog, + log, + before24Time, + pool, + tokenPriceList, +}: { + preLog: IProcessedLogData; + log: IDclLogData; + before24Time: string; + pool: PoolInfo; + tokenPriceList: any; +}) { + let total_value = Big(0); + let distance_from_24; const { token_x_metadata, token_y_metadata } = pool; - const { token_x, token_y, timestamp } = log; + // get current log changed total value + const { token_x, token_y } = log; + const token_x_abs = Big(token_x || 0).abs(); + const token_y_abs = Big(token_y || 0).abs(); + const token_x_amount = toReadableNumber( token_x_metadata.decimals, - Big(token_x || 0).toFixed() + Big(token_x_abs).toFixed() ); const token_y_amount = toReadableNumber( token_y_metadata.decimals, - Big(token_y || 0).toFixed() + Big(token_y_abs).toFixed() ); const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; const token_x_value = Big(token_x_amount).mul(price_x); const token_y_value = Big(token_y_amount).mul(price_y); - const total_value = token_x_value.plus(token_y_value).toFixed(); - const distance_from_24 = Big(timestamp).minus(before24Time).toFixed(0); // 秒 + + total_value = token_x_value.plus(token_y_value); + + distance_from_24 = Big(log.timestamp) + .div(1000000000) + .minus(before24Time) + .toFixed(0); // 秒 + + if (Big(token_x).lt(0) || Big(token_y).lt(0)) { + total_value = Big(preLog.total_value).plus(total_value); + } else { + total_value = Big(preLog.total_value).minus(total_value); + } + return { distance_from_24, + total_value: total_value.lt(0) ? '0' : total_value.toFixed(), + }; +} + +function process_user_token({ + user_token, + pool, + tokenPriceList, +}: { + user_token: IDclLogData; + pool: PoolInfo; + tokenPriceList: any; +}) { + const { token_x_metadata, token_y_metadata } = pool; + const { token_x, token_y } = user_token; + + const price_x = tokenPriceList?.[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList?.[token_y_metadata.id]?.price || 0; + + const token_x_value = Big(token_x || 0).mul(price_x); + const token_y_value = Big(token_y || 0).mul(price_y); + const total_value = token_x_value.plus(token_y_value).toFixed(); + + return { total_value, - token_x: Big(token_x).toFixed(), - token_y: Big(token_y).toFixed(), + distance_from_24: Big(second24).toFixed(), }; } From e55770c2c59a7d48549ee10dcddd31b787c96a80 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 15 Jul 2023 23:40:42 +0800 Subject: [PATCH 100/204] fix bug --- src/components/d3Chart/DclChart.tsx | 54 +++++++++++++-------- src/components/swap/SwapLimitOrderChart.tsx | 8 +++ 2 files changed, 42 insertions(+), 20 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index a82a099ef..2ea8798bf 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -77,6 +77,7 @@ export default function DclChart({ }; newlyAddedLiquidities?: UserLiquidityInfo[]; }) { + console.log('pool_id', pool_id); const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); const [chartDataList, setChartDataList] = useState(); @@ -429,26 +430,39 @@ export default function DclChart({ const { liquidities, orders } = marketdepthData; let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); - // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 - let range_contain_current_point: any; - liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { - const { left_point, right_point, amount_l } = l; - if (right_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['left_point'] = left_point; - range_contain_current_point['amount_l'] = amount_l; - return false; - } - if (left_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['right_point'] = right_point; - range_contain_current_point['amount_l'] = amount_l; - return false; + /** + * 去找 left_point左点位是当前点位,右点位也是当前点位的数据,如果有两条,这需要把这两条数据合并,否则不动。 + */ + let merged_current_point_liquidity: ILiquidityInfoPool; + const contain_current_point_liquidities: ILiquidityInfoPool[] = []; + const exclude_current_point_liquidities_array = liquidities_array.filter( + (l: ILiquidityInfoPool) => { + const { left_point, right_point } = l; + if ( + right_point == pool.current_point || + left_point == pool.current_point + ) { + contain_current_point_liquidities.push(l); + return false; + } + return true; } - return true; - }); - if (range_contain_current_point) { - liquidities_array.push(range_contain_current_point); + ); + if (contain_current_point_liquidities.length == 2) { + contain_current_point_liquidities.sort( + (b: ILiquidityInfoPool, a: ILiquidityInfoPool) => { + return b.left_point - a.left_point; + } + ); + merged_current_point_liquidity = { + left_point: contain_current_point_liquidities[0].left_point, + right_point: contain_current_point_liquidities[1].right_point, + amount_l: contain_current_point_liquidities[0].amount_l, + }; + exclude_current_point_liquidities_array.push( + merged_current_point_liquidity + ); + liquidities_array = exclude_current_point_liquidities_array; } const orders_array: IOrderInfoPool[] = Object.values(orders); const pointsData_l = divide_liquidities_into_bins_pool({ @@ -539,7 +553,7 @@ export default function DclChart({ const chartDataListInRange = chartDataList.filter((d: IChartData) => { const { point } = d; const point_right = point + bin_width; - return point >= point_l && point_right <= point_r; + return point_right > point_l && point < point_r; }); return chartDataListInRange; } diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 842dd0173..d09961ab0 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -4,6 +4,7 @@ import React, { useMemo, useState, createContext, + useRef, } from 'react'; import { get_pointorder_range } from '../../services/swapV3'; import { get_pool, PoolInfo } from '../../services/swapV3'; @@ -43,6 +44,7 @@ export default function SwapLimitOrderChart() { const pool_id = dcl_pool_id; const left_point = -800000; const right_point = 600000; + const sellBoxRef = useRef(null); useEffect(() => { if (pool_id) { get_points_of_orders(); @@ -76,6 +78,11 @@ export default function SwapLimitOrderChart() { buy_token_y_list, sell_token_y_list, ]); + useEffect(() => { + if (sellBoxRef.current && sell_list?.length) { + sellBoxRef.current.scrollTop = 10000; + } + }, [sellBoxRef, sell_list]); const [cur_pairs, cur_token_symbol, cur_pair_icons] = useMemo(() => { if (pool) { const classStr = 'w-6 h-6 rounded-full border border-gradientFromHover'; @@ -423,6 +430,7 @@ export default function SwapLimitOrderChart() {
From 5d6f82fab52f5aef5d1a04e01f9a40c8b5eb90b3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 16 Jul 2023 21:47:26 +0800 Subject: [PATCH 101/204] fix bug --- src/pages/poolsV3/PoolDetailV3.tsx | 74 +++++------------------------- 1 file changed, 11 insertions(+), 63 deletions(-) diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 65d2da64a..08bc8181c 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -3,14 +3,9 @@ import { useLocation, useParams } from 'react-router-dom'; import { Card } from '~components/card/Card'; import { Link } from 'react-router-dom'; import { - calculateFairShare, - calculateFeePercent, - percent, - toNonDivisibleNumber, toPrecision, toReadableNumber, toInternationalCurrencySystem, - toRoundedReadableNumber, formatWithCommas, } from '../../utils/numbers'; import { useClientMobile, isClientMobie } from '../../utils/device'; @@ -132,6 +127,7 @@ import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; import { formatPercentage, formatWithCommas_usd, + formatNumber, } from '../../components/d3Chart/utils'; import { getDCLAccountFee } from '../../services/indexer'; @@ -632,6 +628,8 @@ function YourLiquidityBox(props: { const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); const [accountAPR, setAccountAPR] = useState(''); const [earned_fee, set_earned_fee] = useState(''); + const [earned_fee_x_amount, set_earned_fee_x_amount] = useState(); + const [earned_fee_y_amount, set_earned_fee_y_amount] = useState(); const [operationType, setOperationType] = useState('add'); const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; const { accountId } = useWalletSelector(); @@ -696,6 +694,8 @@ function YourLiquidityBox(props: { async function get_24_apr_and_fee() { let apr_24 = '0'; let total_fee_earned = '0'; + let total_earned_fee_x; + let total_earned_fee_y; const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ pool_id, account_id: accountId, @@ -706,11 +706,11 @@ function YourLiquidityBox(props: { // total earned fee const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; // 总共赚到的fee - const total_earned_fee_x = toReadableNumber( + total_earned_fee_x = toReadableNumber( token_x_metadata.decimals, Big(total_fee_x || 0).toFixed() ); - const total_earned_fee_y = toReadableNumber( + total_earned_fee_y = toReadableNumber( token_y_metadata.decimals, Big(total_fee_y || 0).toFixed() ); @@ -722,7 +722,8 @@ function YourLiquidityBox(props: { .plus(total_earned_fee_y_value) .toFixed(); } - + set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); + set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); set_earned_fee(formatWithCommas_usd(total_fee_earned)); setAccountAPR(formatPercentage(apr_24)); } @@ -868,59 +869,6 @@ function YourLiquidityBox(props: { total_y: display_total_y, }; } - - function getTotalFee() { - let total_x = 0; - let total_y = 0; - - let total_price = 0; - - user_liquidities_detail.forEach((liquidityDetail: UserLiquidityDetail) => { - const { - unclaimed_fee_x_amount, - unclaimed_fee_y_amount, - total_fees_price, - } = liquidityDetail; - total_x += +unclaimed_fee_x_amount; - total_y += +unclaimed_fee_y_amount; - - total_price += +total_fees_price; - }); - - let display_total_price = '$'; - - if (total_price == 0) { - display_total_price = display_total_price + '0'; - } else if (total_price < 0.01) { - display_total_price = display_total_price + '<0.01'; - } else { - display_total_price = - display_total_price + - toInternationalCurrencySystem(total_price.toString(), 3); - } - - let display_total_x = '0'; - let display_total_y = '0'; - if (total_x == 0) { - display_total_x = '0'; - } else if (total_x < 0.01) { - display_total_x = '<0.01'; - } else { - display_total_x = toInternationalCurrencySystem(total_x.toString(), 3); - } - if (total_y == 0) { - display_total_y = '0'; - } else if (total_y < 0.01) { - display_total_y = '<0.01'; - } else { - display_total_y = toInternationalCurrencySystem(total_y.toString(), 3); - } - return { - total_fee_x: display_total_x, - total_fee_y: display_total_y, - total_fee_price: display_total_price, - }; - } function removeLiquidity() { setOperationType('remove'); setShowSelectLiquidityBox(true); @@ -1121,13 +1069,13 @@ function YourLiquidityBox(props: { >
{toRealSymbol(token_x_metadata.symbol)} - {getTotalFee().total_fee_x} + {earned_fee_x_amount || '-'}
{toRealSymbol(token_y_metadata.symbol)} - {getTotalFee().total_fee_y} + {earned_fee_y_amount || '-'}
)} From f71abf1625d0ccde4c4ecb37c6371ba7001993f5 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 6 Aug 2023 20:05:17 +0800 Subject: [PATCH 102/204] add reverse processing --- src/components/d3Chart/DclChart.tsx | 2101 +----- src/components/d3Chart/DclChartCopy.tsx | 3321 +++++++++ src/components/d3Chart/config.tsx | 32 +- src/components/d3Chart/interfaces.tsx | 11 +- src/components/forms/SelectToken.tsx | 4 +- src/components/pool/RemovePoolV3.tsx | 1107 +-- src/components/pool/RemovePoolV3Copy.tsx | 1844 +++++ src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6191 +++++------------ .../poolsV3/AddYourLiquidityPageV3Copy.tsx | 6164 ++++++++++++++++ 9 files changed, 13461 insertions(+), 7314 deletions(-) create mode 100644 src/components/d3Chart/DclChartCopy.tsx create mode 100644 src/components/pool/RemovePoolV3Copy.tsx create mode 100644 src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 903aa0026..44b58febe 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -19,7 +19,6 @@ import { get_x_y_amount_by_condition, get_account_24_apr, divide_liquidities_into_bins_pool, - get_token_amount_in_user_liquidities, reverse_price, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; @@ -33,7 +32,6 @@ import { IUserLiquiditiesDetail, IDCLAccountFee, IRMTYPE, - IDclChartProps } from './interfaces'; import { formatPrice, @@ -51,1595 +49,9 @@ import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; -import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; +import { toReadableNumber } from '~utils/numbers'; import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; -export default function DclChart(props:IDclChartProps) { - if (props.reverse) { - return - } else { - return - } -} -function DclChartForward({ - pool_id, - leftPoint, - rightPoint, - setLeftPoint, - setRightPoint, - config, - chartType, - removeParams, - newlyAddedLiquidities, -}:IDclChartProps) { - const [pool, setPool] = useState(); - const [price_range, set_price_range] = useState(); - const [chartDataList, setChartDataList] = useState(); - const [binDetail, setBinDetail] = useState(); - const [dragLeftPoint, setDragLeftPoint] = useState(); - const [dragRightPoint, setDragRightPoint] = useState(); - const [zoom, setZoom] = useState(); - const [randomId, setRandomId] = useState('.' + createRandomString()); - const [drawChartDone, setDrawChartDone] = useState(false); - const [user_liquidities, set_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - const [user_liquidities_detail, set_user_liquidities_detail] = - useState(); - const [tokenPriceList, setTokenPriceList] = useState>(); - /** constant start */ - const appearanceConfig: IPoolChartConfig = config || {}; - let [timerObj, setTimerObj] = useState({ - timer: '', - }); - const dragBarWidth = 28; - const percentBoxWidth = 44; - const min_bar_height = 2; - const svgWidth = +(appearanceConfig.svgWidth || 520); - const svgHeight = +(appearanceConfig.svgHeight || 250); - const axisHeight = appearanceConfig.axisHidden - ? appearanceConfig.controlHidden - ? 0 - : 5 - : 21; - const wholeBarHeight = svgHeight - axisHeight; - const disFromHoverBoxToPointer = +( - appearanceConfig.disFromPercentBoxToDragBar || 20 - ); - const disFromPercentBoxToDragBar = +( - appearanceConfig.disFromPercentBoxToDragBar || 2 - ); - const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); - const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% - // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 - const whole_bars_background_padding = +( - appearanceConfig.whole_bars_background_padding || 20 - ); - /** constant end */ - const { accountId } = useWalletSelector(); - useEffect(() => { - // get all token prices - getBoostTokenPrices().then((result) => { - setTokenPriceList(result); - }); - }, []); - // init - useEffect(() => { - clearTimeout(timerObj.timer); - if (pool_id) { - timerObj.timer = setTimeout(() => { - get_pool_detail(pool_id); - }, 500); - } - }, [pool_id]); - // init 从后端获取数据 - useEffect(() => { - if (pool) { - get_chart_data(); - } - }, [pool, accountId]); - // 更新用户数据 - useEffect(() => { - if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { - const new_list = get_latest_user_chart_data(); - setChartDataList(new_list); - } - }, [newlyAddedLiquidities, user_liquidities]); - // 绘制图表 - useEffect(() => { - if ( - (chartType !== 'USER' && price_range && chartDataList) || - (chartType == 'USER' && chartDataList?.length) - ) { - drawChart(); - setDrawChartDone(true); - } - }, [price_range, chartDataList]); - useEffect(() => { - if ( - isValid(dragLeftPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragLeftPoint; - const newPrice = get_price_by_point(newPoint); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = rightX - x - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`) - .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) - .attr('width', W); - } - }, [dragLeftPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(leftPoint)) { - setDragLeftPoint(leftPoint); - } - }, [leftPoint]); - useEffect(() => { - if ( - isValid(dragRightPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragRightPoint; - const newPrice = get_price_by_point(newPoint); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr( - 'transform', - `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = x - leftX - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`).attr('width', W); - } - }, [dragRightPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(rightPoint)) { - setDragRightPoint(rightPoint); - } - }, [rightPoint]); - useEffect(() => { - if (config?.radiusMode && config?.targetPoint && drawChartDone) { - // hide drag bar and show target price bar - draw_radius_mode_bar(); - d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); - d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); - } else { - d3.select(`${randomId} .leftBar`).attr('style', ''); - d3.select(`${randomId} .rightBar`).attr('style', ''); - d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); - } - }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); - /** - * 中文 - * remove 流动性,当参数改变,重新绘制删除区域的北京框 - */ - useEffect(() => { - if (removeParams && drawChartDone) { - const scale = scaleAxis(); - const scaleBar = scaleAxisY(); - draw_background_bars_for_select_area({ scale, scaleBar }); - } - }, [ - removeParams?.all, - removeParams?.fromLeft, - removeParams?.fromRight, - removeParams?.point, - drawChartDone, - ]); - /** - * 获取个人流动性图表详情信息 - * 用户 hover 框里数据展示 - */ - useEffect(() => { - if ( - user_liquidities.length && - pool && - chartType == 'USER' && - tokenPriceList - ) { - get_user_liquidities_detail(); - } - }, [user_liquidities, pool, chartType, tokenPriceList]); - async function get_user_liquidities_detail() { - const { token_x_metadata, token_y_metadata, pool_id } = pool; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ - pool_id, - account_id: accountId, - }); - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - - const points: number[] = []; - let total_x_amount = Big(0); - let total_y_amount = Big(0); - let total_value = Big(0); - let total_fee_earned = Big(0); - let apr_24 = ''; - if (dcl_fee_result) { - const dclAccountFee: IDCLAccountFee = dcl_fee_result; - const { total_earned_fee } = dclAccountFee; - // 总共赚到的fee - const { total_fee_x, total_fee_y } = total_earned_fee || {}; - const total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - const total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value.plus( - total_earned_fee_y_value - ); - // 24h 利润 - apr_24 = formatPercentage( - get_account_24_apr(dcl_fee_result, pool, tokenPriceList) - ); - } - user_liquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - points.push(left_point, right_point); - }); - const [total_token_x_amount, total_token_y_amount] = - get_token_amount_in_user_liquidities({ - user_liquidities, - pool, - token_x_metadata, - token_y_metadata, - }); - - total_x_amount = total_x_amount.plus(total_token_x_amount); - total_y_amount = total_y_amount.plus(total_token_y_amount); - - const total_x_value = Big(price_x).mul(total_x_amount); - const total_y_value = Big(price_y).mul(total_y_amount); - - total_value = total_x_value.plus(total_y_value); - points.sort((b: number, a: number) => { - return b - a; - }); - const min_point = points[0]; - const max_point = points[points.length - 1]; - const min_price = get_price_by_point(min_point); - const max_price = get_price_by_point(max_point); - set_user_liquidities_detail({ - total_value: formatWithCommas_usd(total_value.toFixed()), - min_price: formatPrice(min_price), - max_price: formatPrice(max_price), - total_x_amount: formatNumber(total_x_amount.toFixed()), - total_y_amount: formatNumber(total_y_amount.toFixed()), - apr_24, - total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), - }); - } - function get_latest_user_chart_data() { - const { token_x_metadata, token_y_metadata } = pool; - const { bin: bin_final } = getConfig(); - const nfts = user_liquidities.concat(newlyAddedLiquidities || []); - const list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - return list; - } - /** - * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 - */ - async function get_pool_detail(pool_id: string) { - const p: PoolInfo = await get_pool(pool_id); - const { token_x, token_y } = p; - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - setPool(p); - } - async function get_chart_data() { - const { range } = getConfig(); - const list = await get_data_from_back_end(); - if (chartType !== 'USER') { - const [price_l_default, price_r_default] = - get_price_range_by_percent(range); - set_price_range([+price_l_default, +price_r_default]); - setZoom(range); - } - setChartDataList(list); - } - async function get_data_from_back_end() { - const { point_delta, token_x_metadata, token_y_metadata, pool_id } = pool; - const { bin: bin_final, rangeGear } = getConfig(); - const decimalRate_point = - Math.pow(10, token_y_metadata.decimals) / - Math.pow(10, token_x_metadata.decimals); - const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); - const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); - const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); - let list = []; - if (chartType == 'USER' && accountId) { - const liquidities = await list_liquidities(); - const nfts = liquidities.filter((l: UserLiquidityInfo) => { - return l.pool_id == pool_id; - }); - set_user_liquidities(nfts); - list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - } else { - const pointsData_apr = await getDclPoolPoints( - pool_id, - bin_final, - point_l, - point_r - ); - const marketdepthData = await get_pool_marketdepth(pool_id); - const { liquidities, orders } = marketdepthData; - let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); - - /** - * 去找 left_point左点位是当前点位,右点位也是当前点位的数据,如果有两条,这需要把这两条数据合并,否则不动。 - */ - let merged_current_point_liquidity: ILiquidityInfoPool; - const contain_current_point_liquidities: ILiquidityInfoPool[] = []; - const exclude_current_point_liquidities_array = liquidities_array.filter( - (l: ILiquidityInfoPool) => { - const { left_point, right_point } = l; - if ( - right_point == pool.current_point || - left_point == pool.current_point - ) { - contain_current_point_liquidities.push(l); - return false; - } - return true; - } - ); - if (contain_current_point_liquidities.length == 2) { - contain_current_point_liquidities.sort( - (b: ILiquidityInfoPool, a: ILiquidityInfoPool) => { - return b.left_point - a.left_point; - } - ); - merged_current_point_liquidity = { - left_point: contain_current_point_liquidities[0].left_point, - right_point: contain_current_point_liquidities[1].right_point, - amount_l: contain_current_point_liquidities[0].amount_l, - }; - exclude_current_point_liquidities_array.push( - merged_current_point_liquidity - ); - liquidities_array = exclude_current_point_liquidities_array; - } - const orders_array: IOrderInfoPool[] = Object.values(orders); - const pointsData_l = divide_liquidities_into_bins_pool({ - liquidities: liquidities_array, - orders: orders_array, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - console.log('pointsData_l', pointsData_l); - console.log('pointsData_apr', pointsData_apr.point_data); - const binWidth = bin_final * point_delta; - list = combine_data( - pointsData_apr?.point_data, - pointsData_l, - point_l, - point_r, - binWidth - ); - // list = pointsData_apr.point_data; - } - return list; - } - function combine_data( - pointsData_apr: IChartData[], - pointsData_l: IChartData[], - min_point: number, - max_point: number, - binWidth: number - ) { - const pointsData: IChartData[] = []; - const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - const pointsData_l_inRange = pointsData_l?.filter((d: IChartData) => { - return d.point >= min_point && d.point + binWidth <= max_point; - }); - const pointsData_l_map = pointsData_l_inRange?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - if (pointsData_l_map) { - Object.keys(pointsData_l_map).forEach((point_l: string) => { - const { - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - point, - pool_id, - } = pointsData_l_map[point_l]; - - const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; - - pointsData.push({ - fee: fee || '0', - total_liquidity: total_liquidity || '0', - pool_id, - point, - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - }); - pointsData.sort((b: IChartData, a: IChartData) => { - return b.point - a.point; - }); - }); - } - return pointsData; - } - function getChartDataListInRange() { - const point_l = get_point_by_price(price_range[0].toString()); - const point_r = get_point_by_price( - price_range[price_range.length - 1].toString() - ); - const bin_width = getConfig().bin * pool.point_delta; - const chartDataListInRange = chartDataList.filter((d: IChartData) => { - const { point } = d; - const point_right = point + bin_width; - return point_right > point_l && point < point_r; - }); - return chartDataListInRange; - } - function get_price_range_by_percent(percent: number): [string, string] { - const p_l_r = percent / 100; - const price = get_current_price(); - const price_l_temp = Big(1 - p_l_r).mul(price); - const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); - const price_r = Big(1 + p_l_r) - .mul(price) - .toFixed(); - - return [price_l, price_r]; - } - function drawChart() { - const data: IChartData[] = process_chart_data_for_display(); - const scale = scaleAxis(); - const scaleBar = scaleAxisY(); - // down bars - draw_down_bars({ data, scale, scaleBar }); - // up bars - if (chartType !== 'USER') { - draw_up_bars({ data, scale, scaleBar }); - } - // 创建横坐标轴 - if (appearanceConfig.axisHidden) { - d3.select(`${randomId} .axis`).remove(); - } else { - draw_axis({ scale }); - } - // background bars - if (appearanceConfig.hoverBoxHidden) { - d3.select(`${randomId} .bars_background`).remove(); - d3.select(`${randomId} .overBox`).remove(); - d3.select(`${randomId} .whole_bars_background`).remove(); - d3.select(`${randomId} .wholeOverBox`).remove(); - } else { - draw_background_bars({ data, scale, scaleBar }); - } - // remove select area - if (chartType == 'USER' && removeParams) { - draw_background_bars_for_select_area({ scale, scaleBar }); - } else { - d3.select('.remove_bars_background').remove(); - } - - // current line - if (appearanceConfig.currentBarHidden) { - d3.select(`${randomId} .currentLine`).remove(); - } else { - draw_current_bar({ scale }); - } - if (appearanceConfig.controlHidden) { - remove_control(); - } else { - // init - // drag left - draw_drag_left({ scale }); - // drag right - draw_drag_right({ scale }); - } - } - function process_chart_data_for_display() { - const { bin: bin_final } = getConfig(); - const { token_x_metadata, token_y_metadata, point_delta } = pool; - const decimalRate_price = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - const list = - chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const data: IChartData[] = list.map((o: IChartData) => { - const { point } = o; - const price_l = getPriceByPoint(+point, decimalRate_price); - const point_r = +point + point_delta * bin_final; - const price_r = getPriceByPoint(point_r, decimalRate_price); - - return { - ...o, - liquidity: Big(o.liquidity || 0).toFixed(), - order_liquidity: Big(o.order_liquidity || 0).toFixed(), - token_x: Big(o.token_x || 0).toFixed(), - token_y: Big(o.token_y || 0).toFixed(), - order_x: Big(o.order_x || 0).toFixed(), - order_y: Big(o.order_y || 0).toFixed(), - total_liquidity: Big(o.total_liquidity || 0).toFixed(), - fee: Big(o.fee || 0).toFixed(), - price: price_l.toString(), - price_r: price_r.toString(), - point_r: point_r, - }; - }); - return data; - } - function hoverBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = - d; - const { colors } = getConfig(); - - const total_token_x = Big(token_x).plus(order_x); - const total_token_y = Big(token_y).plus(order_y); - const price_by_token_x = get_price_by_point(+point); - const price_by_token_y = reverse_price(price_by_token_x); - const apr = Big(total_liquidity).gt(0) - ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() - : '0'; - setBinDetail({ - feeApr: formatPercentage(apr), - colors, - ...(total_token_x.gt(0) - ? { - token_x_amount: formatWithCommas_number(total_token_x.toFixed()), - token_x_amount_in_liquidity: formatWithCommas_number(token_x), - token_x_amount_in_order: formatWithCommas_number(order_x), - } - : {}), - ...(total_token_y.gt(0) - ? { - token_y_amount: formatWithCommas_number(total_token_y.toFixed()), - token_y_amount_in_liquidity: formatWithCommas_number(token_y), - token_y_amount_in_order: formatWithCommas_number(order_y), - } - : {}), - price_by_token_x: formatPrice(price_by_token_x), - price_by_token_y: formatPrice(price_by_token_y), - }); - } - function LeaveBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function hoverUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function LeaveUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function remove_control() { - d3.select(`${randomId} .control`).remove(); - d3.select(`${randomId} .overlap`).remove(); - d3.select(`${randomId} .rightBar`).remove(); - d3.select(`${randomId} .leftBar`).remove(); - } - function draw_axis({ scale }: { scale: any }) { - const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); - if (appearanceConfig.ticks || chartType == 'USER') { - axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { - const dBig = new Big(d); - if (dBig.gte(10000)) { - return dBig.toFixed(0); - } else { - return d; - } - }); - } - d3.select(`${randomId} svg .axis`) - .call(axis) - .selectAll('text') - .attr('fill', '#7E8A93'); - d3.select(`${randomId} svg .axis`) - .attr('transform', `translate(0, ${svgHeight - axisHeight})`) - .select('.domain') - .attr('stroke', 'transparent'); - } - function draw_down_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { current_point } = pool; - const { colors } = getConfig(); - d3.select(`${randomId} .bars_liquidity`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }); - } - function draw_up_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { colors } = getConfig(); - const { current_point } = pool; - d3.select(`${randomId} .bars_order`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.order_liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return ( - wholeBarHeight - - get_final_bar_height(scaleBar(+d.liquidity)) - - get_final_bar_height(scaleBar(+d.order_liquidity)) - ); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }) - .attr('opacity', '0.7'); - } - function get_final_bar_height(h: number) { - if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) - return min_bar_height; - return h; - } - function draw_background_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - if (chartType == 'USER') { - draw_background_bars_user({ - scale, - scaleBar, - }); - } else { - draw_background_bars_pool({ - data, - scale, - }); - } - } - function draw_background_bars_pool({ - data, - scale, - }: { - data: IChartData[]; - scale: Function; - }) { - d3.select(`${randomId} .bars_background`) - .selectAll('rect') - .data(data) - .join('rect') - .on('mousemove', function (e, d) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverBox(e, d); - }) - .on('mouseleave', function (e, d) { - d3.select(this).attr('fill', 'transparent'); - LeaveBox(e, d); - }) - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return wholeBarHeight; - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return 0; - }) - .attr('rx', 2) - .attr('fill', 'transparent'); - } - function draw_background_bars_user({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - d3.select(`${randomId} .whole_bars_background`) - .on('mousemove', function (e) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverUserBox(e); - }) - .on('mouseleave', function (e) { - d3.select(this).attr('fill', 'transparent'); - LeaveUserBox(e); - }) - .transition() - .attr('width', function () { - return ( - scale(sortP[sortP.length - 1]) - - scale(sortP[0]) + - whole_bars_background_padding * 2 - ); - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - return scale(sortP[0]) - whole_bars_background_padding; - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'transparent'); - } - function draw_background_bars_for_select_area({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - const min_bin_price = sortP[0]; - const max_bin_price = sortP[sortP.length - 1]; - const { fromLeft, fromRight, all, point } = removeParams; - d3.select(`${randomId} .remove_bars_background`) - .attr('width', function () { - if (all) { - return scale(max_bin_price) - scale(min_bin_price); - } else if (fromLeft) { - return scale(get_price_by_point(point)) - scale(min_bin_price); - } else if (fromRight) { - return scale(max_bin_price) - scale(get_price_by_point(point)); - } - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - if (fromRight) { - return scale(get_price_by_point(point)); - } else { - return scale(min_bin_price); - } - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'rgba(255,255,255,0.1)'); - } - function draw_current_bar({ scale }: { scale: Function }) { - d3.select(`${randomId} .currentLine`).attr( - 'style', - `transform:translate(${ - scale(+get_current_price()) + svgPaddingX - }px, -${axisHeight}px)` - ); - } - function draw_drag_left({ scale }: { scale: any }) { - // 初始化左的位置 - const price = get_current_price(); - let price_l; - if (dragLeftPoint) { - price_l = get_price_by_point(dragLeftPoint); - } else { - const price_l_temp = Big(1 - defaultPercent / 100) - .mul(price) - .toFixed(); - const newLeftPoint = get_nearby_bin_left_point( - get_point_by_price(price_l_temp) - ); - price_l = get_price_by_point(newLeftPoint); - setDragLeftPoint(newLeftPoint); - } - const x = scale(price_l) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(`${diffPrices(price_l)}%`) - .attr('fill', 'white'); - const dragLeft = d3.drag().on('drag', function (e) { - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - if (rightX < e.x || e.x < dragBarWidth / 2) return; - const p = scale.invert(e.x); - const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); - - setDragLeftPoint(newLeftPoint); - setLeftPoint && setLeftPoint(newLeftPoint); - }); - d3.select(`${randomId} .drag-left`).call(dragLeft); - } - function draw_drag_right({ scale }: { scale: any }) { - // 初始化右的位置 - const price = get_current_price(); - let price_r; - if (dragRightPoint) { - price_r = get_price_by_point(dragRightPoint); - } else { - const price_r_temp = Big(1 + defaultPercent / 100) - .mul(price) - .toFixed(); - const newRightPoint = get_nearby_bin_right_point( - get_point_by_price(price_r_temp) - ); - price_r = get_price_by_point(newRightPoint); - setDragRightPoint(newRightPoint); - } - const x = scale(price_r); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) - .select('text') - .text(`${diffPrices(price_r)}%`) - .attr('fill', 'white'); - const dragRight = d3.drag().on('drag', (e) => { - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); - if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; - const p = scale.invert(e.x); - const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); - setDragRightPoint(newRightPoint); - setRightPoint && setRightPoint(newRightPoint); - }); - d3.select(`${randomId} .drag-right`).call(dragRight); - } - function draw_radius_mode_bar() { - const scale: any = scaleAxis(); - const { targetPoint } = config; - const price = get_price_by_point(targetPoint); - const x = scale(price); - d3.select(`${randomId} .radiusBar`) - .attr('transform', `translate(${x}, -${axisHeight})`) - .attr('style', 'display:block'); - } - function get_current_price() { - return get_price_by_point(pool.current_point); - } - function get_point_by_price(price: string) { - const { point_delta, token_x_metadata, token_y_metadata } = pool; - const decimalRate_point = - Math.pow(10, token_y_metadata.decimals) / - Math.pow(10, token_x_metadata.decimals); - const point = getPointByPrice(point_delta, price, decimalRate_point); - return point; - } - function get_price_by_point(point: number) { - const { token_x_metadata, token_y_metadata } = pool; - const decimalRate_price = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - return getPriceByPoint(point, decimalRate_price); - } - function get_nearby_bin_right_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin + slots; - } - function get_nearby_bin_left_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin; - } - function clickToLeft() { - const { bin } = getConfig(); - const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); - setDragLeftPoint(newPoint_nearby_bin); - setLeftPoint && setLeftPoint(newPoint_nearby_bin); - } - function clickToRight() { - const { bin } = getConfig(); - const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); - setDragRightPoint(newPoint_nearby_bin); - setRightPoint && setRightPoint(newPoint_nearby_bin); - } - function get_bin_point_by_point(point: number, type?: IRMTYPE) { - const { point_delta } = pool; - const slot_num = getConfig().bin; - return getBinPointByPoint(point_delta, slot_num, point, type); - } - function scaleAxis() { - if (chartType == 'USER') { - return scaleAxis_User(); - } else { - return scaleAxis_Pool(); - } - } - function scaleAxis_Pool() { - return d3 - .scaleLinear() - .domain(price_range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxis_User() { - const binWidth = get_bin_width(); - const min_point = Math.max( - chartDataList[0].point - binWidth * 2, - POINTLEFTRANGE - ); - const max_point = Math.min( - chartDataList[chartDataList.length - 1].point + binWidth * 2, - POINTRIGHTRANGE - ); - const min_price = get_price_by_point(min_point); - const max_price = get_price_by_point(max_point); - const range = [+min_price, +max_price]; - return d3 - .scaleLinear() - .domain(range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxisY() { - if (chartType == 'USER') { - return scaleAxisY_User(); - } else { - return scaleAxisY_Pool(); - } - } - function scaleAxisY_Pool() { - const data: IChartData[] = process_chart_data_for_display(); - const L: number[] = []; - data.forEach((o: IChartData) => { - const { liquidity, order_liquidity } = o; - L.push( - +liquidity, - +order_liquidity, - Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() - ); - }); - const sortL = sortBy(L); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]); - } - function scaleAxisY_User() { - const { sortY: sortL } = get_price_and_liquidity_range(); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight - whole_bars_background_padding]) - .clamp(true); - } - function get_bin_width() { - const slot_num_in_a_bin = getConfig().bin; - const { point_delta } = pool; - return point_delta * slot_num_in_a_bin; - } - function get_price_and_liquidity_range() { - const Y: number[] = []; - const X: number[] = []; - const chartDataListInrange = - chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const binWidth = getConfig().bin * pool.point_delta; - chartDataListInrange.forEach((o: IChartData, index) => { - const { liquidity, point } = o; - Y.push(+liquidity); - X.push(+point); - if (index == chartDataListInrange.length - 1) { - X.push(+point + binWidth); - } - }); - const sortY = sortBy(Y); - const sortX = sortBy(X); - const sortX_Price = sortX.map((x) => { - return get_price_by_point(x); - }); - return { sortP: sortX_Price, sortY }; - } - function diffPrices(newPrice: string, peferencePrice?: string) { - let movePercent; - const price = peferencePrice || get_current_price(); - if (+price > +newPrice) { - movePercent = -Big(1) - .minus(Big(newPrice).div(price)) - .mul(100) - .toFixed(0, 1); - } else { - movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); - } - return movePercent; - } - function getConfig(): IChartItemConfig { - const { bin, range, colors, rangeGear } = - get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const percent_final = custom_config[pool_id]?.range || range; - const bin_final = custom_config[pool_id]?.bin || bin; - const colors_final = custom_config[pool_id]?.colors || colors; - const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; - return { - bin: bin_final, - range: percent_final, - colors: colors_final, - rangeGear: rangeGear_final, - }; - } - function get_current_price_by_token_x() { - if (pool) { - return formatPriceWithCommas(get_current_price()); - } - return '-'; - } - function get_current_price_by_token_y() { - if (pool) { - return formatPriceWithCommas(reverse_price(get_current_price())); - } - return '-'; - } - function zoomOut() { - const { rangeGear } = getConfig(); - const targetPercent = rangeGear.find((item) => item < zoom); - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - function zoomIn() { - const { rangeGear } = getConfig(); - const index = rangeGear.findIndex((item) => item == zoom); - let targetPercent; - if (index !== 0) { - targetPercent = rangeGear[index - 1]; - } - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - const rangeGear = getConfig().rangeGear; - const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; - const is_in_min_zoom = zoom == rangeGear[0]; - return ( -
- {/* 控件按钮*/} -
- {/*
- -
*/} -
- -
-
- -
- {/*
- -
*/} -
- - - - - - - - {/* 横坐标轴 */} - - {/* 拖拽线 left */} - - - - - - - - - - - - - - {/* 拖拽线 right */} - - - - - - - - - - - - - - {/* 左右坐标轴中间的重叠区域 */} - - - - {/* radius 模式下 target price 对应的柱子 */} - - - - - - - - - {/* hover到柱子(bin)上的悬浮框 */} -
-
- Trailing 24hr APR - - {binDetail?.feeApr} - -
-
- Price - - {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} /{' '} - {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} - -
- {binDetail?.token_x_amount ? ( - <> -
- - {pool?.token_x_metadata?.symbol} Amount - - - {binDetail.token_x_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_x_amount_in_liquidity} - -
-
-
- - by Limit Orders -
- - {binDetail.token_x_amount_in_order} - -
- - ) : null} - {binDetail?.token_y_amount ? ( - <> -
- - {pool?.token_y_metadata?.symbol} Amount - - - {binDetail.token_y_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_y_amount_in_liquidity} - -
-
-
- - by Limit Orders -
- - {binDetail.token_y_amount_in_order} - -
- - ) : null} -
-
-
- Your Liquidity - - {user_liquidities_detail?.total_value || '-'} - -
-
- Price Range - - {user_liquidities_detail?.min_price} -{' '} - {user_liquidities_detail?.max_price} - -
-
- Position - - {user_liquidities_detail?.total_x_amount}{' '} - {pool?.token_x_metadata.symbol} +{' '} - {user_liquidities_detail?.total_y_amount}{' '} - {pool?.token_y_metadata.symbol} - -
-
- Trailing 24hr APR - - {user_liquidities_detail?.apr_24 || '-'} - -
-
- Total Earned Fee - - {user_liquidities_detail?.total_earned_fee || '-'} - -
-
- {/* current 价格 */} -
-
-
-
- - {pool?.token_x_metadata?.symbol}:{' '} - - - - {pool?.token_y_metadata?.symbol} - -
-
- - {pool?.token_y_metadata?.symbol}:{' '} - - - - {pool?.token_x_metadata?.symbol} - -
-
-
-
- ); -} -// todo -function DclChartReverse({ +export default function DclChart({ pool_id, leftPoint, rightPoint, @@ -1648,7 +60,8 @@ function DclChartReverse({ config, chartType, removeParams, - newlyAddedLiquidities + newlyAddedLiquidities, + reverse, }: { pool_id: string; leftPoint?: number; @@ -1663,7 +76,8 @@ function DclChartReverse({ point?: number; all?: boolean; }; - newlyAddedLiquidities?: UserLiquidityInfo[] + newlyAddedLiquidities?: UserLiquidityInfo[]; + reverse?: boolean; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -1672,7 +86,7 @@ function DclChartReverse({ const [dragLeftPoint, setDragLeftPoint] = useState(); const [dragRightPoint, setDragRightPoint] = useState(); const [zoom, setZoom] = useState(); - const [randomId, setRandomId] = useState('.' + createRandomString()); + const [randomId] = useState('.' + createRandomString()); const [drawChartDone, setDrawChartDone] = useState(false); const [user_liquidities, set_user_liquidities] = useState< UserLiquidityInfo[] @@ -1703,8 +117,9 @@ function DclChartReverse({ appearanceConfig.disFromPercentBoxToDragBar || 2 ); const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); - const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% - // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 + + // for default left price and right price + const defaultPercent = +(appearanceConfig.defaultPercent || 10); const whole_bars_background_padding = +( appearanceConfig.whole_bars_background_padding || 20 ); @@ -1725,21 +140,14 @@ function DclChartReverse({ }, 500); } }, [pool_id]); - // init 从后端获取数据 - useEffect(() => { // =========>ok + // init get data from back end + useEffect(() => { if (pool) { get_chart_data(); } }, [pool, accountId]); - // 更新用户数据 - useEffect(() => { // ==========>todo 等会看 - if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { - const new_list = get_latest_user_chart_data(); - setChartDataList(new_list); - } - }, [newlyAddedLiquidities, user_liquidities]); - // 绘制图表 - useEffect(() => { // =========>ok + // draw chart + useEffect(() => { if ( (chartType !== 'USER' && price_range && chartDataList) || (chartType == 'USER' && chartDataList?.length) @@ -1748,7 +156,14 @@ function DclChartReverse({ setDrawChartDone(true); } }, [price_range, chartDataList]); - useEffect(() => { // =========> ok + // generate user chart + useEffect(() => { + if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { + const new_list = get_latest_user_chart_data(); + setChartDataList(new_list); + } + }, [newlyAddedLiquidities, user_liquidities]); + useEffect(() => { if ( isValid(dragLeftPoint) && !appearanceConfig.controlHidden && @@ -1756,7 +171,12 @@ function DclChartReverse({ ) { const scale = scaleAxis(); const newPoint = dragLeftPoint; - const newPrice = reverse_price(get_price_by_point(newPoint)); + let newPrice; + if (reverse) { + newPrice = reverse_price(get_price_by_point(newPoint)); + } else { + newPrice = get_price_by_point(newPoint); + } const movePercent = diffPrices(newPrice); const x = scale(+newPrice) - dragBarWidth / 2; d3.select(`${randomId} .drag-left`).attr( @@ -1792,7 +212,7 @@ function DclChartReverse({ setDragLeftPoint(leftPoint); } }, [leftPoint]); - useEffect(() => { // =========> ok + useEffect(() => { if ( isValid(dragRightPoint) && !appearanceConfig.controlHidden && @@ -1800,7 +220,12 @@ function DclChartReverse({ ) { const scale = scaleAxis(); const newPoint = dragRightPoint; - const newPrice = reverse_price(get_price_by_point(newPoint)); + let newPrice = reverse_price(get_price_by_point(newPoint)); + if (reverse) { + newPrice = reverse_price(get_price_by_point(newPoint)); + } else { + newPrice = get_price_by_point(newPoint); + } const movePercent = diffPrices(newPrice); const x = scale(+newPrice); d3.select(`${randomId} .drag-right`).attr( @@ -1832,7 +257,7 @@ function DclChartReverse({ setDragRightPoint(rightPoint); } }, [rightPoint]); - useEffect(() => { // ========> todo 待看 + useEffect(() => { if (config?.radiusMode && config?.targetPoint && drawChartDone) { // hide drag bar and show target price bar draw_radius_mode_bar(); @@ -1844,11 +269,8 @@ function DclChartReverse({ d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); } }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); - /** - * 中文 - * remove 流动性,当参数改变,重新绘制删除区域的北京框 - */ - useEffect(() => { // =======> ok + // draw remove area for user + useEffect(() => { if (removeParams && drawChartDone) { const scale = scaleAxis(); const scaleBar = scaleAxisY(); @@ -1861,11 +283,8 @@ function DclChartReverse({ removeParams?.point, drawChartDone, ]); - /** - * 获取个人流动性图表详情信息 - * 用户 hover 框里数据展示 - */ - useEffect(() => { // =======> ok + // to get user detail when hover on chart + useEffect(() => { if ( user_liquidities.length && pool && @@ -1893,7 +312,7 @@ function DclChartReverse({ if (dcl_fee_result) { const dclAccountFee: IDCLAccountFee = dcl_fee_result; const { total_earned_fee } = dclAccountFee; - // 总共赚到的fee + // total earned fee const { total_fee_x, total_fee_y } = total_earned_fee || {}; const total_earned_fee_x = toReadableNumber( token_x_metadata.decimals, @@ -1908,7 +327,7 @@ function DclChartReverse({ total_fee_earned = total_earned_fee_x_value.plus( total_earned_fee_y_value ); - // 24h 利润 + // 24h profit apr_24 = formatPercentage( get_account_24_apr(dcl_fee_result, pool, tokenPriceList) ); @@ -1960,9 +379,6 @@ function DclChartReverse({ }); return list; } - /** - * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 - */ async function get_pool_detail(pool_id: string) { const p: PoolInfo = await get_pool(pool_id); const { token_x, token_y } = p; @@ -1982,7 +398,7 @@ function DclChartReverse({ setChartDataList(list); } async function get_data_from_back_end() { - const {token_x_metadata, token_y_metadata, pool_id } = pool; + const { token_x_metadata, token_y_metadata, pool_id } = pool; const { bin: bin_final, rangeGear } = getConfig(); const [price_l, price_r] = get_price_range_by_percent(rangeGear[0], true); const point_l = get_point_by_price(price_l); @@ -2011,27 +427,37 @@ function DclChartReverse({ const marketdepthData = await get_pool_marketdepth(pool_id); const { liquidities, orders } = marketdepthData; let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); - - // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 - let range_contain_current_point: any; - liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { - const { left_point, right_point, amount_l } = l; - if (right_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['left_point'] = left_point; - range_contain_current_point['amount_l'] = amount_l; - return false; - } - if (left_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['right_point'] = right_point; - range_contain_current_point['amount_l'] = amount_l; - return false; + // to find the bin which left_point is current_point and right_point is current_point, if the bin number is two,then need to merge. + let merged_current_point_liquidity: ILiquidityInfoPool; + const contain_current_point_liquidities: ILiquidityInfoPool[] = []; + const exclude_current_point_liquidities_array = liquidities_array.filter( + (l: ILiquidityInfoPool) => { + const { left_point, right_point } = l; + if ( + right_point == pool.current_point || + left_point == pool.current_point + ) { + contain_current_point_liquidities.push(l); + return false; + } + return true; } - return true; - }); - if (range_contain_current_point) { - liquidities_array.push(range_contain_current_point); + ); + if (contain_current_point_liquidities.length == 2) { + contain_current_point_liquidities.sort( + (b: ILiquidityInfoPool, a: ILiquidityInfoPool) => { + return b.left_point - a.left_point; + } + ); + merged_current_point_liquidity = { + left_point: contain_current_point_liquidities[0].left_point, + right_point: contain_current_point_liquidities[1].right_point, + amount_l: contain_current_point_liquidities[0].amount_l, + }; + exclude_current_point_liquidities_array.push( + merged_current_point_liquidity + ); + liquidities_array = exclude_current_point_liquidities_array; } const orders_array: IOrderInfoPool[] = Object.values(orders); const pointsData_l = divide_liquidities_into_bins_pool({ @@ -2042,10 +468,7 @@ function DclChartReverse({ tokenY: token_y_metadata, poolDetail: pool, }); - list = combine_data( - pointsData_apr?.point_data, - pointsData_l - ); + list = combine_data(pointsData_apr?.point_data, pointsData_l); } return list; } @@ -2101,19 +524,42 @@ function DclChartReverse({ return pointsData; } function getChartDataListInRange() { - const price_range_min = price_range[0].toString(); - const price_range_max = price_range[price_range.length - 1].toString(); - const price_range_point_max = get_point_by_price(reverse_price(price_range_min)); - const price_range_point_min = get_point_by_price(reverse_price(price_range_max)); + const [price_range_point_min, price_range_point_max] = + get_point_range_by_price_range(); const bin_width = getConfig().bin * pool.point_delta; const chartDataListInRange = chartDataList.filter((d: IChartData) => { const { point } = d; const point_right = point + bin_width; - return point >= price_range_point_min && point_right <= price_range_point_max; + // return point >= price_range_point_min && point_right <= price_range_point_max; + return ( + point_right > price_range_point_min && point < price_range_point_max + ); }); return chartDataListInRange; } - function get_price_range_by_percent(percent: number, forward?:boolean): [string, string] { + function get_point_range_by_price_range() { + let price_range_point_min; + let price_range_point_max; + const price_range_min = price_range[0].toString(); + const price_range_max = price_range[price_range.length - 1].toString(); + if (reverse) { + price_range_point_min = get_point_by_price( + reverse_price(price_range_max) + ); + price_range_point_max = get_point_by_price( + reverse_price(price_range_min) + ); + } else { + price_range_point_min = get_point_by_price(price_range_min); + price_range_point_max = get_point_by_price(price_range_max); + } + return [price_range_point_min, price_range_point_max]; + } + + function get_price_range_by_percent( + percent: number, + forward?: boolean + ): [string, string] { const p_l_r = percent / 100; const price = get_current_price(forward); const price_l_temp = Big(1 - p_l_r).mul(price); @@ -2125,53 +571,64 @@ function DclChartReverse({ return [price_l, price_r]; } function drawChart() { - const data: IChartData[] = process_back_end_data_in_range(); // ======>ok - const scale = scaleAxis(); // ======== ok - const scaleBar = scaleAxisY(); // ====== ok + const data: IChartData[] = process_back_end_data_in_range(); + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); // down bars - draw_down_bars({ data, scale, scaleBar }); // ======== ok + draw_down_bars({ data, scale, scaleBar }); // up bars if (chartType !== 'USER') { - draw_up_bars({ data, scale, scaleBar }); // ========ok + draw_up_bars({ data, scale, scaleBar }); } - // 创建横坐标轴 + // create axis if (appearanceConfig.axisHidden) { d3.select(`${randomId} .axis`).remove(); } else { draw_axis({ scale }); } - // background bars + // draw background bars if (appearanceConfig.hoverBoxHidden) { d3.select(`${randomId} .bars_background`).remove(); d3.select(`${randomId} .overBox`).remove(); d3.select(`${randomId} .whole_bars_background`).remove(); d3.select(`${randomId} .wholeOverBox`).remove(); } else { - draw_background_bars({ data, scale, scaleBar }); // ======= ok + draw_background_bars({ data, scale, scaleBar }); } // remove select area if (chartType == 'USER' && removeParams) { - draw_background_bars_for_select_area({ scale, scaleBar }); // ======== ok + draw_background_bars_for_select_area({ scale, scaleBar }); } else { d3.select('.remove_bars_background').remove(); } - // current line - if (appearanceConfig.currentBarHidden) { - d3.select(`${randomId} .currentLine`).remove(); + // draw current line + if ( + appearanceConfig.currentBarHidden || + (chartType == 'USER' && !is_in_range()) + ) { + d3.select(`${randomId} .currentLine`).remove(); } else { - draw_current_bar({ scale }); // ======== ok + draw_current_bar({ scale }); } if (appearanceConfig.controlHidden) { remove_control(); } else { // init // drag left - draw_drag_left({ scale }); // ======== ok + draw_drag_left({ scale }); // drag right - draw_drag_right({ scale }); // ======= ok + draw_drag_right({ scale }); } } + function is_in_range() { + const { sortP } = get_price_and_liquidity_range(); + const current_price = get_current_price(); + return ( + Big(current_price).gte(sortP[0]) && + Big(current_price).lte(sortP[sortP.length - 1]) + ); + } function process_back_end_data_in_range() { const { bin: bin_final } = getConfig(); const { point_delta } = pool; @@ -2179,9 +636,18 @@ function DclChartReverse({ chartType == 'USER' ? chartDataList : getChartDataListInRange(); const data: IChartData[] = list.map((o: IChartData) => { const { point } = o; - const price_r = reverse_price(get_price_by_point(+point)); - const point_max = +point + point_delta * bin_final; - const price_l = reverse_price(get_price_by_point(point_max)); + let price_l, price_r, point_l, point_r; + if (reverse) { + point_l = +point + point_delta * bin_final; + price_l = reverse_price(get_price_by_point(point_l)); + point_r = point; + price_r = reverse_price(get_price_by_point(+point_r)); + } else { + point_l = point; + price_l = get_price_by_point(+point_l); + point_r = +point + point_delta * bin_final; + price_r = get_price_by_point(point_r); + } return { ...o, liquidity: Big(o.liquidity || 0).toFixed(), @@ -2195,8 +661,8 @@ function DclChartReverse({ price_l, price_r, - point_l: point_max, - point_r: point, + point_l, + point_r, }; }); return data; @@ -2323,7 +789,11 @@ function DclChartReverse({ }) .attr('rx', 2) .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; + if (reverse) { + return +d.point_r >= current_point ? colors[1] : colors[0]; + } else { + return +d.point_l >= current_point ? colors[0] : colors[1]; + } }); } function draw_up_bars({ @@ -2362,7 +832,11 @@ function DclChartReverse({ }) .attr('rx', 2) .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; + if (reverse) { + return +d.point_r >= current_point ? colors[1] : colors[0]; + } else { + return +d.point_l >= current_point ? colors[0] : colors[1]; + } }) .attr('opacity', '0.7'); } @@ -2414,8 +888,7 @@ function DclChartReverse({ .transition() .attr('width', function (d) { return ( - scale(Big(d.price_r).toNumber()) - - scale(Big(d.price_l).toNumber()) + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) ); }) .attr('height', function (d) { @@ -2484,7 +957,12 @@ function DclChartReverse({ const min_bin_price = sortP[0]; const max_bin_price = sortP[sortP.length - 1]; const { fromLeft, fromRight, all, point } = removeParams; - const remove_to_price = reverse_price(get_price_by_point(point)); + let remove_to_price: string; + if (reverse) { + remove_to_price = reverse_price(get_price_by_point(point)); + } else { + remove_to_price = get_price_by_point(point); + } d3.select(`${randomId} .remove_bars_background`) .attr('width', function () { @@ -2527,21 +1005,33 @@ function DclChartReverse({ ); } function draw_drag_left({ scale }: { scale: any }) { - // 初始化左的位置 const price = get_current_price(); let price_l; if (dragLeftPoint) { - price_l = reverse_price(get_price_by_point(dragLeftPoint)); + if (reverse) { + price_l = reverse_price(get_price_by_point(dragLeftPoint)); + } else { + price_l = get_price_by_point(dragLeftPoint); + } } else { const price_l_temp = Big(1 - defaultPercent / 100) .mul(price) .toFixed(); - - const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(reverse_price(price_l_temp))); - price_l = reverse_price(get_price_by_point(newLeftPoint)); - - setDragLeftPoint(newLeftPoint); + if (reverse) { + const newLeftPoint = get_nearby_bin_right_point( + get_point_by_price(reverse_price(price_l_temp)) + ); + price_l = reverse_price(get_price_by_point(newLeftPoint)); + setDragLeftPoint(newLeftPoint); + } else { + const newLeftPoint = get_nearby_bin_left_point( + get_point_by_price(price_l_temp) + ); + price_l = get_price_by_point(newLeftPoint); + setDragLeftPoint(newLeftPoint); + } } + const x = scale(price_l) - dragBarWidth / 2; d3.select(`${randomId} .drag-left`).attr( 'transform', @@ -2566,29 +1056,49 @@ function DclChartReverse({ .slice(10) ); if (rightX < e.x || e.x < dragBarWidth / 2) return; - let p = reverse_price(scale.invert(e.x)); - const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(p)); + let p; + if (reverse) { + p = reverse_price(scale.invert(e.x)); + } else { + p = scale.invert(e.x); + } + let newLeftPoint; + if (reverse) { + newLeftPoint = get_nearby_bin_right_point(get_point_by_price(p)); + } else { + newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); + } setDragLeftPoint(newLeftPoint); setLeftPoint && setLeftPoint(newLeftPoint); }); d3.select(`${randomId} .drag-left`).call(dragLeft); } function draw_drag_right({ scale }: { scale: any }) { - // 初始化右的位置 const price = get_current_price(); let price_r; if (dragRightPoint) { - price_r = reverse_price(get_price_by_point(dragRightPoint)); + if (reverse) { + price_r = reverse_price(get_price_by_point(dragRightPoint)); + } else { + price_r = get_price_by_point(dragRightPoint); + } } else { const price_r_temp = Big(1 + defaultPercent / 100) .mul(price) .toFixed(); - - const newRightPoint = get_nearby_bin_left_point( - get_point_by_price(reverse_price(price_r_temp)) - ) - price_r = reverse_price(get_price_by_point(newRightPoint)); - setDragRightPoint(newRightPoint); + if (reverse) { + const newRightPoint = get_nearby_bin_left_point( + get_point_by_price(reverse_price(price_r_temp)) + ); + price_r = reverse_price(get_price_by_point(newRightPoint)); + setDragRightPoint(newRightPoint); + } else { + const newRightPoint = get_nearby_bin_right_point( + get_point_by_price(price_r_temp) + ); + price_r = get_price_by_point(newRightPoint); + setDragRightPoint(newRightPoint); + } } const x = scale(price_r); d3.select(`${randomId} .drag-right`).attr( @@ -2610,8 +1120,18 @@ function DclChartReverse({ ); const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; - const p = scale.invert(e.x); - const newRightPoint = get_nearby_bin_left_point(get_point_by_price(reverse_price(p))); + let p; + if (reverse) { + p = reverse_price(scale.invert(e.x)); + } else { + p = scale.invert(e.x); + } + let newRightPoint; + if (reverse) { + newRightPoint = get_nearby_bin_left_point(get_point_by_price(p)); + } else { + newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); + } setDragRightPoint(newRightPoint); setRightPoint && setRightPoint(newRightPoint); }); @@ -2626,12 +1146,12 @@ function DclChartReverse({ .attr('transform', `translate(${x}, -${axisHeight})`) .attr('style', 'display:block'); } - function get_current_price(forward?:boolean) { + function get_current_price(forward?: boolean) { const current_price = get_price_by_point(pool.current_point); - if (forward) { - return current_price; - } else { + if (reverse && !forward) { return reverse_price(current_price); + } else { + return current_price; } } function get_point_by_price(price: string) { @@ -2718,23 +1238,6 @@ function DclChartReverse({ return scaleAxisY_Pool(); } } - function scaleAxisY_Pool() { - const data: IChartData[] = process_back_end_data_in_range(); - const L: number[] = []; - data.forEach((o: IChartData) => { - const { liquidity, order_liquidity } = o; - L.push( - +liquidity, - +order_liquidity, - Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() - ); - }); - const sortL = sortBy(L); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]); - } function scaleAxisY_User() { const { sortY: sortL } = get_price_and_liquidity_range(); return d3 @@ -2743,20 +1246,30 @@ function DclChartReverse({ .range([0, wholeBarHeight - whole_bars_background_padding]) .clamp(true); } - function get_bin_width() { - const slot_num_in_a_bin = getConfig().bin; - const { point_delta } = pool; - return point_delta * slot_num_in_a_bin; + function scaleAxisY_Pool() { + const { sortY: sortL } = get_price_and_liquidity_range(); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); } + function get_price_and_liquidity_range() { const Y: number[] = []; const X: number[] = []; const list = process_back_end_data_in_range(); list.forEach((o: IChartData) => { - const { liquidity, price_l, price_r } = o; + const { liquidity, order_liquidity, price_l, price_r } = o; Y.push(+liquidity); - X.push(+price_l, +price_r) + X.push(+price_l, +price_r); + if (chartType !== 'USER') { + Y.push( + +order_liquidity, + Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() + ); + } }); + // X.push(+get_current_price()); const sortY = sortBy(Y); const sortX = sortBy(X); return { sortP: sortX, sortY }; @@ -2837,14 +1350,8 @@ function DclChartReverse({ : 'hidden' } ${randomId.slice(1)}`} > - {/* 控件按钮*/} + {/* control button area*/}
- {/*
- -
*/}
- {/* 横坐标轴 */} + {/* axis */} - {/* 拖拽线 left */} + {/* drag left bar */} - {/* 拖拽线 right */} + {/* drag right bar*/} - {/* 左右坐标轴中间的重叠区域 */} + {/* overlap area between drag left and drag right */} - {/* radius 模式下 target price 对应的柱子 */} + {/* show bar in radius mode */} - {/* hover到柱子(bin)上的悬浮框 */} + {/* show hover box then hover on the bin */}
Trailing 24hr APR @@ -3022,7 +1529,7 @@ function DclChartReverse({
Price - {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} /{' '} + {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} /{' '} {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol}
@@ -3044,7 +1551,9 @@ function DclChartReverse({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.colors[1]}`, + backgroundColor: `${ + reverse ? binDetail?.colors[1] : binDetail?.colors[0] + }`, }} > in Liquidity @@ -3061,7 +1570,9 @@ function DclChartReverse({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.colors[1]}`, + backgroundColor: `${ + reverse ? binDetail?.colors[1] : binDetail?.colors[0] + }`, }} > by Limit Orders @@ -3090,7 +1601,9 @@ function DclChartReverse({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.colors[0]}`, + backgroundColor: `${ + reverse ? binDetail?.colors[0] : binDetail?.colors[1] + }`, }} > in Liquidity @@ -3107,7 +1620,9 @@ function DclChartReverse({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${binDetail?.colors[0]}`, + backgroundColor: `${ + reverse ? binDetail?.colors[0] : binDetail?.colors[1] + }`, }} > by Limit Orders @@ -3155,20 +1670,26 @@ function DclChartReverse({
- {/* current 价格 */} + {/* current price area */}
-
+
{pool?.token_x_metadata?.symbol}:{' '} {pool?.token_y_metadata?.symbol} @@ -3179,7 +1700,9 @@ function DclChartReverse({ {pool?.token_x_metadata?.symbol} @@ -3193,44 +1716,6 @@ function isValid(n: number) { if (n !== undefined && n !== null) return true; return false; } -function LeftArrowIcon(props: any) { - return ( - - - - ); -} -function RightArrowIcon(props: any) { - return ( - - - - ); -} function AddIcon(props: any) { return ( + + + ); +} +function RightArrowIcon(props: any) { + return ( + + + + ); +} diff --git a/src/components/d3Chart/DclChartCopy.tsx b/src/components/d3Chart/DclChartCopy.tsx new file mode 100644 index 000000000..1824d6428 --- /dev/null +++ b/src/components/d3Chart/DclChartCopy.tsx @@ -0,0 +1,3321 @@ +import React, { useState, useRef, useEffect } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { isMobile } from '../../utils/device'; +import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; +import { + get_pool, + PoolInfo, + list_liquidities, + get_pool_marketdepth, +} from '../../services/swapV3'; +import { + getPriceByPoint, + getPointByPrice, + POINTLEFTRANGE, + POINTRIGHTRANGE, + divide_liquidities_into_bins_user, + UserLiquidityInfo, + getBinPointByPoint, + get_x_y_amount_by_condition, + get_account_24_apr, + divide_liquidities_into_bins_pool, + get_token_amount_in_user_liquidities, + reverse_price, +} from '../../services/commonV3'; +import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; +import { sortBy, debounce } from 'lodash'; +import { + IChartData, + IChartItemConfig, + IChartConfig, + IBinDetail, + IPoolChartConfig, + IUserLiquiditiesDetail, + IDCLAccountFee, + IRMTYPE, + IDclChartProps, +} from './interfaces'; +import { + formatPrice, + formatNumber, + formatPercentage, + formatWithCommas_usd, + formatWithCommas_number, + formatPriceWithCommas, +} from './utils'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, +} from './config'; +import Big from 'big.js'; +import * as d3 from 'd3'; +import { useWalletSelector } from '../../context/WalletSelectorContext'; +import { getBoostTokenPrices } from '../../services/farm'; +import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; +import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; +export default function DclChart(props: IDclChartProps) { + if (props.reverse) { + return ; + } else { + return ; + } +} + +function DclChartForward({ + pool_id, + leftPoint, + rightPoint, + setLeftPoint, + setRightPoint, + config, + chartType, + removeParams, + newlyAddedLiquidities, +}: IDclChartProps) { + const [pool, setPool] = useState(); + const [price_range, set_price_range] = useState(); + const [chartDataList, setChartDataList] = useState(); + const [binDetail, setBinDetail] = useState(); + const [dragLeftPoint, setDragLeftPoint] = useState(); + const [dragRightPoint, setDragRightPoint] = useState(); + const [zoom, setZoom] = useState(); + const [randomId, setRandomId] = useState('.' + createRandomString()); + const [drawChartDone, setDrawChartDone] = useState(false); + const [user_liquidities, set_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + const [user_liquidities_detail, set_user_liquidities_detail] = + useState(); + const [tokenPriceList, setTokenPriceList] = useState>(); + /** constant start */ + const appearanceConfig: IPoolChartConfig = config || {}; + let [timerObj, setTimerObj] = useState({ + timer: '', + }); + const dragBarWidth = 28; + const percentBoxWidth = 44; + const min_bar_height = 2; + const svgWidth = +(appearanceConfig.svgWidth || 520); + const svgHeight = +(appearanceConfig.svgHeight || 250); + const axisHeight = appearanceConfig.axisHidden + ? appearanceConfig.controlHidden + ? 0 + : 5 + : 21; + const wholeBarHeight = svgHeight - axisHeight; + const disFromHoverBoxToPointer = +( + appearanceConfig.disFromPercentBoxToDragBar || 20 + ); + const disFromPercentBoxToDragBar = +( + appearanceConfig.disFromPercentBoxToDragBar || 2 + ); + const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); + const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 + const whole_bars_background_padding = +( + appearanceConfig.whole_bars_background_padding || 20 + ); + /** constant end */ + const { accountId } = useWalletSelector(); + useEffect(() => { + // get all token prices + getBoostTokenPrices().then((result) => { + setTokenPriceList(result); + }); + }, []); + // init + useEffect(() => { + clearTimeout(timerObj.timer); + if (pool_id) { + timerObj.timer = setTimeout(() => { + get_pool_detail(pool_id); + }, 500); + } + }, [pool_id]); + // init 从后端获取数据 + useEffect(() => { + if (pool) { + get_chart_data(); + } + }, [pool, accountId]); + // 更新用户数据 + useEffect(() => { + if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { + const new_list = get_latest_user_chart_data(); + setChartDataList(new_list); + } + }, [newlyAddedLiquidities, user_liquidities]); + // 绘制图表 + useEffect(() => { + if ( + (chartType !== 'USER' && price_range && chartDataList) || + (chartType == 'USER' && chartDataList?.length) + ) { + drawChart(); + setDrawChartDone(true); + } + }, [price_range, chartDataList]); + useEffect(() => { + if ( + isValid(dragLeftPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragLeftPoint; + const newPrice = get_price_by_point(newPoint); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - + (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const W = rightX - x - dragBarWidth / 2; + d3.select(`${randomId} .overlap rect`) + .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) + .attr('width', W); + } + }, [dragLeftPoint, price_range, drawChartDone]); + useEffect(() => { + if (isValid(leftPoint)) { + setDragLeftPoint(leftPoint); + } + }, [leftPoint]); + useEffect(() => { + if ( + isValid(dragRightPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragRightPoint; + const newPrice = get_price_by_point(newPoint); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr( + 'transform', + `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + + const leftX = Number( + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const W = x - leftX - dragBarWidth / 2; + d3.select(`${randomId} .overlap rect`).attr('width', W); + } + }, [dragRightPoint, price_range, drawChartDone]); + useEffect(() => { + if (isValid(rightPoint)) { + setDragRightPoint(rightPoint); + } + }, [rightPoint]); + useEffect(() => { + if (config?.radiusMode && config?.targetPoint && drawChartDone) { + // hide drag bar and show target price bar + draw_radius_mode_bar(); + d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); + d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); + } else { + d3.select(`${randomId} .leftBar`).attr('style', ''); + d3.select(`${randomId} .rightBar`).attr('style', ''); + d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); + } + }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); + /** + * 中文 + * remove 流动性,当参数改变,重新绘制删除区域的北京框 + */ + useEffect(() => { + if (removeParams && drawChartDone) { + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + draw_background_bars_for_select_area({ scale, scaleBar }); + } + }, [ + removeParams?.all, + removeParams?.fromLeft, + removeParams?.fromRight, + removeParams?.point, + drawChartDone, + ]); + /** + * 获取个人流动性图表详情信息 + * 用户 hover 框里数据展示 + */ + useEffect(() => { + if ( + user_liquidities.length && + pool && + chartType == 'USER' && + tokenPriceList + ) { + get_user_liquidities_detail(); + } + }, [user_liquidities, pool, chartType, tokenPriceList]); + async function get_user_liquidities_detail() { + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + + const points: number[] = []; + let total_x_amount = Big(0); + let total_y_amount = Big(0); + let total_value = Big(0); + let total_fee_earned = Big(0); + let apr_24 = ''; + if (dcl_fee_result) { + const dclAccountFee: IDCLAccountFee = dcl_fee_result; + const { total_earned_fee } = dclAccountFee; + // 总共赚到的fee + const { total_fee_x, total_fee_y } = total_earned_fee || {}; + const total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + const total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value.plus( + total_earned_fee_y_value + ); + // 24h 利润 + apr_24 = formatPercentage( + get_account_24_apr(dcl_fee_result, pool, tokenPriceList) + ); + } + user_liquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + points.push(left_point, right_point); + }); + const [total_token_x_amount, total_token_y_amount] = + get_token_amount_in_user_liquidities({ + user_liquidities, + pool, + token_x_metadata, + token_y_metadata, + }); + + total_x_amount = total_x_amount.plus(total_token_x_amount); + total_y_amount = total_y_amount.plus(total_token_y_amount); + + const total_x_value = Big(price_x).mul(total_x_amount); + const total_y_value = Big(price_y).mul(total_y_amount); + + total_value = total_x_value.plus(total_y_value); + points.sort((b: number, a: number) => { + return b - a; + }); + const min_point = points[0]; + const max_point = points[points.length - 1]; + const min_price = get_price_by_point(min_point); + const max_price = get_price_by_point(max_point); + set_user_liquidities_detail({ + total_value: formatWithCommas_usd(total_value.toFixed()), + min_price: formatPrice(min_price), + max_price: formatPrice(max_price), + total_x_amount: formatNumber(total_x_amount.toFixed()), + total_y_amount: formatNumber(total_y_amount.toFixed()), + apr_24, + total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), + }); + } + function get_latest_user_chart_data() { + const { token_x_metadata, token_y_metadata } = pool; + const { bin: bin_final } = getConfig(); + const nfts = user_liquidities.concat(newlyAddedLiquidities || []); + const list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + return list; + } + /** + * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 + */ + async function get_pool_detail(pool_id: string) { + const p: PoolInfo = await get_pool(pool_id); + const { token_x, token_y } = p; + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + setPool(p); + } + async function get_chart_data() { + const { range } = getConfig(); + const list = await get_data_from_back_end(); + if (chartType !== 'USER') { + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + } + setChartDataList(list); + } + async function get_data_from_back_end() { + const { point_delta, token_x_metadata, token_y_metadata, pool_id } = pool; + const { bin: bin_final, rangeGear } = getConfig(); + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); + const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); + const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); + let list = []; + if (chartType == 'USER' && accountId) { + const liquidities = await list_liquidities(); + const nfts = liquidities.filter((l: UserLiquidityInfo) => { + return l.pool_id == pool_id; + }); + set_user_liquidities(nfts); + list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + } else { + const pointsData_apr = await getDclPoolPoints( + pool_id, + bin_final, + point_l, + point_r + ); + const marketdepthData = await get_pool_marketdepth(pool_id); + const { liquidities, orders } = marketdepthData; + let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); + + /** + * 去找 left_point左点位是当前点位,右点位也是当前点位的数据,如果有两条,这需要把这两条数据合并,否则不动。 + */ + let merged_current_point_liquidity: ILiquidityInfoPool; + const contain_current_point_liquidities: ILiquidityInfoPool[] = []; + const exclude_current_point_liquidities_array = liquidities_array.filter( + (l: ILiquidityInfoPool) => { + const { left_point, right_point } = l; + if ( + right_point == pool.current_point || + left_point == pool.current_point + ) { + contain_current_point_liquidities.push(l); + return false; + } + return true; + } + ); + if (contain_current_point_liquidities.length == 2) { + contain_current_point_liquidities.sort( + (b: ILiquidityInfoPool, a: ILiquidityInfoPool) => { + return b.left_point - a.left_point; + } + ); + merged_current_point_liquidity = { + left_point: contain_current_point_liquidities[0].left_point, + right_point: contain_current_point_liquidities[1].right_point, + amount_l: contain_current_point_liquidities[0].amount_l, + }; + exclude_current_point_liquidities_array.push( + merged_current_point_liquidity + ); + liquidities_array = exclude_current_point_liquidities_array; + } + const orders_array: IOrderInfoPool[] = Object.values(orders); + const pointsData_l = divide_liquidities_into_bins_pool({ + liquidities: liquidities_array, + orders: orders_array, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + console.log('pointsData_l', pointsData_l); + console.log('pointsData_apr', pointsData_apr.point_data); + const binWidth = bin_final * point_delta; + list = combine_data( + pointsData_apr?.point_data, + pointsData_l, + point_l, + point_r, + binWidth + ); + // list = pointsData_apr.point_data; + } + return list; + } + function combine_data( + pointsData_apr: IChartData[], + pointsData_l: IChartData[], + min_point: number, + max_point: number, + binWidth: number + ) { + const pointsData: IChartData[] = []; + const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + const pointsData_l_inRange = pointsData_l?.filter((d: IChartData) => { + return d.point >= min_point && d.point + binWidth <= max_point; + }); + const pointsData_l_map = pointsData_l_inRange?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + if (pointsData_l_map) { + Object.keys(pointsData_l_map).forEach((point_l: string) => { + const { + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + point, + pool_id, + } = pointsData_l_map[point_l]; + + const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; + + pointsData.push({ + fee: fee || '0', + total_liquidity: total_liquidity || '0', + pool_id, + point, + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + }); + pointsData.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + }); + } + return pointsData; + } + function getChartDataListInRange() { + const point_l = get_point_by_price(price_range[0].toString()); + const point_r = get_point_by_price( + price_range[price_range.length - 1].toString() + ); + const bin_width = getConfig().bin * pool.point_delta; + const chartDataListInRange = chartDataList.filter((d: IChartData) => { + const { point } = d; + const point_right = point + bin_width; + return point_right > point_l && point < point_r; + }); + return chartDataListInRange; + } + function get_price_range_by_percent(percent: number): [string, string] { + const p_l_r = percent / 100; + const price = get_current_price(); + const price_l_temp = Big(1 - p_l_r).mul(price); + const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); + const price_r = Big(1 + p_l_r) + .mul(price) + .toFixed(); + + return [price_l, price_r]; + } + function drawChart() { + const data: IChartData[] = process_back_end_data_in_range(); + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + // down bars + draw_down_bars({ data, scale, scaleBar }); + // up bars + if (chartType !== 'USER') { + draw_up_bars({ data, scale, scaleBar }); + } + // 创建横坐标轴 + if (appearanceConfig.axisHidden) { + d3.select(`${randomId} .axis`).remove(); + } else { + draw_axis({ scale }); + } + // background bars + if (appearanceConfig.hoverBoxHidden) { + d3.select(`${randomId} .bars_background`).remove(); + d3.select(`${randomId} .overBox`).remove(); + d3.select(`${randomId} .whole_bars_background`).remove(); + d3.select(`${randomId} .wholeOverBox`).remove(); + } else { + draw_background_bars({ data, scale, scaleBar }); + } + // remove select area + if (chartType == 'USER' && removeParams) { + draw_background_bars_for_select_area({ scale, scaleBar }); + } else { + d3.select('.remove_bars_background').remove(); + } + + // current line + if (appearanceConfig.currentBarHidden) { + d3.select(`${randomId} .currentLine`).remove(); + } else { + draw_current_bar({ scale }); + } + if (appearanceConfig.controlHidden) { + remove_control(); + } else { + // init + // drag left + draw_drag_left({ scale }); + // drag right + draw_drag_right({ scale }); + } + } + function process_back_end_data_in_range() { + const { bin: bin_final } = getConfig(); + const { token_x_metadata, token_y_metadata, point_delta } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + const list = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const data: IChartData[] = list.map((o: IChartData) => { + const { point } = o; + const price_l = getPriceByPoint(+point, decimalRate_price); + const point_r = +point + point_delta * bin_final; + const price_r = getPriceByPoint(point_r, decimalRate_price); + + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + price: price_l.toString(), + price_r: price_r.toString(), + point_r: point_r, + }; + }); + return data; + } + function hoverBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = + d; + const { colors } = getConfig(); + + const total_token_x = Big(token_x).plus(order_x); + const total_token_y = Big(token_y).plus(order_y); + const price_by_token_x = get_price_by_point(+point); + const price_by_token_y = reverse_price(price_by_token_x); + const apr = Big(total_liquidity).gt(0) + ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() + : '0'; + setBinDetail({ + feeApr: formatPercentage(apr), + colors, + ...(total_token_x.gt(0) + ? { + token_x_amount: formatWithCommas_number(total_token_x.toFixed()), + token_x_amount_in_liquidity: formatWithCommas_number(token_x), + token_x_amount_in_order: formatWithCommas_number(order_x), + } + : {}), + ...(total_token_y.gt(0) + ? { + token_y_amount: formatWithCommas_number(total_token_y.toFixed()), + token_y_amount_in_liquidity: formatWithCommas_number(token_y), + token_y_amount_in_order: formatWithCommas_number(order_y), + } + : {}), + price_by_token_x: formatPrice(price_by_token_x), + price_by_token_y: formatPrice(price_by_token_y), + }); + } + function LeaveBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function hoverUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function LeaveUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function remove_control() { + d3.select(`${randomId} .control`).remove(); + d3.select(`${randomId} .overlap`).remove(); + d3.select(`${randomId} .rightBar`).remove(); + d3.select(`${randomId} .leftBar`).remove(); + } + function draw_axis({ scale }: { scale: any }) { + const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); + if (appearanceConfig.ticks || chartType == 'USER') { + axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { + const dBig = new Big(d); + if (dBig.gte(10000)) { + return dBig.toFixed(0); + } else { + return d; + } + }); + } + d3.select(`${randomId} svg .axis`) + .call(axis) + .selectAll('text') + .attr('fill', '#7E8A93'); + d3.select(`${randomId} svg .axis`) + .attr('transform', `translate(0, ${svgHeight - axisHeight})`) + .select('.domain') + .attr('stroke', 'transparent'); + } + function draw_down_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { current_point } = pool; + const { colors } = getConfig(); + d3.select(`${randomId} .bars_liquidity`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }); + } + function draw_up_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { colors } = getConfig(); + const { current_point } = pool; + d3.select(`${randomId} .bars_order`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.order_liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return ( + wholeBarHeight - + get_final_bar_height(scaleBar(+d.liquidity)) - + get_final_bar_height(scaleBar(+d.order_liquidity)) + ); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }) + .attr('opacity', '0.7'); + } + function get_final_bar_height(h: number) { + if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) + return min_bar_height; + return h; + } + function draw_background_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + if (chartType == 'USER') { + draw_background_bars_user({ + scale, + scaleBar, + }); + } else { + draw_background_bars_pool({ + data, + scale, + }); + } + } + function draw_background_bars_pool({ + data, + scale, + }: { + data: IChartData[]; + scale: Function; + }) { + d3.select(`${randomId} .bars_background`) + .selectAll('rect') + .data(data) + .join('rect') + .on('mousemove', function (e, d) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverBox(e, d); + }) + .on('mouseleave', function (e, d) { + d3.select(this).attr('fill', 'transparent'); + LeaveBox(e, d); + }) + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) + ); + }) + .attr('height', function (d) { + return wholeBarHeight; + }) + .attr('x', function (d) { + return scale(Big(d.price).toNumber()); + }) + .attr('y', function (d) { + return 0; + }) + .attr('rx', 2) + .attr('fill', 'transparent'); + } + function draw_background_bars_user({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + d3.select(`${randomId} .whole_bars_background`) + .on('mousemove', function (e) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverUserBox(e); + }) + .on('mouseleave', function (e) { + d3.select(this).attr('fill', 'transparent'); + LeaveUserBox(e); + }) + .transition() + .attr('width', function () { + return ( + scale(sortP[sortP.length - 1]) - + scale(sortP[0]) + + whole_bars_background_padding * 2 + ); + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + return scale(sortP[0]) - whole_bars_background_padding; + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'transparent'); + } + function draw_background_bars_for_select_area({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + const min_bin_price = sortP[0]; + const max_bin_price = sortP[sortP.length - 1]; + const { fromLeft, fromRight, all, point } = removeParams; + d3.select(`${randomId} .remove_bars_background`) + .attr('width', function () { + if (all) { + return scale(max_bin_price) - scale(min_bin_price); + } else if (fromLeft) { + return scale(get_price_by_point(point)) - scale(min_bin_price); + } else if (fromRight) { + return scale(max_bin_price) - scale(get_price_by_point(point)); + } + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + if (fromRight) { + return scale(get_price_by_point(point)); + } else { + return scale(min_bin_price); + } + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'rgba(255,255,255,0.1)'); + } + function draw_current_bar({ scale }: { scale: Function }) { + d3.select(`${randomId} .currentLine`).attr( + 'style', + `transform:translate(${ + scale(+get_current_price()) + svgPaddingX + }px, -${axisHeight}px)` + ); + } + function draw_drag_left({ scale }: { scale: any }) { + // 初始化左的位置 + const price = get_current_price(); + let price_l; + if (dragLeftPoint) { + price_l = get_price_by_point(dragLeftPoint); + } else { + const price_l_temp = Big(1 - defaultPercent / 100) + .mul(price) + .toFixed(); + const newLeftPoint = get_nearby_bin_left_point( + get_point_by_price(price_l_temp) + ); + price_l = get_price_by_point(newLeftPoint); + setDragLeftPoint(newLeftPoint); + } + const x = scale(price_l) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(`${diffPrices(price_l)}%`) + .attr('fill', 'white'); + const dragLeft = d3.drag().on('drag', function (e) { + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + if (rightX < e.x || e.x < dragBarWidth / 2) return; + const p = scale.invert(e.x); + const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); + + setDragLeftPoint(newLeftPoint); + setLeftPoint && setLeftPoint(newLeftPoint); + }); + d3.select(`${randomId} .drag-left`).call(dragLeft); + } + function draw_drag_right({ scale }: { scale: any }) { + // 初始化右的位置 + const price = get_current_price(); + let price_r; + if (dragRightPoint) { + price_r = get_price_by_point(dragRightPoint); + } else { + const price_r_temp = Big(1 + defaultPercent / 100) + .mul(price) + .toFixed(); + const newRightPoint = get_nearby_bin_right_point( + get_point_by_price(price_r_temp) + ); + price_r = get_price_by_point(newRightPoint); + setDragRightPoint(newRightPoint); + } + const x = scale(price_r); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) + .select('text') + .text(`${diffPrices(price_r)}%`) + .attr('fill', 'white'); + const dragRight = d3.drag().on('drag', (e) => { + const leftX = Number( + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); + if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; + const p = scale.invert(e.x); + const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); + setDragRightPoint(newRightPoint); + setRightPoint && setRightPoint(newRightPoint); + }); + d3.select(`${randomId} .drag-right`).call(dragRight); + } + function draw_radius_mode_bar() { + const scale: any = scaleAxis(); + const { targetPoint } = config; + const price = get_price_by_point(targetPoint); + const x = scale(price); + d3.select(`${randomId} .radiusBar`) + .attr('transform', `translate(${x}, -${axisHeight})`) + .attr('style', 'display:block'); + } + function get_current_price() { + return get_price_by_point(pool.current_point); + } + function get_point_by_price(price: string) { + const { point_delta, token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_nearby_bin_right_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin + slots; + } + function get_nearby_bin_left_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin; + } + function clickToLeft() { + const { bin } = getConfig(); + const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); + setDragLeftPoint(newPoint_nearby_bin); + setLeftPoint && setLeftPoint(newPoint_nearby_bin); + } + function clickToRight() { + const { bin } = getConfig(); + const newPoint = dragRightPoint + pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); + setDragRightPoint(newPoint_nearby_bin); + setRightPoint && setRightPoint(newPoint_nearby_bin); + } + function get_bin_point_by_point(point: number, type?: IRMTYPE) { + const { point_delta } = pool; + const slot_num = getConfig().bin; + return getBinPointByPoint(point_delta, slot_num, point, type); + } + function scaleAxis() { + if (chartType == 'USER') { + return scaleAxis_User(); + } else { + return scaleAxis_Pool(); + } + } + function scaleAxis_Pool() { + return d3 + .scaleLinear() + .domain(price_range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxis_User() { + const binWidth = get_bin_width(); + const min_point = Math.max( + chartDataList[0].point - binWidth * 2, + POINTLEFTRANGE + ); + const max_point = Math.min( + chartDataList[chartDataList.length - 1].point + binWidth * 2, + POINTRIGHTRANGE + ); + const min_price = get_price_by_point(min_point); + const max_price = get_price_by_point(max_point); + const range = [+min_price, +max_price]; + return d3 + .scaleLinear() + .domain(range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxisY() { + if (chartType == 'USER') { + return scaleAxisY_User(); + } else { + return scaleAxisY_Pool(); + } + } + function scaleAxisY_Pool() { + const data: IChartData[] = process_back_end_data_in_range(); + const L: number[] = []; + data.forEach((o: IChartData) => { + const { liquidity, order_liquidity } = o; + L.push( + +liquidity, + +order_liquidity, + Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() + ); + }); + const sortL = sortBy(L); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); + } + function scaleAxisY_User() { + const { sortY: sortL } = get_price_and_liquidity_range(); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true); + } + function get_bin_width() { + const slot_num_in_a_bin = getConfig().bin; + const { point_delta } = pool; + return point_delta * slot_num_in_a_bin; + } + function get_price_and_liquidity_range() { + // todo + const Y: number[] = []; + const X: number[] = []; + const chartDataListInrange = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const binWidth = getConfig().bin * pool.point_delta; + chartDataListInrange.forEach((o: IChartData, index) => { + const { liquidity, point } = o; + Y.push(+liquidity); + X.push(+point); + if (index == chartDataListInrange.length - 1) { + X.push(+point + binWidth); + } + }); + const sortY = sortBy(Y); + const sortX = sortBy(X); + const sortX_Price = sortX.map((x) => { + return get_price_by_point(x); + }); + return { sortP: sortX_Price, sortY }; + } + function diffPrices(newPrice: string, peferencePrice?: string) { + let movePercent; + const price = peferencePrice || get_current_price(); + if (+price > +newPrice) { + movePercent = -Big(1) + .minus(Big(newPrice).div(price)) + .mul(100) + .toFixed(0, 1); + } else { + movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); + } + return movePercent; + } + function getConfig(): IChartItemConfig { + const { bin, range, colors, rangeGear } = + get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const percent_final = custom_config[pool_id]?.range || range; + const bin_final = custom_config[pool_id]?.bin || bin; + const colors_final = custom_config[pool_id]?.colors || colors; + const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; + return { + bin: bin_final, + range: percent_final, + colors: colors_final, + rangeGear: rangeGear_final, + }; + } + function get_current_price_by_token_x() { + if (pool) { + return formatPriceWithCommas(get_current_price()); + } + return '-'; + } + function get_current_price_by_token_y() { + if (pool) { + return formatPriceWithCommas(reverse_price(get_current_price())); + } + return '-'; + } + function zoomOut() { + const { rangeGear } = getConfig(); + const targetPercent = rangeGear.find((item) => item < zoom); + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + function zoomIn() { + const { rangeGear } = getConfig(); + const index = rangeGear.findIndex((item) => item == zoom); + let targetPercent; + if (index !== 0) { + targetPercent = rangeGear[index - 1]; + } + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + const rangeGear = getConfig().rangeGear; + const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; + const is_in_min_zoom = zoom == rangeGear[0]; + return ( +
+ {/* 控件按钮*/} +
+ {/*
+ +
*/} +
+ +
+
+ +
+ {/*
+ +
*/} +
+ + + + + + + + {/* 横坐标轴 */} + + {/* 拖拽线 left */} + + + + + + + + + + + + + + {/* 拖拽线 right */} + + + + + + + + + + + + + + {/* 左右坐标轴中间的重叠区域 */} + + + + {/* radius 模式下 target price 对应的柱子 */} + + + + + + + + + {/* hover到柱子(bin)上的悬浮框 */} +
+
+ Trailing 24hr APR + + {binDetail?.feeApr} + +
+
+ Price + + {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} /{' '} + {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} + +
+ {binDetail?.token_x_amount ? ( + <> +
+ + {pool?.token_x_metadata?.symbol} Amount + + + {binDetail.token_x_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_x_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_x_amount_in_order} + +
+ + ) : null} + {binDetail?.token_y_amount ? ( + <> +
+ + {pool?.token_y_metadata?.symbol} Amount + + + {binDetail.token_y_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_y_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_y_amount_in_order} + +
+ + ) : null} +
+
+
+ Your Liquidity + + {user_liquidities_detail?.total_value || '-'} + +
+
+ Price Range + + {user_liquidities_detail?.min_price} -{' '} + {user_liquidities_detail?.max_price} + +
+
+ Position + + {user_liquidities_detail?.total_x_amount}{' '} + {pool?.token_x_metadata.symbol} +{' '} + {user_liquidities_detail?.total_y_amount}{' '} + {pool?.token_y_metadata.symbol} + +
+
+ Trailing 24hr APR + + {user_liquidities_detail?.apr_24 || '-'} + +
+
+ Total Earned Fee + + {user_liquidities_detail?.total_earned_fee || '-'} + +
+
+ {/* current 价格 */} +
+
+
+
+ + {pool?.token_x_metadata?.symbol}:{' '} + + + + {pool?.token_y_metadata?.symbol} + +
+
+ + {pool?.token_y_metadata?.symbol}:{' '} + + + + {pool?.token_x_metadata?.symbol} + +
+
+
+
+ ); +} +function DclChartReverse({ + pool_id, + leftPoint, + rightPoint, + setLeftPoint, + setRightPoint, + config, + chartType, + removeParams, + newlyAddedLiquidities, +}: { + pool_id: string; + leftPoint?: number; + rightPoint?: number; + setLeftPoint?: Function; + setRightPoint?: Function; + config?: IPoolChartConfig; + chartType?: 'POOL' | 'USER'; + removeParams?: { + fromLeft?: boolean; + fromRight?: boolean; + point?: number; + all?: boolean; + }; + newlyAddedLiquidities?: UserLiquidityInfo[]; +}) { + const [pool, setPool] = useState(); + const [price_range, set_price_range] = useState(); + const [chartDataList, setChartDataList] = useState(); + const [binDetail, setBinDetail] = useState(); + const [dragLeftPoint, setDragLeftPoint] = useState(); + const [dragRightPoint, setDragRightPoint] = useState(); + const [zoom, setZoom] = useState(); + const [randomId, setRandomId] = useState('.' + createRandomString()); + const [drawChartDone, setDrawChartDone] = useState(false); + const [user_liquidities, set_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + const [user_liquidities_detail, set_user_liquidities_detail] = + useState(); + const [tokenPriceList, setTokenPriceList] = useState>(); + /** constant start */ + const appearanceConfig: IPoolChartConfig = config || {}; + let [timerObj, setTimerObj] = useState({ + timer: '', + }); + const dragBarWidth = 28; + const percentBoxWidth = 44; + const min_bar_height = 2; + const svgWidth = +(appearanceConfig.svgWidth || 520); + const svgHeight = +(appearanceConfig.svgHeight || 250); + const axisHeight = appearanceConfig.axisHidden + ? appearanceConfig.controlHidden + ? 0 + : 5 + : 21; + const wholeBarHeight = svgHeight - axisHeight; + const disFromHoverBoxToPointer = +( + appearanceConfig.disFromPercentBoxToDragBar || 20 + ); + const disFromPercentBoxToDragBar = +( + appearanceConfig.disFromPercentBoxToDragBar || 2 + ); + const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); + const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% + // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 + const whole_bars_background_padding = +( + appearanceConfig.whole_bars_background_padding || 20 + ); + /** constant end */ + const { accountId } = useWalletSelector(); + useEffect(() => { + // get all token prices + getBoostTokenPrices().then((result) => { + setTokenPriceList(result); + }); + }, []); + // init + useEffect(() => { + clearTimeout(timerObj.timer); + if (pool_id) { + timerObj.timer = setTimeout(() => { + get_pool_detail(pool_id); + }, 500); + } + }, [pool_id]); + // init 从后端获取数据 + useEffect(() => { + // =========>ok + if (pool) { + get_chart_data(); + } + }, [pool, accountId]); + // 更新用户数据 + useEffect(() => { + // ==========>todo 等会看 + if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { + const new_list = get_latest_user_chart_data(); + setChartDataList(new_list); + } + }, [newlyAddedLiquidities, user_liquidities]); + // 绘制图表 + useEffect(() => { + // =========>ok + if ( + (chartType !== 'USER' && price_range && chartDataList) || + (chartType == 'USER' && chartDataList?.length) + ) { + drawChart(); + setDrawChartDone(true); + } + }, [price_range, chartDataList]); + useEffect(() => { + // =========> ok + if ( + isValid(dragLeftPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragLeftPoint; + const newPrice = reverse_price(get_price_by_point(newPoint)); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - + (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const W = rightX - x - dragBarWidth / 2; + d3.select(`${randomId} .overlap rect`) + .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) + .attr('width', W); + } + }, [dragLeftPoint, price_range, drawChartDone]); + useEffect(() => { + if (isValid(leftPoint)) { + setDragLeftPoint(leftPoint); + } + }, [leftPoint]); + useEffect(() => { + // =========> ok + if ( + isValid(dragRightPoint) && + !appearanceConfig.controlHidden && + drawChartDone + ) { + const scale = scaleAxis(); + const newPoint = dragRightPoint; + const newPrice = reverse_price(get_price_by_point(newPoint)); + const movePercent = diffPrices(newPrice); + const x = scale(+newPrice); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr( + 'transform', + `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` + ) + .select('text') + .text(movePercent + '%') + .attr('fill', 'white'); + + const leftX = Number( + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const W = x - leftX - dragBarWidth / 2; + d3.select(`${randomId} .overlap rect`).attr('width', W); + } + }, [dragRightPoint, price_range, drawChartDone]); + useEffect(() => { + if (isValid(rightPoint)) { + setDragRightPoint(rightPoint); + } + }, [rightPoint]); + useEffect(() => { + // ========> todo 待看 + if (config?.radiusMode && config?.targetPoint && drawChartDone) { + // hide drag bar and show target price bar + draw_radius_mode_bar(); + d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); + d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); + } else { + d3.select(`${randomId} .leftBar`).attr('style', ''); + d3.select(`${randomId} .rightBar`).attr('style', ''); + d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); + } + }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); + /** + * 中文 + * remove 流动性,当参数改变,重新绘制删除区域的北京框 + */ + useEffect(() => { + // =======> ok + if (removeParams && drawChartDone) { + const scale = scaleAxis(); + const scaleBar = scaleAxisY(); + draw_background_bars_for_select_area({ scale, scaleBar }); + } + }, [ + removeParams?.all, + removeParams?.fromLeft, + removeParams?.fromRight, + removeParams?.point, + drawChartDone, + ]); + /** + * 获取个人流动性图表详情信息 + * 用户 hover 框里数据展示 + */ + useEffect(() => { + // =======> ok + if ( + user_liquidities.length && + pool && + chartType == 'USER' && + tokenPriceList + ) { + get_user_liquidities_detail(); + } + }, [user_liquidities, pool, chartType, tokenPriceList]); + async function get_user_liquidities_detail() { + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + + const points: number[] = []; + let total_x_amount = Big(0); + let total_y_amount = Big(0); + let total_value = Big(0); + let total_fee_earned = Big(0); + let apr_24 = ''; + if (dcl_fee_result) { + const dclAccountFee: IDCLAccountFee = dcl_fee_result; + const { total_earned_fee } = dclAccountFee; + // 总共赚到的fee + const { total_fee_x, total_fee_y } = total_earned_fee || {}; + const total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + const total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value.plus( + total_earned_fee_y_value + ); + // 24h 利润 + apr_24 = formatPercentage( + get_account_24_apr(dcl_fee_result, pool, tokenPriceList) + ); + } + user_liquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point, amount } = l; + points.push(left_point, right_point); + const { total_x, total_y } = get_x_y_amount_by_condition({ + left_point, + right_point, + amount, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + total_x_amount = total_x_amount.plus(total_x); + total_y_amount = total_y_amount.plus(total_y); + }); + const total_x_value = Big(price_x).mul(total_x_amount); + const total_y_value = Big(price_y).mul(total_y_amount); + total_value = total_x_value.plus(total_y_value); + points.sort((b: number, a: number) => { + return b - a; + }); + const min_point = points[0]; + const max_point = points[points.length - 1]; + const min_price = reverse_price(get_price_by_point(max_point)); + const max_price = reverse_price(get_price_by_point(min_point)); + set_user_liquidities_detail({ + total_value: formatWithCommas_usd(total_value.toFixed()), + min_price: formatPrice(min_price), + max_price: formatPrice(max_price), + total_x_amount: formatNumber(total_x_amount.toFixed()), + total_y_amount: formatNumber(total_y_amount.toFixed()), + apr_24, + total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), + }); + } + function get_latest_user_chart_data() { + const { token_x_metadata, token_y_metadata } = pool; + const { bin: bin_final } = getConfig(); + const nfts = user_liquidities.concat(newlyAddedLiquidities || []); + const list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + return list; + } + /** + * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 + */ + async function get_pool_detail(pool_id: string) { + const p: PoolInfo = await get_pool(pool_id); + const { token_x, token_y } = p; + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + setPool(p); + } + async function get_chart_data() { + const { range } = getConfig(); + const list = await get_data_from_back_end(); + if (chartType !== 'USER') { + const [price_l_default, price_r_default] = + get_price_range_by_percent(range); + set_price_range([+price_l_default, +price_r_default]); + setZoom(range); + } + setChartDataList(list); + } + async function get_data_from_back_end() { + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const { bin: bin_final, rangeGear } = getConfig(); + const [price_l, price_r] = get_price_range_by_percent(rangeGear[0], true); + const point_l = get_point_by_price(price_l); + const point_r = get_point_by_price(price_r); + let list = []; + if (chartType == 'USER' && accountId) { + const liquidities = await list_liquidities(); + const nfts = liquidities.filter((l: UserLiquidityInfo) => { + return l.pool_id == pool_id; + }); + set_user_liquidities(nfts); + list = divide_liquidities_into_bins_user({ + liquidities: nfts, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + } else { + const pointsData_apr = await getDclPoolPoints( + pool_id, + bin_final, + point_l, + point_r + ); + const marketdepthData = await get_pool_marketdepth(pool_id); + const { liquidities, orders } = marketdepthData; + let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); + + // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 + let range_contain_current_point: any; + liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { + const { left_point, right_point, amount_l } = l; + if (right_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['left_point'] = left_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + if (left_point == pool.current_point) { + range_contain_current_point = range_contain_current_point || {}; + range_contain_current_point['right_point'] = right_point; + range_contain_current_point['amount_l'] = amount_l; + return false; + } + return true; + }); + if (range_contain_current_point) { + liquidities_array.push(range_contain_current_point); + } + const orders_array: IOrderInfoPool[] = Object.values(orders); + const pointsData_l = divide_liquidities_into_bins_pool({ + liquidities: liquidities_array, + orders: orders_array, + slot_number_in_a_bin: bin_final, + tokenX: token_x_metadata, + tokenY: token_y_metadata, + poolDetail: pool, + }); + list = combine_data(pointsData_apr?.point_data, pointsData_l); + } + return list; + } + function combine_data( + pointsData_apr: IChartData[], + pointsData_l: IChartData[] + ) { + const pointsData: IChartData[] = []; + const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + const pointsData_l_map = pointsData_l?.reduce((acc, cur) => { + return { + ...acc, + [cur.point]: cur, + }; + }, {}); + if (pointsData_l_map) { + Object.keys(pointsData_l_map).forEach((point_l: string) => { + const { + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + point, + pool_id, + } = pointsData_l_map[point_l]; + + const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; + + pointsData.push({ + fee: fee || '0', + total_liquidity: total_liquidity || '0', + pool_id, + point, + liquidity, + token_x, + token_y, + order_liquidity, + order_x, + order_y, + }); + pointsData.sort((b: IChartData, a: IChartData) => { + return b.point - a.point; + }); + }); + } + return pointsData; + } + function getChartDataListInRange() { + const price_range_min = price_range[0].toString(); + const price_range_max = price_range[price_range.length - 1].toString(); + const price_range_point_max = get_point_by_price( + reverse_price(price_range_min) + ); + const price_range_point_min = get_point_by_price( + reverse_price(price_range_max) + ); + const bin_width = getConfig().bin * pool.point_delta; + const chartDataListInRange = chartDataList.filter((d: IChartData) => { + const { point } = d; + const point_right = point + bin_width; + return ( + point >= price_range_point_min && point_right <= price_range_point_max + ); + }); + return chartDataListInRange; + } + function get_price_range_by_percent( + percent: number, + forward?: boolean + ): [string, string] { + const p_l_r = percent / 100; + const price = get_current_price(forward); + const price_l_temp = Big(1 - p_l_r).mul(price); + const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); + const price_r = Big(1 + p_l_r) + .mul(price) + .toFixed(); + + return [price_l, price_r]; + } + function drawChart() { + const data: IChartData[] = process_back_end_data_in_range(); // ======>ok + const scale = scaleAxis(); // ======== ok + const scaleBar = scaleAxisY(); // ====== ok + // down bars + draw_down_bars({ data, scale, scaleBar }); // ======== ok + // up bars + if (chartType !== 'USER') { + draw_up_bars({ data, scale, scaleBar }); // ========ok + } + // 创建横坐标轴 + if (appearanceConfig.axisHidden) { + d3.select(`${randomId} .axis`).remove(); + } else { + draw_axis({ scale }); + } + // background bars + if (appearanceConfig.hoverBoxHidden) { + d3.select(`${randomId} .bars_background`).remove(); + d3.select(`${randomId} .overBox`).remove(); + d3.select(`${randomId} .whole_bars_background`).remove(); + d3.select(`${randomId} .wholeOverBox`).remove(); + } else { + draw_background_bars({ data, scale, scaleBar }); // ======= ok + } + // remove select area + if (chartType == 'USER' && removeParams) { + draw_background_bars_for_select_area({ scale, scaleBar }); // ======== ok + } else { + d3.select('.remove_bars_background').remove(); + } + + // current line + if (appearanceConfig.currentBarHidden) { + d3.select(`${randomId} .currentLine`).remove(); + } else { + draw_current_bar({ scale }); // ======== ok + } + if (appearanceConfig.controlHidden) { + remove_control(); + } else { + // init + // drag left + draw_drag_left({ scale }); // ======== ok + // drag right + draw_drag_right({ scale }); // ======= ok + } + } + function process_back_end_data_in_range() { + const { bin: bin_final } = getConfig(); + const { point_delta } = pool; + const list = + chartType == 'USER' ? chartDataList : getChartDataListInRange(); + const data: IChartData[] = list.map((o: IChartData) => { + const { point } = o; + const price_r = reverse_price(get_price_by_point(+point)); + const point_max = +point + point_delta * bin_final; + const price_l = reverse_price(get_price_by_point(point_max)); + return { + ...o, + liquidity: Big(o.liquidity || 0).toFixed(), + order_liquidity: Big(o.order_liquidity || 0).toFixed(), + token_x: Big(o.token_x || 0).toFixed(), + token_y: Big(o.token_y || 0).toFixed(), + order_x: Big(o.order_x || 0).toFixed(), + order_y: Big(o.order_y || 0).toFixed(), + total_liquidity: Big(o.total_liquidity || 0).toFixed(), + fee: Big(o.fee || 0).toFixed(), + + price_l, + price_r, + point_l: point_max, + point_r: point, + }; + }); + return data; + } + function hoverBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = + d; + const { colors } = getConfig(); + + const total_token_x = Big(token_x).plus(order_x); + const total_token_y = Big(token_y).plus(order_y); + const price_by_token_x = get_price_by_point(+point); + const price_by_token_y = reverse_price(price_by_token_x); + const apr = Big(total_liquidity).gt(0) + ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() + : '0'; + setBinDetail({ + feeApr: formatPercentage(apr), + colors, + ...(total_token_x.gt(0) + ? { + token_x_amount: formatWithCommas_number(total_token_x.toFixed()), + token_x_amount_in_liquidity: formatWithCommas_number(token_x), + token_x_amount_in_order: formatWithCommas_number(order_x), + } + : {}), + ...(total_token_y.gt(0) + ? { + token_y_amount: formatWithCommas_number(total_token_y.toFixed()), + token_y_amount_in_liquidity: formatWithCommas_number(token_y), + token_y_amount_in_order: formatWithCommas_number(order_y), + } + : {}), + price_by_token_x: formatPrice(price_by_token_x), + price_by_token_y: formatPrice(price_by_token_y), + }); + } + function LeaveBox(e: any, d: IChartData) { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function hoverUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function LeaveUserBox(e: any) { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + function remove_control() { + d3.select(`${randomId} .control`).remove(); + d3.select(`${randomId} .overlap`).remove(); + d3.select(`${randomId} .rightBar`).remove(); + d3.select(`${randomId} .leftBar`).remove(); + } + function draw_axis({ scale }: { scale: any }) { + const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); + if (appearanceConfig.ticks || chartType == 'USER') { + axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { + const dBig = new Big(d); + if (dBig.gte(10000)) { + return dBig.toFixed(0); + } else { + return d; + } + }); + } + d3.select(`${randomId} svg .axis`) + .call(axis) + .selectAll('text') + .attr('fill', '#7E8A93'); + d3.select(`${randomId} svg .axis`) + .attr('transform', `translate(0, ${svgHeight - axisHeight})`) + .select('.domain') + .attr('stroke', 'transparent'); + } + function draw_down_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { current_point } = pool; + const { colors } = getConfig(); + d3.select(`${randomId} .bars_liquidity`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }); + } + function draw_up_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + const { colors } = getConfig(); + const { current_point } = pool; + d3.select(`${randomId} .bars_order`) + .selectAll('rect') + .data(data) + .join('rect') + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return get_final_bar_height(scaleBar(+d.order_liquidity)); + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return ( + wholeBarHeight - + get_final_bar_height(scaleBar(+d.liquidity)) - + get_final_bar_height(scaleBar(+d.order_liquidity)) + ); + }) + .attr('rx', 2) + .attr('fill', function (d) { + return +d.point >= current_point ? colors[1] : colors[0]; + }) + .attr('opacity', '0.7'); + } + function get_final_bar_height(h: number) { + if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) + return min_bar_height; + return h; + } + function draw_background_bars({ + data, + scale, + scaleBar, + }: { + data: IChartData[]; + scale: Function; + scaleBar: Function; + }) { + if (chartType == 'USER') { + draw_background_bars_user({ + scale, + scaleBar, + }); + } else { + draw_background_bars_pool({ + data, + scale, + }); + } + } + function draw_background_bars_pool({ + data, + scale, + }: { + data: IChartData[]; + scale: Function; + }) { + d3.select(`${randomId} .bars_background`) + .selectAll('rect') + .data(data) + .join('rect') + .on('mousemove', function (e, d) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverBox(e, d); + }) + .on('mouseleave', function (e, d) { + d3.select(this).attr('fill', 'transparent'); + LeaveBox(e, d); + }) + .transition() + .attr('width', function (d) { + return ( + scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) + ); + }) + .attr('height', function (d) { + return wholeBarHeight; + }) + .attr('x', function (d) { + return scale(Big(d.price_l).toNumber()); + }) + .attr('y', function (d) { + return 0; + }) + .attr('rx', 2) + .attr('fill', 'transparent'); + } + function draw_background_bars_user({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + d3.select(`${randomId} .whole_bars_background`) + .on('mousemove', function (e) { + d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); + hoverUserBox(e); + }) + .on('mouseleave', function (e) { + d3.select(this).attr('fill', 'transparent'); + LeaveUserBox(e); + }) + .transition() + .attr('width', function () { + return ( + scale(sortP[sortP.length - 1]) - + scale(sortP[0]) + + whole_bars_background_padding * 2 + ); + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + return scale(sortP[0]) - whole_bars_background_padding; + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'transparent'); + } + function draw_background_bars_for_select_area({ + scale, + scaleBar, + }: { + scale: Function; + scaleBar: Function; + }) { + const { sortP, sortY } = get_price_and_liquidity_range(); + const min_bin_price = sortP[0]; + const max_bin_price = sortP[sortP.length - 1]; + const { fromLeft, fromRight, all, point } = removeParams; + const remove_to_price = reverse_price(get_price_by_point(point)); + + d3.select(`${randomId} .remove_bars_background`) + .attr('width', function () { + if (all) { + return scale(max_bin_price) - scale(min_bin_price); + } else if (fromLeft) { + return scale(remove_to_price) - scale(min_bin_price); + } else if (fromRight) { + return scale(max_bin_price) - scale(remove_to_price); + } + }) + .attr('height', function () { + return ( + scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding + ); + }) + .attr('x', function () { + if (fromRight) { + return scale(remove_to_price); + } else { + return scale(min_bin_price); + } + }) + .attr('y', function () { + return ( + wholeBarHeight - + scaleBar(sortY[sortY.length - 1]) - + whole_bars_background_padding + ); + }) + .attr('rx', 4) + .attr('fill', 'rgba(255,255,255,0.1)'); + } + function draw_current_bar({ scale }: { scale: Function }) { + d3.select(`${randomId} .currentLine`).attr( + 'style', + `transform:translate(${ + scale(+get_current_price()) + svgPaddingX + }px, -${axisHeight}px)` + ); + } + function draw_drag_left({ scale }: { scale: any }) { + // 初始化左的位置 + const price = get_current_price(); + let price_l; + if (dragLeftPoint) { + price_l = reverse_price(get_price_by_point(dragLeftPoint)); + } else { + const price_l_temp = Big(1 - defaultPercent / 100) + .mul(price) + .toFixed(); + + const newLeftPoint = get_nearby_bin_right_point( + get_point_by_price(reverse_price(price_l_temp)) + ); + price_l = reverse_price(get_price_by_point(newLeftPoint)); + + setDragLeftPoint(newLeftPoint); + } + const x = scale(price_l) - dragBarWidth / 2; + d3.select(`${randomId} .drag-left`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentLeft`) + .attr( + 'transform', + `translate(${ + x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) + }, 0)` + ) + .select('text') + .text(`${diffPrices(price_l)}%`) + .attr('fill', 'white'); + const dragLeft = d3.drag().on('drag', function (e) { + const rightX = Number( + d3 + .select(`${randomId} .drag-right`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + if (rightX < e.x || e.x < dragBarWidth / 2) return; + let p = reverse_price(scale.invert(e.x)); + const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(p)); + setDragLeftPoint(newLeftPoint); + setLeftPoint && setLeftPoint(newLeftPoint); + }); + d3.select(`${randomId} .drag-left`).call(dragLeft); + } + function draw_drag_right({ scale }: { scale: any }) { + // 初始化右的位置 + const price = get_current_price(); + let price_r; + if (dragRightPoint) { + price_r = reverse_price(get_price_by_point(dragRightPoint)); + } else { + const price_r_temp = Big(1 + defaultPercent / 100) + .mul(price) + .toFixed(); + + const newRightPoint = get_nearby_bin_left_point( + get_point_by_price(reverse_price(price_r_temp)) + ); + price_r = reverse_price(get_price_by_point(newRightPoint)); + setDragRightPoint(newRightPoint); + } + const x = scale(price_r); + d3.select(`${randomId} .drag-right`).attr( + 'transform', + `translate(${x}, -${axisHeight})` + ); + d3.select(`${randomId} .percentRight`) + .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) + .select('text') + .text(`${diffPrices(price_r)}%`) + .attr('fill', 'white'); + const dragRight = d3.drag().on('drag', (e) => { + const leftX = Number( + d3 + .select(`${randomId} .drag-left`) + .attr('transform') + .split(',')[0] + .slice(10) + ); + const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); + if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; + const p = scale.invert(e.x); + const newRightPoint = get_nearby_bin_left_point( + get_point_by_price(reverse_price(p)) + ); + setDragRightPoint(newRightPoint); + setRightPoint && setRightPoint(newRightPoint); + }); + d3.select(`${randomId} .drag-right`).call(dragRight); + } + function draw_radius_mode_bar() { + const scale: any = scaleAxis(); + const { targetPoint } = config; + const price = get_price_by_point(targetPoint); + const x = scale(price); + d3.select(`${randomId} .radiusBar`) + .attr('transform', `translate(${x}, -${axisHeight})`) + .attr('style', 'display:block'); + } + function get_current_price(forward?: boolean) { + const current_price = get_price_by_point(pool.current_point); + if (forward) { + return current_price; + } else { + return reverse_price(current_price); + } + } + function get_point_by_price(price: string) { + const { point_delta, token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_price = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_nearby_bin_right_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin + slots; + } + function get_nearby_bin_left_point(p: number) { + const { point_delta } = pool; + const slots = getConfig().bin * point_delta; + const point_int_bin = Math.round(p / slots) * slots; + if (point_int_bin < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_bin > POINTRIGHTRANGE) { + return 800000; + } + return point_int_bin; + } + function clickToLeft() { + const { bin } = getConfig(); + const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); + setDragLeftPoint(newPoint_nearby_bin); + setLeftPoint && setLeftPoint(newPoint_nearby_bin); + } + function clickToRight() { + const { bin } = getConfig(); + const newPoint = dragRightPoint + pool.point_delta * (bin + 1); + const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); + setDragRightPoint(newPoint_nearby_bin); + setRightPoint && setRightPoint(newPoint_nearby_bin); + } + function get_bin_point_by_point(point: number, type?: IRMTYPE) { + const { point_delta } = pool; + const slot_num = getConfig().bin; + return getBinPointByPoint(point_delta, slot_num, point, type); + } + function scaleAxis() { + if (chartType == 'USER') { + return scaleAxis_User(); + } else { + return scaleAxis_Pool(); + } + } + function scaleAxis_User() { + const { sortP } = get_price_and_liquidity_range(); + const range = [+sortP[0], +sortP[sortP.length - 1]]; + return d3 + .scaleLinear() + .domain(range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxis_Pool() { + return d3 + .scaleLinear() + .domain(price_range) + .range([0, svgWidth - svgPaddingX * 2]); + } + function scaleAxisY() { + if (chartType == 'USER') { + return scaleAxisY_User(); + } else { + return scaleAxisY_Pool(); + } + } + function scaleAxisY_Pool() { + const data: IChartData[] = process_back_end_data_in_range(); + const L: number[] = []; + data.forEach((o: IChartData) => { + const { liquidity, order_liquidity } = o; + L.push( + +liquidity, + +order_liquidity, + Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() + ); + }); + const sortL = sortBy(L); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight]); + } + function scaleAxisY_User() { + const { sortY: sortL } = get_price_and_liquidity_range(); + return d3 + .scaleLinear() + .domain([0, +sortL[sortL.length - 1]]) + .range([0, wholeBarHeight - whole_bars_background_padding]) + .clamp(true); + } + function get_bin_width() { + const slot_num_in_a_bin = getConfig().bin; + const { point_delta } = pool; + return point_delta * slot_num_in_a_bin; + } + function get_price_and_liquidity_range() { + const Y: number[] = []; + const X: number[] = []; + const list = process_back_end_data_in_range(); + list.forEach((o: IChartData) => { + const { liquidity, price_l, price_r } = o; + Y.push(+liquidity); + X.push(+price_l, +price_r); + }); + X.push(+get_current_price()); + const sortY = sortBy(Y); + const sortX = sortBy(X); + return { sortP: sortX, sortY }; + } + function diffPrices(newPrice: string, peferencePrice?: string) { + let movePercent; + const price = peferencePrice || get_current_price(); + if (+price > +newPrice) { + movePercent = -Big(1) + .minus(Big(newPrice).div(price)) + .mul(100) + .toFixed(0, 1); + } else { + movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); + } + return movePercent; + } + function getConfig(): IChartItemConfig { + const { bin, range, colors, rangeGear } = + get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const percent_final = custom_config[pool_id]?.range || range; + const bin_final = custom_config[pool_id]?.bin || bin; + const colors_final = custom_config[pool_id]?.colors || colors; + const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; + return { + bin: bin_final, + range: percent_final, + colors: colors_final, + rangeGear: rangeGear_final, + }; + } + function get_current_price_by_token_x() { + if (pool) { + return formatPriceWithCommas(get_current_price()); + } + return '-'; + } + function get_current_price_by_token_y() { + if (pool) { + return formatPriceWithCommas(reverse_price(get_current_price())); + } + return '-'; + } + function zoomOut() { + const { rangeGear } = getConfig(); + const targetPercent = rangeGear.find((item) => item < zoom); + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + function zoomIn() { + const { rangeGear } = getConfig(); + const index = rangeGear.findIndex((item) => item == zoom); + let targetPercent; + if (index !== 0) { + targetPercent = rangeGear[index - 1]; + } + if (targetPercent) { + const [new_left_price, new_right_price] = + get_price_range_by_percent(targetPercent); + set_price_range([+new_left_price, +new_right_price]); + setZoom(targetPercent); + } + } + const rangeGear = getConfig().rangeGear; + const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; + const is_in_min_zoom = zoom == rangeGear[0]; + return ( +
+ {/* 控件按钮*/} +
+ {/*
+ +
*/} +
+ +
+
+ +
+ {/*
+ +
*/} +
+ + + + + + + + {/* 横坐标轴 */} + + {/* 拖拽线 left */} + + + + + + + + + + + + + + {/* 拖拽线 right */} + + + + + + + + + + + + + + {/* 左右坐标轴中间的重叠区域 */} + + + + {/* radius 模式下 target price 对应的柱子 */} + + + + + + + + + {/* hover到柱子(bin)上的悬浮框 */} +
+
+ Trailing 24hr APR + + {binDetail?.feeApr} + +
+
+ Price + + {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} /{' '} + {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} + +
+ {binDetail?.token_x_amount ? ( + <> +
+ + {pool?.token_x_metadata?.symbol} Amount + + + {binDetail.token_x_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_x_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_x_amount_in_order} + +
+ + ) : null} + {binDetail?.token_y_amount ? ( + <> +
+ + {pool?.token_y_metadata?.symbol} Amount + + + {binDetail.token_y_amount} + +
+
+
+ + in Liquidity +
+ + {binDetail.token_y_amount_in_liquidity} + +
+
+
+ + by Limit Orders +
+ + {binDetail.token_y_amount_in_order} + +
+ + ) : null} +
+
+
+ Your Liquidity + + {user_liquidities_detail?.total_value || '-'} + +
+
+ Price Range + + {user_liquidities_detail?.min_price} -{' '} + {user_liquidities_detail?.max_price} + +
+
+ Position + + {user_liquidities_detail?.total_x_amount}{' '} + {pool?.token_x_metadata.symbol} +{' '} + {user_liquidities_detail?.total_y_amount}{' '} + {pool?.token_y_metadata.symbol} + +
+
+ Trailing 24hr APR + + {user_liquidities_detail?.apr_24 || '-'} + +
+
+ Total Earned Fee + + {user_liquidities_detail?.total_earned_fee || '-'} + +
+
+ {/* current 价格 */} +
+
+
+
+ + {pool?.token_y_metadata?.symbol} + + + + {pool?.token_x_metadata?.symbol} + +
+
+ + {pool?.token_x_metadata?.symbol} + + + + {pool?.token_y_metadata?.symbol} + +
+
+
+
+ ); +} +function isValid(n: number) { + if (n !== undefined && n !== null) return true; + return false; +} +function LeftArrowIcon(props: any) { + return ( + + + + ); +} +function RightArrowIcon(props: any) { + return ( + + + + ); +} +function AddIcon(props: any) { + return ( + + + + + ); +} +function SubIcon(props: any) { + return ( + + + + ); +} +function createRandomChar(c = 'a-z') { + switch (c) { + case 'A-Z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 65); + case 'a-z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 97); + case '0-9': + default: + return String.fromCharCode(Math.trunc(Math.random() * 10) + 48); + } +} +function createRandomString(length = 4) { + let str = ''; + for (let i = 0; i < length; i++) str += createRandomChar(); + return str; +} diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index 149dd2b72..9bcd27d19 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -4,7 +4,36 @@ export function get_custom_config_for_chart(): IChartConfig { if (env == 'pub-testnet') { return {}; } else if (env == 'testnet') { - return {}; + return { + 'phoenix-bonds.testnet|wrap.testnet|2000': { + bin: 2, + range: 20, + rangeGear: [100, 80, 60, 40, 20, 10], + colors: ['#A36949', '#717C84'], + }, + 'usdc.fakes.testnet|wrap.testnet|2000': { + bin: 10, + range: 100, + rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], + colors: ['#707C84', '#2775CA'], + }, + 'aurora.fakes.testnet|usdc.fakes.testnet|2000': + // AURORA<>USDC.e@2000 + { + bin: 10, + range: 100, + rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], + colors: ['#79BD84', '#2775CA'], + }, + 'eth.fakes.testnet|usdc.fakes.testnet|2000': + // ETH<>USDC.e@2000 + { + bin: 4, + range: 50, + rangeGear: [130, 110, 90, 70, 50, 30, 20, 10], + colors: ['#626CA3', '#2775CA'], + }, + }; } else { return { 'phoenix-bonds.near|wrap.near|2000': @@ -75,6 +104,5 @@ export function get_default_config_for_chart(): IChartItemConfig { } } -export const SLIDER_BIN_NUMBER = 20; export const RADIUS_DEFAULT_NUMBER = 3; export const max_nft_divisional_per_side = 3; diff --git a/src/components/d3Chart/interfaces.tsx b/src/components/d3Chart/interfaces.tsx index 1d6a0ec2a..df359c52b 100644 --- a/src/components/d3Chart/interfaces.tsx +++ b/src/components/d3Chart/interfaces.tsx @@ -5,15 +5,12 @@ export interface IChartData { token_x: string; token_y: string; liquidity: string; - price?: string; - + price_l?: string; price_r?: string; - point_l?:number; + point_l?: number; point_r?: number; - - price_l_reverse?:string; - price_r_reverse?:string; + order_x?: string; order_y?: string; order_liquidity?: string; @@ -118,4 +115,4 @@ export interface IDclChartProps { }; newlyAddedLiquidities?: UserLiquidityInfo[]; reverse?: boolean; -} \ No newline at end of file +} diff --git a/src/components/forms/SelectToken.tsx b/src/components/forms/SelectToken.tsx index 9429f8c6f..b1c126225 100644 --- a/src/components/forms/SelectToken.tsx +++ b/src/components/forms/SelectToken.tsx @@ -853,9 +853,7 @@ export function SelectTokenDCL({ const renderList = renderPools?.map((p) => { const { token_x_metadata, token_y_metadata } = p; - const tokens = notNeedSortToken - ? [token_x_metadata, token_y_metadata] - : sort_tokens_by_base([token_x_metadata, token_y_metadata]); + const tokens = sort_tokens_by_base([token_x_metadata, token_y_metadata]); return (
{ - - return - // return - -}; -/** - * - * @param props 正向操作 - * @returns - */ -export const RemovePoolV3Forward = ({ props }: any) => { const { tokenMetadata_x_y, poolDetail, @@ -88,8 +73,8 @@ export const RemovePoolV3Forward = ({ props }: any) => { } = props; const SLOT_NUMBER = get_slot_number_in_a_bin(); const [slippageTolerance, setSlippageTolerance] = useState(0.5); - - // const tokens = sort_tokens_by_base(tokenMetadata_x_y);// todo + const pair_is_reverse = + sort_tokens_by_base(tokenMetadata_x_y)[0].id !== tokenMetadata_x_y[0].id; const tokens = tokenMetadata_x_y; const { decimals: token_x_decimals } = tokens[0]; const { decimals: token_y_decimals } = tokens[1]; @@ -98,7 +83,6 @@ export const RemovePoolV3Forward = ({ props }: any) => { const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; - // new const [minPoint, setMinPoint] = useState(); const [maxPoint, setMaxPoint] = useState(); const [minPrice, setMinPrice] = useState(''); @@ -119,7 +103,12 @@ export const RemovePoolV3Forward = ({ props }: any) => { }, [tokens, poolDetail, listLiquidities]); useEffect(() => { if (minBoxPoint && maxBoxPoint) { - const bin_amount = get_bin_amount_by_points(minBoxPoint, maxBoxPoint); + let bin_amount; + if (pair_is_reverse) { + bin_amount = get_bin_amount_by_points(maxBoxPoint, minBoxPoint); + } else { + bin_amount = get_bin_amount_by_points(minBoxPoint, maxBoxPoint); + } setBinBoxAmount(bin_amount); } }, [minBoxPoint, maxBoxPoint]); @@ -162,23 +151,42 @@ export const RemovePoolV3Forward = ({ props }: any) => { } else if (removeType == 'left') { listLiquidities.forEach((l: UserLiquidityInfo) => { const { left_point, right_point } = l; - if (left_point < maxBoxPoint) { - // 之前:left_point <= maxBoxPoint - if (right_point <= maxBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); + if (pair_is_reverse) { + if (right_point > maxBoxPoint) { + if (left_point >= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + } else { + if (left_point < maxBoxPoint) { + if (right_point <= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } } } }); } else if (removeType == 'right') { listLiquidities.forEach((l: UserLiquidityInfo) => { const { left_point, right_point } = l; - if (right_point > minBoxPoint) { - if (left_point >= minBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); + if (pair_is_reverse) { + if (left_point < minBoxPoint) { + if (right_point <= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + } else { + if (right_point > minBoxPoint) { + if (left_point >= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } } } }); @@ -206,8 +214,14 @@ export const RemovePoolV3Forward = ({ props }: any) => { user_points[user_points.length - 1], 'ceil' ); - const min_price = get_bin_price_by_point(min_point); - const max_price = get_bin_price_by_point(max_point); + let min_price, max_price; + if (pair_is_reverse) { + min_price = reverse_price(get_bin_price_by_point(max_point)); + max_price = reverse_price(get_bin_price_by_point(min_point)); + } else { + min_price = get_bin_price_by_point(min_point); + max_price = get_bin_price_by_point(max_point); + } const max_bin_amount = get_bin_amount_by_points(min_point, max_point); setMinPoint(min_point); @@ -218,10 +232,19 @@ export const RemovePoolV3Forward = ({ props }: any) => { setMinBoxPrice(min_price); setMaxBoxPrice(max_price); - setMinBoxPoint(min_point); - setMaxBoxPoint(max_point); + if (pair_is_reverse) { + setMinBoxPoint(max_point); + setMaxBoxPoint(min_point); + } else { + setMinBoxPoint(min_point); + setMaxBoxPoint(max_point); + } setBinBoxAmount(max_bin_amount); } + function reverse_price(price: string) { + if (Big(price).eq(0)) return '0'; + return Big(1).div(price).toFixed(); + } function get_bin_amount_by_points(left_point: number, right_point: number) { const { point_delta } = poolDetail; const binWidth = SLOT_NUMBER * point_delta; @@ -265,13 +288,24 @@ export const RemovePoolV3Forward = ({ props }: any) => { const big_price = Big(minBoxPrice || 0); if (big_price.lt(minPrice)) { appropriate_price = minPrice; - appropriate_point = minPoint; + if (pair_is_reverse) { + appropriate_point = maxPoint; + } else { + appropriate_point = minPoint; + } } else if (big_price.gt(maxBoxPrice)) { appropriate_price = maxBoxPrice; appropriate_point = maxBoxPoint; } else { - appropriate_point = get_bin_point_by_price(minBoxPrice); - appropriate_price = get_bin_price_by_point(appropriate_point); + if (pair_is_reverse) { + appropriate_point = get_bin_point_by_price(reverse_price(minBoxPrice)); + appropriate_price = reverse_price( + get_bin_price_by_point(appropriate_point) + ); + } else { + appropriate_point = get_bin_point_by_price(minBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); + } } setMinBoxPrice(appropriate_price); setMinBoxPoint(appropriate_point); @@ -288,10 +322,21 @@ export const RemovePoolV3Forward = ({ props }: any) => { appropriate_point = minBoxPoint; } else if (big_price.gt(maxPrice)) { appropriate_price = maxPrice; - appropriate_point = maxPoint; + if (pair_is_reverse) { + appropriate_point = minPoint; + } else { + appropriate_point = maxPoint; + } } else { - appropriate_point = get_bin_point_by_price(maxBoxPrice); - appropriate_price = get_bin_price_by_point(appropriate_point); + if (pair_is_reverse) { + appropriate_point = get_bin_point_by_price(reverse_price(maxBoxPrice)); + appropriate_price = reverse_price( + get_bin_price_by_point(appropriate_point) + ); + } else { + appropriate_point = get_bin_point_by_price(maxBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); + } } setMaxBoxPrice(appropriate_price); setMaxBoxPoint(appropriate_point); @@ -301,8 +346,8 @@ export const RemovePoolV3Forward = ({ props }: any) => { * bin amount 修改会改变可以修改的点位 * 0 <= bin amount < max bin amount */ - function handleBinAmountToAppropriateAmount(point: number) { - const amount_int = point || +binBoxAmount; + function handleBinAmountToAppropriateAmount(binAmount: number) { + const amount_int = binAmount || +binBoxAmount; const { point_delta } = poolDetail; const binWidth = SLOT_NUMBER * point_delta; let appropriate_amount = amount_int; @@ -310,13 +355,27 @@ export const RemovePoolV3Forward = ({ props }: any) => { appropriate_amount = +maxBinAmount; } if (removeType == 'left') { - const right_box_point = minPoint + binWidth * appropriate_amount; - const right_box_price = get_bin_price_by_point(right_box_point); + let right_box_point, right_box_price; + if (pair_is_reverse) { + right_box_point = maxPoint - binWidth * appropriate_amount; + right_box_price = reverse_price( + get_bin_price_by_point(right_box_point) + ); + } else { + right_box_point = minPoint + binWidth * appropriate_amount; + right_box_price = get_bin_price_by_point(right_box_point); + } setMaxBoxPoint(right_box_point); setMaxBoxPrice(right_box_price); } else if (removeType == 'right') { - const left_box_point = maxPoint - binWidth * appropriate_amount; - const left_box_price = get_bin_price_by_point(left_box_point); + let left_box_point, left_box_price; + if (pair_is_reverse) { + left_box_point = minPoint + binWidth * appropriate_amount; + left_box_price = reverse_price(get_bin_price_by_point(left_box_point)); + } else { + left_box_point = maxPoint - binWidth * appropriate_amount; + left_box_price = get_bin_price_by_point(left_box_point); + } setMinBoxPoint(left_box_point); setMinBoxPrice(left_box_price); } @@ -394,16 +453,12 @@ export const RemovePoolV3Forward = ({ props }: any) => { * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 * step3 未截断的区间 和 token 数量作为 添加nft的参数 */ - let min_withdraw_token_x_amount = Big(0); - let min_withdraw_token_y_amount = Big(0); if (broken_deleted_nfts.length) { const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; const addLiquidityInfoList: IAddLiquidityInfo[] = []; broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { const { amount, lpt_id, left_point, right_point, mft_id } = l; const [new_left_point, new_right_point] = get_un_deleted_range(l); - // const [whole_token_x_amount, whole_token_y_amount] = - // get_x_y_amount_of_liquidity({ left_point, right_point, amount }); const [new_token_x_amount, new_token_y_amount] = get_x_y_amount_of_liquidity({ left_point: new_left_point, @@ -416,31 +471,6 @@ export const RemovePoolV3Forward = ({ props }: any) => { right_point: right_point, amount, }); - // if (Big(min_token_x_amount).gt(new_token_x_amount)) { - // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - // Big(min_token_x_amount).minus(new_token_x_amount) - // ); - // } - // if (Big(min_token_y_amount).gt(new_token_y_amount)) { - // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - // Big(min_token_y_amount).minus(new_token_y_amount) - // ); - // } - - // const remove_part_token_x_amount = new Big( - // whole_token_x_amount || 0 - // ).minus(new_token_x_amount || 0); - // const remove_part_token_y_amount = new Big( - // whole_token_y_amount || 0 - // ).minus(whole_token_y_amount || 0); - // const rate = (100 - slippageTolerance) / 100; - // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - // remove_part_token_x_amount.mul(rate) - // ); - // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - // remove_part_token_y_amount.mul(rate) - // ); - addLiquidityInfoList.push({ pool_id, left_point: new_left_point, @@ -500,16 +530,6 @@ export const RemovePoolV3Forward = ({ props }: any) => { }); batch_remove_liquidity = batchRemoveLiquidity; } - // const widthdraw_infos = { - // min_withdraw_token_x_amount: toNonDivisibleNumber( - // tokenX.decimals, - // min_withdraw_token_x_amount.toFixed() - // ), - // min_withdraw_token_y_amount: toNonDivisibleNumber( - // tokenY.decimals, - // min_withdraw_token_y_amount.toFixed() - // ), - // }; batch_remove_liquidity_contract({ token_x: tokenX, token_y: tokenY, @@ -597,26 +617,37 @@ export const RemovePoolV3Forward = ({ props }: any) => { function get_un_deleted_range(liquidity: UserLiquidityInfo) { const { left_point, right_point } = liquidity; // intersection part - const intersection_l = Math.max(left_point, minBoxPoint); - const intersection_r = Math.min(right_point, maxBoxPoint); + let intersection_l, intersection_r; + if (pair_is_reverse) { + intersection_l = Math.min(right_point, minBoxPoint); + intersection_r = Math.max(left_point, maxBoxPoint); + } else { + intersection_l = Math.max(left_point, minBoxPoint); + intersection_r = Math.min(right_point, maxBoxPoint); + } // intersection part let un_intersection_l; let un_intersection_r; if (removeType == 'left') { un_intersection_l = intersection_r; - un_intersection_r = right_point; + if (pair_is_reverse) { + un_intersection_r = left_point; + } else { + un_intersection_r = right_point; + } } else if (removeType == 'right') { - un_intersection_l = left_point; + if (pair_is_reverse) { + un_intersection_l = right_point; + } else { + un_intersection_l = left_point; + } un_intersection_r = intersection_l; } - return [un_intersection_l, un_intersection_r]; - } - function get_deleted_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - // intersection part - const intersection_l = Math.max(left_point, minBoxPoint); - const intersection_r = Math.min(right_point, maxBoxPoint); - return [intersection_l, intersection_r]; + if (pair_is_reverse) { + return [un_intersection_r, un_intersection_l]; + } else { + return [un_intersection_l, un_intersection_r]; + } } function get_x_y_amount_of_liquidity(liquidity: { left_point: number; @@ -685,7 +716,11 @@ export const RemovePoolV3Forward = ({ props }: any) => { {/* Symbol pairs */}
-
+
{ >
- {tokens[0]?.symbol}/{tokens[1]?.symbol} + {pair_is_reverse + ? `${tokens[1]?.symbol}/${tokens[0]?.symbol}` + : `${tokens[0]?.symbol}/${tokens[1]?.symbol}`}
@@ -721,6 +758,7 @@ export const RemovePoolV3Forward = ({ props }: any) => { all: removeType == 'all', point: removeType == 'left' ? maxBoxPoint : minBoxPoint, }} + reverse={pair_is_reverse} > )}
@@ -743,7 +781,11 @@ export const RemovePoolV3Forward = ({ props }: any) => { e.stopPropagation(); setRemoveType('left'); setMinBoxPrice(minPrice); - setMinBoxPoint(minPoint); + if (pair_is_reverse) { + setMinBoxPoint(maxPoint); + } else { + setMinBoxPoint(minPoint); + } }} > { e.stopPropagation(); setRemoveType('right'); setMaxBoxPrice(maxPrice); - setMaxBoxPoint(maxPoint); + if (pair_is_reverse) { + setMaxBoxPoint(minPoint); + } else { + setMaxBoxPoint(maxPoint); + } }} > { e.stopPropagation(); setRemoveType('all'); setMinBoxPrice(minPrice); - setMinBoxPoint(minPoint); setMaxBoxPrice(maxPrice); - setMaxBoxPoint(maxPoint); + if (pair_is_reverse) { + setMinBoxPoint(maxPoint); + setMaxBoxPoint(minPoint); + } else { + setMinBoxPoint(minPoint); + setMaxBoxPoint(maxPoint); + } }} > {
- {/* remove slider todo */} + {/* remove slider */} {/* binBoxAmount 控制 */} { -
-
+
+
{ ); }; - -/** - * - * @param props 逆向的操作思路是:展示层价格 是反的,数据层,点位是不变的。 - * @returns - */ -export const RemovePoolV3Reverse = ({ props }: any) => { - const { - tokenMetadata_x_y, - poolDetail, - tokenPriceList, - isLegacy, - listLiquidities, - ...restProps - }: { - tokenMetadata_x_y: TokenMetadata[]; - poolDetail: PoolInfo; - tokenPriceList: any; - isLegacy?: boolean; - restProps: any; - listLiquidities: UserLiquidityInfo[]; - } = props; - const SLOT_NUMBER = get_slot_number_in_a_bin(); - const [slippageTolerance, setSlippageTolerance] = useState(0.5); - - const tokens = tokenMetadata_x_y; - const { decimals: token_x_decimals } = tokens[0]; - const { decimals: token_y_decimals } = tokens[1]; - const [removeLoading, setRemoveLoading] = useState(false); - const [removeType, setRemoveType] = useState('all'); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - - // new - const [minPoint, setMinPoint] = useState(); - const [maxPoint, setMaxPoint] = useState(); - const [minPrice, setMinPrice] = useState(''); - const [maxPrice, setMaxPrice] = useState(''); - const [maxBinAmount, setMaxBinAmount] = useState(); - - const [minBoxPrice, setMinBoxPrice] = useState(''); - const [maxBoxPrice, setMaxBoxPrice] = useState(''); - const [minBoxPoint, setMinBoxPoint] = useState(); - const [maxBoxPoint, setMaxBoxPoint] = useState(); - const [binBoxAmount, setBinBoxAmount] = useState(''); - - useEffect(() => { // =======》ok - // init - if (tokens && poolDetail && listLiquidities) { - get_user_points_range(); - } - }, [tokens, poolDetail, listLiquidities]); - useEffect(() => { // =======》ok - if (minBoxPoint && maxBoxPoint) { - const bin_amount = get_bin_amount_by_points(maxBoxPoint, minBoxPoint); - setBinBoxAmount(bin_amount); - } - }, [minBoxPoint, maxBoxPoint]); - useEffect(() => { - if (binBoxAmount !== '') { // =======》ok - handleBinAmountToAppropriateAmount(+binBoxAmount); - } - }, [binBoxAmount]); - const [ - min_received_x_amount, - min_received_y_amount, - min_received_total_value, - ] = useMemo(() => { // ========> ok - if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { - const { total_token_x_amount, total_token_y_amount, total_value } = - get_minimum_received_data(); - return [ - formatNumber(total_token_x_amount), - formatNumber(total_token_y_amount), - formatWithCommas_usd(total_value), - ]; - } - return ['0', '0', '$0']; - }, [ - tokenPriceList, - tokenMetadata_x_y, - minBoxPoint, - maxBoxPoint, - slippageTolerance, - ]); - /** - * NOTE 删除一个点的场景暂时不考虑 - * @returns - */ - function get_will_deleted_nfts() { - let whole_deleted_nfts: UserLiquidityInfo[] = []; - let broken_deleted_nfts: UserLiquidityInfo[] = []; - if (removeType == 'all') { - whole_deleted_nfts = [].concat(listLiquidities); - } else if (removeType == 'left') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (right_point > maxBoxPoint) { - if (left_point >= maxBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } else if (removeType == 'right') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (left_point < minBoxPoint) { - if (right_point <= minBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } - return { - whole_deleted_nfts, - broken_deleted_nfts, - }; - } - function get_slot_number_in_a_bin() { - const pool_id = poolDetail?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - function get_user_points_range() { - const user_points: number[] = []; - listLiquidities.forEach((l: UserLiquidityInfo) => { - user_points.push(l.left_point, l.right_point); - }); - user_points.sort((b, a) => b - a); - const min_point = get_bin_point_by_point(user_points[0], 'floor'); - const max_point = get_bin_point_by_point( - user_points[user_points.length - 1], - 'ceil' - ); - const min_price = reverse_price(get_bin_price_by_point(max_point)); - const max_price = reverse_price(get_bin_price_by_point(min_point)); - const max_bin_amount = get_bin_amount_by_points(min_point, max_point); - setMinPoint(min_point); - setMaxPoint(max_point); - setMinPrice(min_price); - setMaxPrice(max_price); - setMaxBinAmount(max_bin_amount); - - setMinBoxPrice(min_price); - setMaxBoxPrice(max_price); - setMinBoxPoint(max_point); - setMaxBoxPoint(min_point); - setBinBoxAmount(max_bin_amount); - } - function reverse_price(price:string) { - if (Big(price).eq(0)) return '0'; - return Big(1).div(price).toFixed(); - } - function get_bin_amount_by_points(left_point: number, right_point: number) { - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - const bin_amount = Big(right_point - left_point) - .div(binWidth) - .toFixed(); - return bin_amount; - } - function get_bin_price_by_point(point: number) { - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(point, decimalRate); - return price; - } - function get_bin_point_by_price(price: string) { - const point_delta = poolDetail.point_delta; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return point; - } - function get_bin_point_by_point( - point: number, - type: 'round' | 'floor' | 'ceil' - ) { - const point_delta = poolDetail.point_delta; - const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); - return bin_point; - } - function handleMinBoxPriceToAppropriatePoint() { - /** - * min price <= price < max box price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(minBoxPrice || 0); - if (big_price.lt(minPrice)) { - appropriate_price = minPrice; - appropriate_point = maxPoint; - } else if (big_price.gt(maxBoxPrice)) { - appropriate_price = maxBoxPrice; - appropriate_point = maxBoxPoint; - } else { - appropriate_point = get_bin_point_by_price(reverse_price(minBoxPrice)); - appropriate_price = reverse_price(get_bin_price_by_point(appropriate_point)); - } - setMinBoxPrice(appropriate_price); - setMinBoxPoint(appropriate_point); - } - function handleMaxBoxPriceToAppropriatePoint() { - /** - * min box price <= price <= max price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(maxBoxPrice || 0); - if (big_price.lt(minBoxPrice)) { - appropriate_price = minBoxPrice; - appropriate_point = minBoxPoint; - } else if (big_price.gt(maxPrice)) { - appropriate_price = maxPrice; - appropriate_point = minPoint; - } else { - appropriate_point = get_bin_point_by_price(reverse_price(maxBoxPrice)); - appropriate_price = reverse_price(get_bin_price_by_point(appropriate_point)); - } - setMaxBoxPrice(appropriate_price); - setMaxBoxPoint(appropriate_point); - } - /** - * 左右点位改变会触发bin amount随之更改 - * bin amount 修改会改变可以修改的点位 - * 0 <= bin amount < max bin amount - */ - function handleBinAmountToAppropriateAmount(binAmount: number) { - const amount_int = binAmount || +binBoxAmount; - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - let appropriate_amount = amount_int; - if (amount_int > +maxBinAmount) { - appropriate_amount = +maxBinAmount; - } - if (removeType == 'left') { - const right_box_point = maxPoint - binWidth * appropriate_amount; - const right_box_price = reverse_price(get_bin_price_by_point(right_box_point)); - setMaxBoxPoint(right_box_point); - setMaxBoxPrice(right_box_price); - } else if (removeType == 'right') { - const left_box_point = minPoint + binWidth * appropriate_amount; - const left_box_price = reverse_price(get_bin_price_by_point(left_box_point)); - setMinBoxPoint(left_box_point); - setMinBoxPrice(left_box_price); - } - setBinBoxAmount(appropriate_amount.toString()); - } - function getY( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const y = new BigNumber(L).multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - const y_result = y.toFixed(); - return toReadableNumber(token.decimals, toPrecision(y_result, 0)); - } - function getX( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const x = new BigNumber(L) - .multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ) - .toFixed(); - return toReadableNumber(token.decimals, toPrecision(x, 0)); - } - function get_X_Y_In_CurrentPoint( - tokenX: TokenMetadata, - tokenY: TokenMetadata, - L: string - ) { - const { liquidity, liquidity_x, current_point } = poolDetail; - const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); - let Ly = '0'; - let Lx = '0'; - // only remove y - if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { - Ly = L; - } else { - // have x and y - Ly = liquidity_y_big.toFixed(); - Lx = new BigNumber(L).minus(Ly).toFixed(); - } - const amountX = getXAmount_per_point_by_Lx(Lx, current_point); - const amountY = getYAmount_per_point_by_Ly(Ly, current_point); - const amountX_read = toReadableNumber( - tokenX.decimals, - toPrecision(amountX, 0) - ); - const amountY_read = toReadableNumber( - tokenY.decimals, - toPrecision(amountY, 0) - ); - return { amountx: amountX_read, amounty: amountY_read }; - } - function batch_remove_nfts() { - setRemoveLoading(true); - const [tokenX, tokenY] = tokenMetadata_x_y; - sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); - let batch_remove_liquidity: IRemoveLiquidityInfo[]; - let batch_update_liquidity: IBatchUpdateiquidityInfo; - let mint_liquidities: UserLiquidityInfo[] = []; - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - const { pool_id } = poolDetail; - /** - * step1 找到被截断的nft的 未截断的区间 - * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 - * step3 未截断的区间 和 token 数量作为 添加nft的参数 - */ - if (broken_deleted_nfts.length) { - const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - addLiquidityInfoList.push({ - pool_id, - left_point: new_left_point, - right_point: new_right_point, - amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), - amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), - min_amount_x: '0', - min_amount_y: '0', - }); - removeLiquidityInfos.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - - batch_update_liquidity = { - remove_liquidity_infos: removeLiquidityInfos, - add_liquidity_infos: addLiquidityInfoList, - }; - } - if (whole_deleted_nfts.length) { - const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - batchRemoveLiquidity.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - batch_remove_liquidity = batchRemoveLiquidity; - } - batch_remove_liquidity_contract({ - token_x: tokenX, - token_y: tokenY, - batch_remove_liquidity, - batch_update_liquidity, - mint_liquidities, - // widthdraw_infos, - }); - } - function get_minimum_received_data() { - /** - * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 - * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 - * step3 把上述step1, step2 得到的x,y 累加起来即可 - */ - let total_token_x_amount = Big(0); - let total_token_y_amount = Big(0); - let minimum_total_value = Big(0); - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - if (whole_deleted_nfts.length) { - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - total_token_x_amount = total_token_x_amount.plus( - min_token_x_amount || 0 - ); - total_token_y_amount = total_token_y_amount.plus( - min_token_y_amount || 0 - ); - }); - } - if (broken_deleted_nfts.length) { - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - const broken_min_token_x_amount = - Big(min_token_x_amount).minus(new_token_x_amount); - const broken_min_token_y_amount = - Big(min_token_y_amount).minus(new_token_y_amount); - if (broken_min_token_x_amount.gt(0)) { - total_token_x_amount = total_token_x_amount.plus( - broken_min_token_x_amount || 0 - ); - } - if (broken_min_token_y_amount.gt(0)) { - total_token_y_amount = total_token_y_amount.plus( - broken_min_token_y_amount || 0 - ); - } - }); - } - if (tokenPriceList && tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const priceX = tokenPriceList[tokenX.id]?.price || 0; - const priceY = tokenPriceList[tokenY.id]?.price || 0; - const token_x_value = total_token_x_amount.mul(priceX); - const token_y_value = total_token_y_amount.mul(priceY); - minimum_total_value = token_x_value.plus(token_y_value); - } - const rate = (100 - slippageTolerance) / 100; - return { - total_token_x_amount: total_token_x_amount.toFixed(), - total_token_y_amount: total_token_y_amount.toFixed(), - minimum_total_value: minimum_total_value.toFixed(), - total_value: minimum_total_value.div(rate).toFixed(), - }; - } - function get_un_deleted_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - // intersection part - const intersection_l = Math.min(right_point, minBoxPoint); - const intersection_r = Math.max(left_point, maxBoxPoint); - // intersection part - let un_intersection_l; - let un_intersection_r; - if (removeType == 'left') { - un_intersection_l = intersection_r; - un_intersection_r = left_point; - } else if (removeType == 'right') { - un_intersection_l = right_point; - un_intersection_r = intersection_l; - } - return [un_intersection_r, un_intersection_l]; - } - function get_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const { left_point, right_point, amount: L } = liquidity; - const { current_point } = poolDetail; - let curTokenXAmount = '0'; - let curTokenYAmount = '0'; - // in range - if (current_point >= left_point && right_point > current_point) { - curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); - curTokenYAmount = getY(left_point, current_point, L, tokenY); - const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); - curTokenXAmount = Big(amountx || '0') - .plus(curTokenXAmount || '0') - .toFixed(); - curTokenYAmount = Big(amounty || '0') - .plus(curTokenYAmount || '0') - .toFixed(); - } - // only y token - if (current_point >= right_point) { - curTokenYAmount = getY(left_point, right_point, L, tokenY); - } - // only x token - if (left_point > current_point) { - curTokenXAmount = getX(left_point, right_point, L, tokenX); - } - return [curTokenXAmount, curTokenYAmount]; - } - function get_min_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const rate = (100 - slippageTolerance) / 100; - const [token_x_amount, token_y_amount] = - get_x_y_amount_of_liquidity(liquidity); - const min_token_x_amount = Big(token_x_amount || 0) - .mul(rate) - .toFixed(); - const min_token_y_amount = Big(token_y_amount || 0) - .mul(rate) - .toFixed(); - return [min_token_x_amount, min_token_y_amount]; - } - const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; - return ( - - - {/* Title */} -
- - - -
- -
-
- {/* Symbol pairs */} -
-
-
- - -
- - {tokens[1]?.symbol}/{tokens[0]?.symbol} - -
- - {min_received_total_value} - -
-
- {maxPoint && ( - - )} -
- {/* Removing way */} -
-
- -
-
-
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('left'); - setMinBoxPrice(minPrice); - setMinBoxPoint(maxPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('right'); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(minPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('all'); - setMinBoxPrice(minPrice); - setMinBoxPoint(maxPoint); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(minPoint); - }} - > - -
-
-
- {/* remove slider */} - {/* binBoxAmount 控制 */} - { - setBinBoxAmount(v.toString()); - }} - value={+binBoxAmount} - min={0} - max={+maxBinAmount} - step={1} - > - {/* Set points */} -
- {/* min price */} -
- - - - - { - const value = e.target.value; - setMinBoxPrice(value); - }} - inputMode="decimal" - onBlur={() => { - handleMinBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'right'} - > -
- {/* max price */} -
- - - - { - const value = e.target.value; - setMaxBoxPrice(value); - }} - value={maxBoxPrice} - inputMode="decimal" - onBlur={() => { - handleMaxBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'left'} - > -
- {/* bin amount */} -
- - - - - -
-
- {/* Slippage */} -
- -
- {/* Minimum received */} -
-
- - - - -
-
- - - - {min_received_y_amount} - -
-
- - - - {min_received_x_amount} - -
-
-
- {/* Button */} - {isSignedIn ? ( - - } - /> - - ) : ( -
- -
- )} -
-
- ); -}; export function IntegerInputComponent({ value, setValue, @@ -1831,4 +1050,4 @@ export function IntegerInputComponent({ />
); -} \ No newline at end of file +} diff --git a/src/components/pool/RemovePoolV3Copy.tsx b/src/components/pool/RemovePoolV3Copy.tsx new file mode 100644 index 000000000..3dc0567d2 --- /dev/null +++ b/src/components/pool/RemovePoolV3Copy.tsx @@ -0,0 +1,1844 @@ +import React, { useEffect, useMemo, useState, useContext, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { WalletContext } from '../../utils/wallets-integration'; +import { Card } from '~components/card/Card'; +import { ModalClose } from '~components/icon'; +import { TokenMetadata } from '../../services/ft-contract'; +import ReactSlider from 'react-slider'; +import { + GradientButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from '../../components/button/Button'; +import { PoolSlippageSelectorV3 } from '~components/forms/SlippageSelector'; +import Modal from 'react-modal'; +import BigNumber from 'bignumber.js'; +import { + toPrecision, + toReadableNumber, + toNonDivisibleNumber, +} from '~utils/numbers'; +import { + getPriceByPoint, + CONSTANT_D, + UserLiquidityInfo, + getXAmount_per_point_by_Lx, + getYAmount_per_point_by_Ly, + sort_tokens_by_base, + getBinPointByPrice, + getBinPointByPoint, +} from '../../services/commonV3'; +import { + PoolInfo, + batch_remove_liquidity_contract, +} from '../../services/swapV3'; +import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; +import Big from 'big.js'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, +} from '../../components/d3Chart/config'; +import { + IChartItemConfig, + IChartConfig, +} from '../../components/d3Chart/interfaces'; +import { + formatNumber, + formatWithCommas_usd, +} from '../../components/d3Chart/utils'; +import { + IAddLiquidityInfo, + IRemoveLiquidityInfo, + IBatchUpdateiquidityInfo, +} from '~pages/poolsV3/interfaces'; +import DclChart from '../../components/d3Chart/DclChart'; + +export type RemoveType = 'left' | 'right' | 'all'; +/** + * 中文 + * @param props + * @returns + */ +export const RemovePoolV3 = (props: any) => { + // return + return ; +}; +/** + * + * @param props 正向操作 + * @returns + */ +export const RemovePoolV3Forward = ({ props }: any) => { + const { + tokenMetadata_x_y, + poolDetail, + tokenPriceList, + isLegacy, + listLiquidities, + ...restProps + }: { + tokenMetadata_x_y: TokenMetadata[]; + poolDetail: PoolInfo; + tokenPriceList: any; + isLegacy?: boolean; + restProps: any; + listLiquidities: UserLiquidityInfo[]; + } = props; + const SLOT_NUMBER = get_slot_number_in_a_bin(); + const [slippageTolerance, setSlippageTolerance] = useState(0.5); + + // const tokens = sort_tokens_by_base(tokenMetadata_x_y);// todo + const tokens = tokenMetadata_x_y; + const { decimals: token_x_decimals } = tokens[0]; + const { decimals: token_y_decimals } = tokens[1]; + const [removeLoading, setRemoveLoading] = useState(false); + const [removeType, setRemoveType] = useState('all'); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + + // new + const [minPoint, setMinPoint] = useState(); + const [maxPoint, setMaxPoint] = useState(); + const [minPrice, setMinPrice] = useState(''); + const [maxPrice, setMaxPrice] = useState(''); + const [maxBinAmount, setMaxBinAmount] = useState(); + + const [minBoxPrice, setMinBoxPrice] = useState(''); + const [maxBoxPrice, setMaxBoxPrice] = useState(''); + const [minBoxPoint, setMinBoxPoint] = useState(); + const [maxBoxPoint, setMaxBoxPoint] = useState(); + const [binBoxAmount, setBinBoxAmount] = useState(''); + + useEffect(() => { + // init + if (tokens && poolDetail && listLiquidities) { + get_user_points_range(); + } + }, [tokens, poolDetail, listLiquidities]); + useEffect(() => { + if (minBoxPoint && maxBoxPoint) { + const bin_amount = get_bin_amount_by_points(minBoxPoint, maxBoxPoint); + setBinBoxAmount(bin_amount); + } + }, [minBoxPoint, maxBoxPoint]); + useEffect(() => { + if (binBoxAmount !== '') { + handleBinAmountToAppropriateAmount(+binBoxAmount); + } + }, [binBoxAmount]); + const [ + min_received_x_amount, + min_received_y_amount, + min_received_total_value, + ] = useMemo(() => { + if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { + const { total_token_x_amount, total_token_y_amount, total_value } = + get_minimum_received_data(); + return [ + formatNumber(total_token_x_amount), + formatNumber(total_token_y_amount), + formatWithCommas_usd(total_value), + ]; + } + return ['0', '0', '$0']; + }, [ + tokenPriceList, + tokenMetadata_x_y, + minBoxPoint, + maxBoxPoint, + slippageTolerance, + ]); + /** + * NOTE 删除一个点的场景暂时不考虑 + * @returns + */ + function get_will_deleted_nfts() { + let whole_deleted_nfts: UserLiquidityInfo[] = []; + let broken_deleted_nfts: UserLiquidityInfo[] = []; + if (removeType == 'all') { + whole_deleted_nfts = [].concat(listLiquidities); + } else if (removeType == 'left') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (left_point < maxBoxPoint) { + // 之前:left_point <= maxBoxPoint + if (right_point <= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } else if (removeType == 'right') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (right_point > minBoxPoint) { + if (left_point >= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } + return { + whole_deleted_nfts, + broken_deleted_nfts, + }; + } + function get_slot_number_in_a_bin() { + const pool_id = poolDetail?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + function get_user_points_range() { + const user_points: number[] = []; + listLiquidities.forEach((l: UserLiquidityInfo) => { + user_points.push(l.left_point, l.right_point); + }); + user_points.sort((b, a) => b - a); + const min_point = get_bin_point_by_point(user_points[0], 'floor'); + const max_point = get_bin_point_by_point( + user_points[user_points.length - 1], + 'ceil' + ); + const min_price = get_bin_price_by_point(min_point); + const max_price = get_bin_price_by_point(max_point); + + const max_bin_amount = get_bin_amount_by_points(min_point, max_point); + setMinPoint(min_point); + setMaxPoint(max_point); + setMinPrice(min_price); + setMaxPrice(max_price); + setMaxBinAmount(max_bin_amount); + + setMinBoxPrice(min_price); + setMaxBoxPrice(max_price); + setMinBoxPoint(min_point); + setMaxBoxPoint(max_point); + setBinBoxAmount(max_bin_amount); + } + function get_bin_amount_by_points(left_point: number, right_point: number) { + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + const bin_amount = Big(right_point - left_point) + .div(binWidth) + .toFixed(); + return bin_amount; + } + function get_bin_price_by_point(point: number) { + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(point, decimalRate); + return price; + } + function get_bin_point_by_price(price: string) { + const point_delta = poolDetail.point_delta; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return point; + } + function get_bin_point_by_point( + point: number, + type: 'round' | 'floor' | 'ceil' + ) { + const point_delta = poolDetail.point_delta; + const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); + return bin_point; + } + function handleMinBoxPriceToAppropriatePoint() { + /** + * min price <= price < max box price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(minBoxPrice || 0); + if (big_price.lt(minPrice)) { + appropriate_price = minPrice; + appropriate_point = minPoint; + } else if (big_price.gt(maxBoxPrice)) { + appropriate_price = maxBoxPrice; + appropriate_point = maxBoxPoint; + } else { + appropriate_point = get_bin_point_by_price(minBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); + } + setMinBoxPrice(appropriate_price); + setMinBoxPoint(appropriate_point); + } + function handleMaxBoxPriceToAppropriatePoint() { + /** + * min box price <= price <= max price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(maxBoxPrice || 0); + if (big_price.lt(minBoxPrice)) { + appropriate_price = minBoxPrice; + appropriate_point = minBoxPoint; + } else if (big_price.gt(maxPrice)) { + appropriate_price = maxPrice; + appropriate_point = maxPoint; + } else { + appropriate_point = get_bin_point_by_price(maxBoxPrice); + appropriate_price = get_bin_price_by_point(appropriate_point); + } + setMaxBoxPrice(appropriate_price); + setMaxBoxPoint(appropriate_point); + } + /** + * 左右点位改变会触发bin amount随之更改 + * bin amount 修改会改变可以修改的点位 + * 0 <= bin amount < max bin amount + */ + function handleBinAmountToAppropriateAmount(point: number) { + const amount_int = point || +binBoxAmount; + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + let appropriate_amount = amount_int; + if (amount_int > +maxBinAmount) { + appropriate_amount = +maxBinAmount; + } + if (removeType == 'left') { + const right_box_point = minPoint + binWidth * appropriate_amount; + const right_box_price = get_bin_price_by_point(right_box_point); + setMaxBoxPoint(right_box_point); + setMaxBoxPrice(right_box_price); + } else if (removeType == 'right') { + const left_box_point = maxPoint - binWidth * appropriate_amount; + const left_box_price = get_bin_price_by_point(left_box_point); + setMinBoxPoint(left_box_point); + setMinBoxPrice(left_box_price); + } + setBinBoxAmount(appropriate_amount.toString()); + } + function getY( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + function batch_remove_nfts() { + setRemoveLoading(true); + const [tokenX, tokenY] = tokenMetadata_x_y; + sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); + let batch_remove_liquidity: IRemoveLiquidityInfo[]; + let batch_update_liquidity: IBatchUpdateiquidityInfo; + let mint_liquidities: UserLiquidityInfo[] = []; + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + const { pool_id } = poolDetail; + /** + * step1 找到被截断的nft的 未截断的区间 + * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 + * step3 未截断的区间 和 token 数量作为 添加nft的参数 + */ + let min_withdraw_token_x_amount = Big(0); + let min_withdraw_token_y_amount = Big(0); + if (broken_deleted_nfts.length) { + const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + // const [whole_token_x_amount, whole_token_y_amount] = + // get_x_y_amount_of_liquidity({ left_point, right_point, amount }); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + // if (Big(min_token_x_amount).gt(new_token_x_amount)) { + // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + // Big(min_token_x_amount).minus(new_token_x_amount) + // ); + // } + // if (Big(min_token_y_amount).gt(new_token_y_amount)) { + // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + // Big(min_token_y_amount).minus(new_token_y_amount) + // ); + // } + + // const remove_part_token_x_amount = new Big( + // whole_token_x_amount || 0 + // ).minus(new_token_x_amount || 0); + // const remove_part_token_y_amount = new Big( + // whole_token_y_amount || 0 + // ).minus(whole_token_y_amount || 0); + // const rate = (100 - slippageTolerance) / 100; + // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( + // remove_part_token_x_amount.mul(rate) + // ); + // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( + // remove_part_token_y_amount.mul(rate) + // ); + + addLiquidityInfoList.push({ + pool_id, + left_point: new_left_point, + right_point: new_right_point, + amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), + amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), + min_amount_x: '0', + min_amount_y: '0', + }); + removeLiquidityInfos.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + + batch_update_liquidity = { + remove_liquidity_infos: removeLiquidityInfos, + add_liquidity_infos: addLiquidityInfoList, + }; + } + if (whole_deleted_nfts.length) { + const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + batchRemoveLiquidity.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + batch_remove_liquidity = batchRemoveLiquidity; + } + // const widthdraw_infos = { + // min_withdraw_token_x_amount: toNonDivisibleNumber( + // tokenX.decimals, + // min_withdraw_token_x_amount.toFixed() + // ), + // min_withdraw_token_y_amount: toNonDivisibleNumber( + // tokenY.decimals, + // min_withdraw_token_y_amount.toFixed() + // ), + // }; + batch_remove_liquidity_contract({ + token_x: tokenX, + token_y: tokenY, + batch_remove_liquidity, + batch_update_liquidity, + mint_liquidities, + // widthdraw_infos, + }); + } + function get_minimum_received_data() { + /** + * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 + * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 + * step3 把上述step1, step2 得到的x,y 累加起来即可 + */ + let total_token_x_amount = Big(0); + let total_token_y_amount = Big(0); + let minimum_total_value = Big(0); + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + if (whole_deleted_nfts.length) { + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + total_token_x_amount = total_token_x_amount.plus( + min_token_x_amount || 0 + ); + total_token_y_amount = total_token_y_amount.plus( + min_token_y_amount || 0 + ); + }); + } + if (broken_deleted_nfts.length) { + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + const broken_min_token_x_amount = + Big(min_token_x_amount).minus(new_token_x_amount); + const broken_min_token_y_amount = + Big(min_token_y_amount).minus(new_token_y_amount); + if (broken_min_token_x_amount.gt(0)) { + total_token_x_amount = total_token_x_amount.plus( + broken_min_token_x_amount || 0 + ); + } + if (broken_min_token_y_amount.gt(0)) { + total_token_y_amount = total_token_y_amount.plus( + broken_min_token_y_amount || 0 + ); + } + }); + } + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + const token_x_value = total_token_x_amount.mul(priceX); + const token_y_value = total_token_y_amount.mul(priceY); + minimum_total_value = token_x_value.plus(token_y_value); + } + const rate = (100 - slippageTolerance) / 100; + return { + total_token_x_amount: total_token_x_amount.toFixed(), + total_token_y_amount: total_token_y_amount.toFixed(), + minimum_total_value: minimum_total_value.toFixed(), + total_value: minimum_total_value.div(rate).toFixed(), + }; + } + function get_un_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.max(left_point, minBoxPoint); + const intersection_r = Math.min(right_point, maxBoxPoint); + // intersection part + let un_intersection_l; + let un_intersection_r; + if (removeType == 'left') { + un_intersection_l = intersection_r; + un_intersection_r = right_point; + } else if (removeType == 'right') { + un_intersection_l = left_point; + un_intersection_r = intersection_l; + } + return [un_intersection_l, un_intersection_r]; + } + function get_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.max(left_point, minBoxPoint); + const intersection_r = Math.min(right_point, maxBoxPoint); + return [intersection_l, intersection_r]; + } + function get_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { left_point, right_point, amount: L } = liquidity; + const { current_point } = poolDetail; + let curTokenXAmount = '0'; + let curTokenYAmount = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); + curTokenYAmount = getY(left_point, current_point, L, tokenY); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + curTokenXAmount = Big(amountx || '0') + .plus(curTokenXAmount || '0') + .toFixed(); + curTokenYAmount = Big(amounty || '0') + .plus(curTokenYAmount || '0') + .toFixed(); + } + // only y token + if (current_point >= right_point) { + curTokenYAmount = getY(left_point, right_point, L, tokenY); + } + // only x token + if (left_point > current_point) { + curTokenXAmount = getX(left_point, right_point, L, tokenX); + } + return [curTokenXAmount, curTokenYAmount]; + } + function get_min_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const rate = (100 - slippageTolerance) / 100; + const [token_x_amount, token_y_amount] = + get_x_y_amount_of_liquidity(liquidity); + const min_token_x_amount = Big(token_x_amount || 0) + .mul(rate) + .toFixed(); + const min_token_y_amount = Big(token_y_amount || 0) + .mul(rate) + .toFixed(); + return [min_token_x_amount, min_token_y_amount]; + } + const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; + return ( + + + {/* Title */} +
+ + + +
+ +
+
+ {/* Symbol pairs */} +
+
+
+ + +
+ + {tokens[0]?.symbol}/{tokens[1]?.symbol} + +
+ + {min_received_total_value} + +
+
+ {maxPoint && ( + + )} +
+ {/* Removing way */} +
+
+ +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('left'); + setMinBoxPrice(minPrice); + setMinBoxPoint(minPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('right'); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(maxPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('all'); + setMinBoxPrice(minPrice); + setMinBoxPoint(minPoint); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(maxPoint); + }} + > + +
+
+
+ {/* remove slider todo */} + {/* binBoxAmount 控制 */} + { + setBinBoxAmount(v.toString()); + }} + value={+binBoxAmount} + min={0} + max={+maxBinAmount} + step={1} + > + {/* Set points */} +
+ {/* min price */} +
+ + + + + { + const value = e.target.value; + setMinBoxPrice(value); + }} + inputMode="decimal" + onBlur={() => { + handleMinBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'right'} + > +
+ {/* max price */} +
+ + + + { + const value = e.target.value; + setMaxBoxPrice(value); + }} + value={maxBoxPrice} + inputMode="decimal" + onBlur={() => { + handleMaxBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'left'} + > +
+ {/* bin amount */} +
+ + + + + +
+
+ {/* Slippage */} +
+ +
+ {/* Minimum received */} +
+
+ + + + +
+
+ + + + {min_received_x_amount} + +
+
+ + + + {min_received_y_amount} + +
+
+
+ {/* Button */} + {isSignedIn ? ( + + } + /> + + ) : ( +
+ +
+ )} +
+
+ ); +}; + +/** + * + * @param props 逆向的操作思路是:展示层价格 是反的,数据层,点位是不变的。 + * @returns + */ +export const RemovePoolV3Reverse = ({ props }: any) => { + const { + tokenMetadata_x_y, + poolDetail, + tokenPriceList, + isLegacy, + listLiquidities, + ...restProps + }: { + tokenMetadata_x_y: TokenMetadata[]; + poolDetail: PoolInfo; + tokenPriceList: any; + isLegacy?: boolean; + restProps: any; + listLiquidities: UserLiquidityInfo[]; + } = props; + const SLOT_NUMBER = get_slot_number_in_a_bin(); + const [slippageTolerance, setSlippageTolerance] = useState(0.5); + + const tokens = tokenMetadata_x_y; + const { decimals: token_x_decimals } = tokens[0]; + const { decimals: token_y_decimals } = tokens[1]; + const [removeLoading, setRemoveLoading] = useState(false); + const [removeType, setRemoveType] = useState('all'); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + + // new + const [minPoint, setMinPoint] = useState(); + const [maxPoint, setMaxPoint] = useState(); + const [minPrice, setMinPrice] = useState(''); + const [maxPrice, setMaxPrice] = useState(''); + const [maxBinAmount, setMaxBinAmount] = useState(); + + const [minBoxPrice, setMinBoxPrice] = useState(''); + const [maxBoxPrice, setMaxBoxPrice] = useState(''); + const [minBoxPoint, setMinBoxPoint] = useState(); + const [maxBoxPoint, setMaxBoxPoint] = useState(); + const [binBoxAmount, setBinBoxAmount] = useState(''); + + useEffect(() => { + // =======》ok + // init + if (tokens && poolDetail && listLiquidities) { + get_user_points_range(); + } + }, [tokens, poolDetail, listLiquidities]); + useEffect(() => { + // =======》ok + if (minBoxPoint && maxBoxPoint) { + const bin_amount = get_bin_amount_by_points(maxBoxPoint, minBoxPoint); + setBinBoxAmount(bin_amount); + } + }, [minBoxPoint, maxBoxPoint]); + useEffect(() => { + if (binBoxAmount !== '') { + // =======》ok + handleBinAmountToAppropriateAmount(+binBoxAmount); + } + }, [binBoxAmount]); + const [ + min_received_x_amount, + min_received_y_amount, + min_received_total_value, + ] = useMemo(() => { + // ========> ok + if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { + const { total_token_x_amount, total_token_y_amount, total_value } = + get_minimum_received_data(); + return [ + formatNumber(total_token_x_amount), + formatNumber(total_token_y_amount), + formatWithCommas_usd(total_value), + ]; + } + return ['0', '0', '$0']; + }, [ + tokenPriceList, + tokenMetadata_x_y, + minBoxPoint, + maxBoxPoint, + slippageTolerance, + ]); + /** + * NOTE 删除一个点的场景暂时不考虑 + * @returns + */ + function get_will_deleted_nfts() { + let whole_deleted_nfts: UserLiquidityInfo[] = []; + let broken_deleted_nfts: UserLiquidityInfo[] = []; + if (removeType == 'all') { + whole_deleted_nfts = [].concat(listLiquidities); + } else if (removeType == 'left') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (right_point > maxBoxPoint) { + if (left_point >= maxBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } else if (removeType == 'right') { + listLiquidities.forEach((l: UserLiquidityInfo) => { + const { left_point, right_point } = l; + if (left_point < minBoxPoint) { + if (right_point <= minBoxPoint) { + whole_deleted_nfts.push(l); + } else { + broken_deleted_nfts.push(l); + } + } + }); + } + return { + whole_deleted_nfts, + broken_deleted_nfts, + }; + } + function get_slot_number_in_a_bin() { + const pool_id = poolDetail?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + function get_user_points_range() { + const user_points: number[] = []; + listLiquidities.forEach((l: UserLiquidityInfo) => { + user_points.push(l.left_point, l.right_point); + }); + user_points.sort((b, a) => b - a); + const min_point = get_bin_point_by_point(user_points[0], 'floor'); + const max_point = get_bin_point_by_point( + user_points[user_points.length - 1], + 'ceil' + ); + const min_price = reverse_price(get_bin_price_by_point(max_point)); + const max_price = reverse_price(get_bin_price_by_point(min_point)); + const max_bin_amount = get_bin_amount_by_points(min_point, max_point); + setMinPoint(min_point); + setMaxPoint(max_point); + setMinPrice(min_price); + setMaxPrice(max_price); + setMaxBinAmount(max_bin_amount); + + setMinBoxPrice(min_price); + setMaxBoxPrice(max_price); + setMinBoxPoint(max_point); + setMaxBoxPoint(min_point); + setBinBoxAmount(max_bin_amount); + } + function reverse_price(price: string) { + if (Big(price).eq(0)) return '0'; + return Big(1).div(price).toFixed(); + } + function get_bin_amount_by_points(left_point: number, right_point: number) { + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + const bin_amount = Big(right_point - left_point) + .div(binWidth) + .toFixed(); + return bin_amount; + } + function get_bin_price_by_point(point: number) { + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + const price = getPriceByPoint(point, decimalRate); + return price; + } + function get_bin_point_by_price(price: string) { + const point_delta = poolDetail.point_delta; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return point; + } + function get_bin_point_by_point( + point: number, + type: 'round' | 'floor' | 'ceil' + ) { + const point_delta = poolDetail.point_delta; + const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); + return bin_point; + } + function handleMinBoxPriceToAppropriatePoint() { + /** + * min price <= price < max box price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(minBoxPrice || 0); + if (big_price.lt(minPrice)) { + appropriate_price = minPrice; + appropriate_point = maxPoint; + } else if (big_price.gt(maxBoxPrice)) { + appropriate_price = maxBoxPrice; + appropriate_point = maxBoxPoint; + } else { + appropriate_point = get_bin_point_by_price(reverse_price(minBoxPrice)); + appropriate_price = reverse_price( + get_bin_price_by_point(appropriate_point) + ); + } + setMinBoxPrice(appropriate_price); + setMinBoxPoint(appropriate_point); + } + function handleMaxBoxPriceToAppropriatePoint() { + /** + * min box price <= price <= max price + */ + let appropriate_price; + let appropriate_point; + const big_price = Big(maxBoxPrice || 0); + if (big_price.lt(minBoxPrice)) { + appropriate_price = minBoxPrice; + appropriate_point = minBoxPoint; + } else if (big_price.gt(maxPrice)) { + appropriate_price = maxPrice; + appropriate_point = minPoint; + } else { + appropriate_point = get_bin_point_by_price(reverse_price(maxBoxPrice)); + appropriate_price = reverse_price( + get_bin_price_by_point(appropriate_point) + ); + } + setMaxBoxPrice(appropriate_price); + setMaxBoxPoint(appropriate_point); + } + /** + * 左右点位改变会触发bin amount随之更改 + * bin amount 修改会改变可以修改的点位 + * 0 <= bin amount < max bin amount + */ + function handleBinAmountToAppropriateAmount(binAmount: number) { + const amount_int = binAmount || +binBoxAmount; + const { point_delta } = poolDetail; + const binWidth = SLOT_NUMBER * point_delta; + let appropriate_amount = amount_int; + if (amount_int > +maxBinAmount) { + appropriate_amount = +maxBinAmount; + } + if (removeType == 'left') { + const right_box_point = maxPoint - binWidth * appropriate_amount; + const right_box_price = reverse_price( + get_bin_price_by_point(right_box_point) + ); + setMaxBoxPoint(right_box_point); + setMaxBoxPrice(right_box_price); + } else if (removeType == 'right') { + const left_box_point = minPoint + binWidth * appropriate_amount; + const left_box_price = reverse_price( + get_bin_price_by_point(left_box_point) + ); + setMinBoxPoint(left_box_point); + setMinBoxPrice(left_box_price); + } + setBinBoxAmount(appropriate_amount.toString()); + } + function getY( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + function batch_remove_nfts() { + setRemoveLoading(true); + const [tokenX, tokenY] = tokenMetadata_x_y; + sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); + let batch_remove_liquidity: IRemoveLiquidityInfo[]; + let batch_update_liquidity: IBatchUpdateiquidityInfo; + let mint_liquidities: UserLiquidityInfo[] = []; + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + const { pool_id } = poolDetail; + /** + * step1 找到被截断的nft的 未截断的区间 + * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 + * step3 未截断的区间 和 token 数量作为 添加nft的参数 + */ + if (broken_deleted_nfts.length) { + const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + addLiquidityInfoList.push({ + pool_id, + left_point: new_left_point, + right_point: new_right_point, + amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), + amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), + min_amount_x: '0', + min_amount_y: '0', + }); + removeLiquidityInfos.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + + batch_update_liquidity = { + remove_liquidity_infos: removeLiquidityInfos, + add_liquidity_infos: addLiquidityInfoList, + }; + } + if (whole_deleted_nfts.length) { + const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, lpt_id, left_point, right_point, mft_id } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + batchRemoveLiquidity.push({ + lpt_id, + amount, + min_amount_x: toNonDivisibleNumber( + tokenX.decimals, + min_token_x_amount + ), + min_amount_y: toNonDivisibleNumber( + tokenY.decimals, + min_token_y_amount + ), + }); + if (mft_id) { + mint_liquidities.push(l); + } + }); + batch_remove_liquidity = batchRemoveLiquidity; + } + batch_remove_liquidity_contract({ + token_x: tokenX, + token_y: tokenY, + batch_remove_liquidity, + batch_update_liquidity, + mint_liquidities, + // widthdraw_infos, + }); + } + function get_minimum_received_data() { + /** + * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 + * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 + * step3 把上述step1, step2 得到的x,y 累加起来即可 + */ + let total_token_x_amount = Big(0); + let total_token_y_amount = Big(0); + let minimum_total_value = Big(0); + const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); + if (whole_deleted_nfts.length) { + whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + total_token_x_amount = total_token_x_amount.plus( + min_token_x_amount || 0 + ); + total_token_y_amount = total_token_y_amount.plus( + min_token_y_amount || 0 + ); + }); + } + if (broken_deleted_nfts.length) { + broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { + const { amount, left_point, right_point } = l; + const [new_left_point, new_right_point] = get_un_deleted_range(l); + const [new_token_x_amount, new_token_y_amount] = + get_x_y_amount_of_liquidity({ + left_point: new_left_point, + right_point: new_right_point, + amount, + }); + const [min_token_x_amount, min_token_y_amount] = + get_min_x_y_amount_of_liquidity({ + left_point: left_point, + right_point: right_point, + amount, + }); + const broken_min_token_x_amount = + Big(min_token_x_amount).minus(new_token_x_amount); + const broken_min_token_y_amount = + Big(min_token_y_amount).minus(new_token_y_amount); + if (broken_min_token_x_amount.gt(0)) { + total_token_x_amount = total_token_x_amount.plus( + broken_min_token_x_amount || 0 + ); + } + if (broken_min_token_y_amount.gt(0)) { + total_token_y_amount = total_token_y_amount.plus( + broken_min_token_y_amount || 0 + ); + } + }); + } + if (tokenPriceList && tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const priceX = tokenPriceList[tokenX.id]?.price || 0; + const priceY = tokenPriceList[tokenY.id]?.price || 0; + const token_x_value = total_token_x_amount.mul(priceX); + const token_y_value = total_token_y_amount.mul(priceY); + minimum_total_value = token_x_value.plus(token_y_value); + } + const rate = (100 - slippageTolerance) / 100; + return { + total_token_x_amount: total_token_x_amount.toFixed(), + total_token_y_amount: total_token_y_amount.toFixed(), + minimum_total_value: minimum_total_value.toFixed(), + total_value: minimum_total_value.div(rate).toFixed(), + }; + } + function get_un_deleted_range(liquidity: UserLiquidityInfo) { + const { left_point, right_point } = liquidity; + // intersection part + const intersection_l = Math.min(right_point, minBoxPoint); + const intersection_r = Math.max(left_point, maxBoxPoint); + // intersection part + let un_intersection_l; + let un_intersection_r; + if (removeType == 'left') { + un_intersection_l = intersection_r; + un_intersection_r = left_point; + } else if (removeType == 'right') { + un_intersection_l = right_point; + un_intersection_r = intersection_l; + } + return [un_intersection_r, un_intersection_l]; + } + function get_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const { left_point, right_point, amount: L } = liquidity; + const { current_point } = poolDetail; + let curTokenXAmount = '0'; + let curTokenYAmount = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); + curTokenYAmount = getY(left_point, current_point, L, tokenY); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + curTokenXAmount = Big(amountx || '0') + .plus(curTokenXAmount || '0') + .toFixed(); + curTokenYAmount = Big(amounty || '0') + .plus(curTokenYAmount || '0') + .toFixed(); + } + // only y token + if (current_point >= right_point) { + curTokenYAmount = getY(left_point, right_point, L, tokenY); + } + // only x token + if (left_point > current_point) { + curTokenXAmount = getX(left_point, right_point, L, tokenX); + } + return [curTokenXAmount, curTokenYAmount]; + } + function get_min_x_y_amount_of_liquidity(liquidity: { + left_point: number; + right_point: number; + amount: string; + }) { + const rate = (100 - slippageTolerance) / 100; + const [token_x_amount, token_y_amount] = + get_x_y_amount_of_liquidity(liquidity); + const min_token_x_amount = Big(token_x_amount || 0) + .mul(rate) + .toFixed(); + const min_token_y_amount = Big(token_y_amount || 0) + .mul(rate) + .toFixed(); + return [min_token_x_amount, min_token_y_amount]; + } + const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; + return ( + + + {/* Title */} +
+ + + +
+ +
+
+ {/* Symbol pairs */} +
+
+
+ + +
+ + {tokens[1]?.symbol}/{tokens[0]?.symbol} + +
+ + {min_received_total_value} + +
+
+ {maxPoint && ( + + )} +
+ {/* Removing way */} +
+
+ +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('left'); + setMinBoxPrice(minPrice); + setMinBoxPoint(maxPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('right'); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(minPoint); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + setRemoveType('all'); + setMinBoxPrice(minPrice); + setMinBoxPoint(maxPoint); + setMaxBoxPrice(maxPrice); + setMaxBoxPoint(minPoint); + }} + > + +
+
+
+ {/* remove slider */} + {/* binBoxAmount 控制 */} + { + setBinBoxAmount(v.toString()); + }} + value={+binBoxAmount} + min={0} + max={+maxBinAmount} + step={1} + > + {/* Set points */} +
+ {/* min price */} +
+ + + + + { + const value = e.target.value; + setMinBoxPrice(value); + }} + inputMode="decimal" + onBlur={() => { + handleMinBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'right'} + > +
+ {/* max price */} +
+ + + + { + const value = e.target.value; + setMaxBoxPrice(value); + }} + value={maxBoxPrice} + inputMode="decimal" + onBlur={() => { + handleMaxBoxPriceToAppropriatePoint(); + }} + disabled={removeType !== 'left'} + > +
+ {/* bin amount */} +
+ + + + + +
+
+ {/* Slippage */} +
+ +
+ {/* Minimum received */} +
+
+ + + + +
+
+ + + + {min_received_y_amount} + +
+
+ + + + {min_received_x_amount} + +
+
+
+ {/* Button */} + {isSignedIn ? ( + + } + /> + + ) : ( +
+ +
+ )} +
+
+ ); +}; +export function IntegerInputComponent({ + value, + setValue, + disabled, + className, + max, + onBlur, +}: any) { + const removeLeadingZeros = (s: string) => { + const oldLen = s.length; + s = s.replace(/^0+/, ''); + + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + + if (max && Number(s) > max) { + return max; + } + + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + setValue(val); + }; + + return ( +
+ { + if (onBlur) { + onBlur(); + } else if (!target.value) { + setValue(1); + } + }} + onChange={({ target }) => { + handleChange(target.value); + }} + /> +
+ ); +} diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index ec9cb9bd2..e0b583b0d 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -8,22 +8,13 @@ import React, { import { ReturnIcon, - AddIcon, - SelectIcon, - BgIcon, - SwitchButton, AddButton, ReduceButton, - SwitchIcon, BoxDarkBg, SideIcon, InvalidIcon, WarningIcon, EmptyIcon, - WarningMark, - SwitchLRButton, - SwitchArrowL, - SwitchArrowR, } from '~components/icon/V3'; import { FormattedMessage, useIntl } from 'react-intl'; import { useHistory } from 'react-router-dom'; @@ -32,7 +23,6 @@ import { ButtonTextWrapper, ConnectToNearBtn, } from '~components/button/Button'; -import SelectToken from '~components/forms/SelectToken'; import { useTriTokens, useWhitelistTokens } from '../../state/token'; import { useTriTokenIdsOnRef } from '../../services/aurora/aurora'; import { @@ -40,13 +30,7 @@ import { ftGetBalance, ftGetTokenMetadata, } from '../../services/ft-contract'; -import { getTokenPriceList } from '../../services/indexer'; -import { - getBoostTokenPrices, - Seed, - FarmBoost, - classificationOfCoins, -} from '../../services/farm'; +import { getBoostTokenPrices } from '../../services/farm'; import { useTokenBalances, useDepositableBalance } from '../../state/token'; import Loading from '~components/layout/Loading'; import { @@ -54,8 +38,6 @@ import { add_liquidity, create_pool, PoolInfo, - get_pool_marketdepth, - regularizedPoint, batch_add_liquidity, } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; @@ -69,8 +51,6 @@ import { POINTLEFTRANGE, POINTRIGHTRANGE, useAddAndRemoveUrlHandle, - get_matched_seeds_for_dcl_pool, - get_all_seeds, get_pool_id, get_pool_name, openUrl, @@ -79,17 +59,16 @@ import { get_l_amount_by_condition, UserLiquidityInfo, reverse_price, + sort_tokens_by_base, } from '../../services/commonV3'; import { formatWithCommas, toPrecision, toReadableNumber, toNonDivisibleNumber, - checkAllocations, scientificNotationToString, getAllocationsLeastOne, toInternationalCurrencySystem, - ONLY_ZEROS, } from '~utils/numbers'; import { WalletContext } from '../../utils/wallets-integration'; import _, { forEach, set } from 'lodash'; @@ -100,7 +79,6 @@ import { getURLInfo } from '../../components/layout/transactionTipPopUp'; import { BlueCircleLoading } from '../../components/layout/Loading'; import { isMobile } from '../../utils/device'; import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; -import ReactSlider from 'react-slider'; import Big from 'big.js'; import { SelectTokenDCL } from '../../components/forms/SelectToken'; import { SliderCurColor } from '~components/icon/Info'; @@ -110,17 +88,14 @@ import { BidAskShape, } from '../Orderly/components/Common/Icons'; import DclChart from '../../components/d3Chart/DclChart'; -import { IChartData } from '../../components/d3Chart/interfaces'; import { IAddLiquidityInfo, IAddLiquidityInfoHelp, LiquidityShape, - PriceRangeModeType, } from './interfaces'; import { get_custom_config_for_chart, get_default_config_for_chart, - SLIDER_BIN_NUMBER, RADIUS_DEFAULT_NUMBER, max_nft_divisional_per_side, } from '../../components/d3Chart/config'; @@ -131,10 +106,6 @@ import { import { isInvalid } from '../../components/d3Chart/utils'; const LiquidityProviderData = createContext(null); export default function AddYourLiquidityPageV3() { - // return - return -} -function AddYourLiquidityPageV3Forward() { const [tokenX, setTokenX] = useState(null); const [tokenY, setTokenY] = useState(null); const [tokenXAmount, setTokenXAmount] = useState(''); @@ -147,7 +118,6 @@ function AddYourLiquidityPageV3Forward() { const [invalidRange, setInvalidRange] = useState(false); const [currentSelectedPool, setCurrentSelectedPool] = useState(null); - const [listPool, setListPool] = useState([]); const [tokenPriceList, setTokenPriceList] = useState>({}); const [currentPools, setCurrentPools] = @@ -156,18 +126,11 @@ function AddYourLiquidityPageV3Forward() { const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); const [feeBoxStatus, setFeeBoxStatus] = useState(true); - const [buttonSort, setButtonSort] = useState(false); const [selectHover, setSelectHover] = useState(false); const [hoverFeeBox, setHoverFeeBox] = useState(false); - // abandon - const [seed_list, set_seed_list] = useState(); - const [related_seeds, set_related_seeds] = useState([]); - - // new const [binNumber, setBinNumber] = useState(); const [liquidityShape, setLiquidityShape] = useState('Spot'); - const [topPairs, setTopPairs] = useState([]); const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); const [token_amount_tip, set_token_amount_tip] = @@ -179,24 +142,27 @@ function AddYourLiquidityPageV3Forward() { const [new_user_liquidities, set_new_user_liquidities] = useState< UserLiquidityInfo[] >([]); + const [pair_is_reverse, set_pair_is_reverse] = useState(false); // callBack handle useAddAndRemoveUrlHandle(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const nearBalance = useDepositableBalance('NEAR'); const history = useHistory(); const triTokenIds = useTriTokenIdsOnRef(); const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); const triTokens = useTriTokens(); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const nearBalance = useDepositableBalance('NEAR'); - const intl = useIntl(); const OPEN_CREATE_POOL_ENTRY = false; + const mobileDevice = isMobile(); + // init useEffect(() => { getBoostTokenPrices().then(setTokenPriceList); get_list_pools(); - get_seeds(); }, []); + + // get balance of tokenX and tokenY useEffect(() => { if (tokenX) { const tokenXId = tokenX.id; @@ -229,35 +195,34 @@ function AddYourLiquidityPageV3Forward() { } } }, [tokenX, tokenY, isSignedIn, nearBalance]); + + // get tokenX and tokenY by hash useEffect(() => { if (listPool.length > 0) { get_init_pool(); } }, [listPool]); + + // update hash by tokenX and tokenY useEffect(() => { if (currentSelectedPool && tokenX && tokenY) { const { fee } = currentSelectedPool; const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); history.replace(`#${link}`); - if (seed_list && currentSelectedPool.pool_id) { - get_optional_seeds(); - } } - }, [currentSelectedPool, tokenX, tokenY, seed_list]); + }, [currentSelectedPool, tokenX, tokenY]); + + // get currentPools and currentSelectedPool by tokenX and tokenY useEffect(() => { if (tokenX && tokenY) { searchPools(); } }, [tokenX, tokenY, tokenPriceList, listPool]); + + // get base info of currentSelectedPool useEffect(() => { - if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { - getTopPairs(); - } - }, [listPool, tokenPriceList]); - // new - useEffect(() => { - // init if (currentSelectedPool?.pool_id) { + console.log('000000000---pool_id', currentSelectedPool.pool_id); const { current_point, point_delta } = currentSelectedPool; const n = get_slot_number_in_a_bin(); const bin_width = n * point_delta; @@ -265,9 +230,12 @@ function AddYourLiquidityPageV3Forward() { SET_BIN_WIDTH(bin_width); setCurrentPoint(current_point); set_switch_pool_loading(false); + set_pair_is_reverse(is_reverse_fun()); + set_new_user_liquidities([]); } }, [currentSelectedPool]); - // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 + + // if one bin and inRange then spot mode can only be used useEffect(() => { set_only_suppport_spot_shape(false); if (currentSelectedPool) { @@ -294,57 +262,140 @@ function AddYourLiquidityPageV3Forward() { currentSelectedPool, liquidityShape, ]); + + // clean error tip useEffect(() => { set_token_amount_tip(null); }, [tokenXAmount, tokenYAmount, currentSelectedPool]); - async function getTopPairs() { - const listPromise = listPool.map(async (p: PoolInfo) => { - const token_x = p.token_x; - const token_y = p.token_y; - - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged) - .toFixed(); - const tvlx = - Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * - Number(pricey); - p.tvl = tvlx + tvly; - - return p; - }); - const list: PoolInfo[] = await Promise.all(listPromise); - list.sort((b: PoolInfo, a: PoolInfo) => { - return a.tvl - b.tvl; - }); - const top3 = list.slice(0, 3); - setTopPairs(top3); + function changeTokenXAmount(amount: string = '0') { + setTokenXAmount(amount); + if (!onlyAddXToken && liquidityShape === 'Spot') { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenYAmount(amount_result); + } } - if (!refTokens || !triTokens || !triTokenIds) return ; - async function get_seeds() { - const seeds = await get_all_seeds(); - set_seed_list(seeds); + function changeTokenYAmount(amount: string = '0') { + setTokenYAmount(amount); + if (!onlyAddYToken && liquidityShape === 'Spot') { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } } - function get_optional_seeds() { - const optional_seeds = get_matched_seeds_for_dcl_pool({ - seeds: seed_list, - pool_id: currentSelectedPool.pool_id, - }); - if (optional_seeds.length) { - set_related_seeds(optional_seeds); + function getTokenYAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + // X-->L + const L = + +amount * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); + // L-->current Y + const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); + // L--> Y + const Y = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1)); + const decimalsRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + + const Y_result = (Y + Yc) * decimalsRate; + return Y_result.toString(); + } + } + function getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; } else { - set_related_seeds([]); + let L; + // Yc-->L + if (leftPoint == currentPoint) { + L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); + } else { + // Y-->L + L = + +amount * + ((Math.sqrt(CONSTANT_D) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); + } + const X = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); + const decimalsRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const X_result = X * decimalsRate; + return X_result.toString(); + } + } + function get_slot_number_in_a_bin() { + const pool_id = currentSelectedPool?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + async function get_list_pools() { + const list: PoolInfo[] = await list_pools(); + + await Promise.all( + list.map(async (p: PoolInfo) => { + const token_x_metadata = await ftGetTokenMetadata(p.token_x); + const token_y_metadata = await ftGetTokenMetadata(p.token_y); + p.token_x_metadata = token_x_metadata; + p.token_y_metadata = token_y_metadata; + return p; + }) + ); + + if (list.length > 0) { + setListPool(list); } } async function get_init_pool() { @@ -364,19 +415,6 @@ function AddYourLiquidityPageV3Forward() { setTokenY(tokeny); } } - function goYourLiquidityPage() { - if (history.length == 2) { - history.push('/yourliquidity'); - } else { - history.goBack(); - } - } - async function get_list_pools() { - const list: PoolInfo[] = await list_pools(); - if (list.length > 0) { - setListPool(list); - } - } function searchPools() { const hash = decodeURIComponent(location.hash); let url_fee; @@ -493,6 +531,21 @@ function AddYourLiquidityPageV3Forward() { } } } + function is_reverse_fun() { + const { token_x_metadata, token_y_metadata } = currentSelectedPool; + if (token_x_metadata && token_y_metadata) { + const tokens = sort_tokens_by_base([token_x_metadata, token_y_metadata]); + return tokens[0].id !== token_x_metadata.id; + } + return false; + } + function goYourLiquidityPage() { + if (history.length == 2) { + history.push('/yourliquidity'); + } else { + history.goBack(); + } + } function switchSelectedFee(fee: number) { if (tokenX && tokenY && currentPools) { const pool = currentPools[fee]; @@ -504,329 +557,480 @@ function AddYourLiquidityPageV3Forward() { } } } - function changeTokenXAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenXAmount(amount); - /*if (sort) {*/ - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenYAmount(amount_result); - } - /*} else { - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); + /** + * step1 当数据发生改变 + * leftPoint, rightPoint 有效 + * tokenXAmount, tokenYAmount 至少有一个有值 + * ===> 可以触发 + * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 + * step3 把新增的liquidity传递给Chart组件, + * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 + * step5 疑问;?实时修改图表 会导致效率什么问题吗? + */ + function generate_new_user_chart() { + if ( + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + (+tokenXAmount > 0 || +tokenYAmount > 0) + ) { + let new_nfts: any; + if (liquidityShape == 'Spot') { + const new_nft = getLiquiditySpot(); + new_nfts = [new_nft]; + } else { + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; } - }*/ + const processed_new_nfts = process_liquidities(new_nfts); + set_new_user_liquidities(processed_new_nfts); + } else { + set_new_user_liquidities([]); + } } - function changeTokenYAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenYAmount(amount); - /*if (sort) {*/ - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); + + function getLiquiditySpot() { + const { pool_id } = currentSelectedPool; + return { + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + }; + } + + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; } - /*} else { - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenXAmount(amount_result); - } - }*/ } - function getTokenYAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, + /** + * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_curve({ + left_point, + right_point, }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; + left_point: number; + right_point: number; }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - // X-->L - const L = - +amount * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); - // L-->current Y - const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); - // L--> Y - const Y = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1)); - const decimalsRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ - const Y_result = (Y + Yc) * decimalsRate; - return Y_result.toString(); - } - } - function getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; } else { - let L; - // Yc-->L - if (leftPoint == currentPoint) { - L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); - } else { - // Y-->L - L = - +amount * - ((Math.sqrt(CONSTANT_D) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); - } - const X = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); - const decimalsRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const X_result = X * decimalsRate; - return X_result.toString(); - } - } - function pointChange({ - leftPoint, - rightPoint, - currentPoint, - }: { - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setInvalidRange(false); - setOnlyAddXToken(false); - setOnlyAddYToken(false); - // invalid point - if (leftPoint >= rightPoint) { - setInvalidRange(true); - setTokenXAmount(''); - setTokenYAmount(''); - return; - } - // can only add x token - if (leftPoint > currentPoint) { - setOnlyAddXToken(true); - if (sort) { - setTokenYAmount(''); - } else { - setTokenXAmount(''); - } - return; + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; } - // can only add y token - if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { - setOnlyAddYToken(true); - if (sort) { - setTokenXAmount(''); + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; } else { - setTokenYAmount(''); + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); } - return; + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 1).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; } - if (liquidityShape !== 'Spot') return; - if (sort) { - if (tokenXAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, + // 不同点4 + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == exclude_cur_total_nft_number) { + amount_y = dis + .mul(exclude_cur_total_nft_number + 1) + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', }); - setTokenXAmount(amount_result); } - } else { - if (tokenXAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - } - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; } - } - // start - function get_slot_number_in_a_bin() { - const pool_id = currentSelectedPool?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; } /** - * step1 当数据发生改变 - * leftPoint, rightPoint 有效 - * tokenXAmount, tokenYAmount 至少有一个有值 - * ===> 可以触发 - * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 - * step3 把新增的liquidity传递给Chart组件, - * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 - * step5 疑问;?实时修改图表 会导致效率什么问题吗? + * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns */ - function generate_new_user_chart() { - if ( - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - (+tokenXAmount > 0 || +tokenYAmount > 0) - ) { - let new_nfts: any; - if (liquidityShape == 'Spot') { - const new_nft = getLiquiditySpot(); - new_nfts = [new_nft]; + function get_y_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + // 不同点1 + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + // 不同点2 + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; } else { - new_nfts = getLiquidityForCurveAndBidAskMode(); - if (!new_nfts) return; + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); } - const processed_new_nfts = process_liquidities(new_nfts); - set_new_user_liquidities(processed_new_nfts); - } else { - set_new_user_liquidities([]); + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 2).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; } - } - function getLiquiditySpot() { - const { pool_id } = currentSelectedPool; - return { - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - token_x: tokenX, - token_y: tokenY, - // amount_x: tokenSort - // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - // amount_y: tokenSort - // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - // token_x: tokenSort ? tokenX : tokenY, - // token_y: tokenSort ? tokenY : tokenX, + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), }; - } - function formatWithCommas_for_tip(v: string, commas?: boolean) { - const v_big = Big(v || 0); - let v_temp; - if (v_big.lt(0.001)) { - v_temp = v_big.toFixed(6, 3); - } else { - if (commas) { - v_temp = formatWithCommas(v_big.toFixed(3, 3)); - } else { - v_temp = v_big.toFixed(3, 3); + + // 不同点5 + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == 0) { + amount_x = dis + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); } } - return v_temp; + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; } - function getLiquidityForCurveAndBidAskMode() { + /** + * curve 和 bid ask 上升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_rise_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + const left_i_point = left_point + nftWidth * i; + let right_i_point; + if (i == total_nft_number - 1) { + right_i_point = right_point; + } else { + right_i_point = left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + function formatWithCommas_for_tip(v: string, commas?: boolean) { + const v_big = Big(v || 0); + let v_temp; + if (v_big.lt(0.001)) { + v_temp = v_big.toFixed(6, 3); + } else { + if (commas) { + v_temp = formatWithCommas(v_big.toFixed(3, 3)); + } else { + v_temp = v_big.toFixed(3, 3); + } + } + return v_temp; + } + function getLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, }); } if (onlyAddXToken) { @@ -1199,13 +1403,149 @@ function AddYourLiquidityPageV3Forward() { }; } /** - * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * curve 和 bid ask 下降升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_decline_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐下降模式 + * nft 从右往左计算 + * e.g. + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + let left_i_point; + let right_i_point; + if (i == total_nft_number - 1) { + left_i_point = left_point; + } else { + left_i_point = right_point - nftWidth * (i + 1); + } + right_i_point = right_point - nftWidth * i; + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + /** + * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 + */ + function process_liquidities(liquidities: IAddLiquidityInfo[]) { + const { pool_id } = currentSelectedPool; + const new_liquidities: UserLiquidityInfo[] = []; + liquidities.forEach((l: IAddLiquidityInfo) => { + const { left_point, right_point, amount_x, amount_y } = l; + const L = get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount: amount_x, + token_y_amount: amount_y, + poolDetail: currentSelectedPool, + }); + new_liquidities.push({ + pool_id, + left_point, + right_point, + amount: L, + }); + }); + return new_liquidities; + } + /** + * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 * 此区间bin的数量要求 > 1 * 双边 * @param param0 * @returns */ - function get_x_nfts_contain_current_curve({ + function get_x_nfts_contain_current_bid_ask({ left_point, right_point, }: { @@ -1223,150 +1563,8 @@ function AddYourLiquidityPageV3Forward() { */ /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 1).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == exclude_cur_total_nft_number) { - amount_y = dis - .mul(exclude_cur_total_nft_number + 1) - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 + * 从左往右逐渐上升模式 + * 从左往右计算 * e.g. * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; @@ -1480,3785 +1678,529 @@ function AddYourLiquidityPageV3Forward() { ), }; } - /** - * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_bid_ask({ - left_point, - right_point, + function pointChange({ + leftPoint, + rightPoint, }: { - left_point: number; - right_point: number; + leftPoint: number; + rightPoint: number; }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ + setInvalidRange(false); + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setLeftPoint(leftPoint); + setRightPoint(rightPoint); + // invalid point + if (leftPoint >= rightPoint) { + setInvalidRange(true); + setTokenXAmount(''); + setTokenYAmount(''); + return; + } + // can only add x token + if (leftPoint > currentPoint) { + setOnlyAddXToken(true); + setTokenYAmount(''); + return; + } + // can only add y token + if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { + setOnlyAddYToken(true); + setTokenXAmount(''); + return; + } + if (liquidityShape !== 'Spot') return; + if (pair_is_reverse) { + if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } else if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } + } else { + if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + } + if (!refTokens || !triTokens || !triTokenIds) return ; + console.log('999999999999-pair_is_reverse--9999999999', pair_is_reverse); + return ( + dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ + tokenXAmount, + setTokenXAmount, + tokenYAmount, + setTokenYAmount, - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - // 不同点1 - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; + currentSelectedPool, + setTokenPriceList, - // 不同点2 - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; + tokenPriceList, + history, + OPEN_CREATE_POOL_ENTRY, + mobileDevice, - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 2).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == 0) { - amount_x = dis - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 和 bid ask 上升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_rise_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - const left_i_point = left_point + nftWidth * i; - let right_i_point; - if (i == total_nft_number - 1) { - right_i_point = right_point; - } else { - right_i_point = left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - /** - * curve 和 bid ask 下降升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_decline_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐下降模式 - * nft 从右往左计算 - * e.g. - * 公式推导: - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - let left_i_point; - let right_i_point; - if (i == total_nft_number - 1) { - left_i_point = left_point; - } else { - left_i_point = right_point - nftWidth * (i + 1); - } - right_i_point = right_point - nftWidth * i; - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - function formula_of_token_x(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ); - } - function formula_of_token_y(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - } - /** - * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 - */ - function process_liquidities(liquidities: IAddLiquidityInfo[]) { - const { pool_id } = currentSelectedPool; - const new_liquidities: UserLiquidityInfo[] = []; - liquidities.forEach((l: IAddLiquidityInfo) => { - const { left_point, right_point, amount_x, amount_y } = l; - const L = get_l_amount_by_condition({ - left_point, - right_point, - token_x_amount: amount_x, - token_y_amount: amount_y, - poolDetail: currentSelectedPool, - }); - new_liquidities.push({ - pool_id, - left_point, - right_point, - amount: L, - }); - }); - return new_liquidities; - } - function pointAndshapeChange() { - set_token_amount_tip(null); - if (liquidityShape == 'Spot') { - if (tokenXAmount) { - changeTokenXAmount(tokenXAmount); - } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount); - } - } - } - const tokenSort = tokenX?.id == currentSelectedPool?.token_x; - const mobileDevice = isMobile(); - - return ( - -
- {/* 缩略图 */} - {/* */} - {/* 详情页图 */} - {/* */} - {/* 添加页图 */} - {/* */} - {/* 用户流动性图表*/} - {/* */} - {/* 删除流动性图表 从右侧部分删除 */} - {/* */} - {/* 删除流动性图表 从左侧部分删除 */} - {/* */} - {/* 删除流动性图表 全部删除 */} - {/* */} -
- - {/* mobile head */} -
-
- -
- - - -
-
- {/* pc head */} -
{ - history.goBack(); - }} - > -
- -
- - - -
- - {/* content */} -
-
- {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} - {/* right area */} -
-
-
- -
- - { - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
- - - - {token_amount_tip ? ( -
- - {token_amount_tip} -
- ) : null} - -
-
- -
- -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - - -
{ - setHoverFeeBox(false); - }} - onMouseEnter={() => { - setHoverFeeBox(true); - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
- -
- -
- -
- {[SpotShape, CurveShape, BidAskShape].map( - (Shape, index: number) => { - let disabled = false; - if ( - (index == 1 || index == 2) && - only_suppport_spot_shape - ) { - disabled = true; - } - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - if (index === 0) setLiquidityShape('Spot'); - else if (index === 1 && !only_suppport_spot_shape) - setLiquidityShape('Curve'); - else if (index == 2 && !only_suppport_spot_shape) - setLiquidityShape('BidAsk'); - }} - > - - - - {index === 0 && ( - - )} - {index === 1 && ( - - )} - - {index === 2 && ( - - )} - -
- ); - } - )} -
- {/* new user chart part */} - {isSignedIn && currentSelectedPool ? ( -
-
-
- -
-
- Generate -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- ) : null} - - {currentSelectedPool && currentSelectedPool.pool_id && ( - - )} -
- - -
-
-
-
-
-
- ); -} -// todo -function AddYourLiquidityPageV3Reverse() { - const [tokenX, setTokenX] = useState(null); - const [tokenY, setTokenY] = useState(null); - const [tokenXAmount, setTokenXAmount] = useState(''); - const [tokenYAmount, setTokenYAmount] = useState(''); - const [leftPoint, setLeftPoint] = useState(); - const [rightPoint, setRightPoint] = useState(); - const [currentPoint, setCurrentPoint] = useState(); - const [onlyAddYToken, setOnlyAddYToken] = useState(false); - const [onlyAddXToken, setOnlyAddXToken] = useState(false); - const [invalidRange, setInvalidRange] = useState(false); - const [currentSelectedPool, setCurrentSelectedPool] = - useState(null); - - const [listPool, setListPool] = useState([]); - const [tokenPriceList, setTokenPriceList] = useState>({}); - const [currentPools, setCurrentPools] = - useState>(null); - const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); - const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - - const [feeBoxStatus, setFeeBoxStatus] = useState(true); - const [buttonSort, setButtonSort] = useState(false); - const [selectHover, setSelectHover] = useState(false); - const [hoverFeeBox, setHoverFeeBox] = useState(false); - - // abandon - const [seed_list, set_seed_list] = useState(); - const [related_seeds, set_related_seeds] = useState([]); - - // new - const [binNumber, setBinNumber] = useState(); - const [liquidityShape, setLiquidityShape] = useState('Spot'); - const [topPairs, setTopPairs] = useState([]); - const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); - const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); - const [token_amount_tip, set_token_amount_tip] = - useState(); - const [only_suppport_spot_shape, set_only_suppport_spot_shape] = - useState(false); - const [switch_pool_loading, set_switch_pool_loading] = - useState(true); - const [new_user_liquidities, set_new_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - - // callBack handle - useAddAndRemoveUrlHandle(); - const history = useHistory(); - const triTokenIds = useTriTokenIdsOnRef(); - const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); - const triTokens = useTriTokens(); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const nearBalance = useDepositableBalance('NEAR'); - const intl = useIntl(); - const OPEN_CREATE_POOL_ENTRY = false; - - useEffect(() => { - getBoostTokenPrices().then(setTokenPriceList); - get_list_pools(); - get_seeds(); - }, []); - useEffect(() => { - if (tokenX) { - const tokenXId = tokenX.id; - if (tokenXId) { - if (isSignedIn) { - ftGetBalance(tokenXId).then((available: string) => - setTokenXBalanceFromNear( - toReadableNumber( - tokenX.decimals, - tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - if (tokenY) { - const tokenYId = tokenY.id; - if (tokenYId) { - if (isSignedIn) { - ftGetBalance(tokenYId).then((available: string) => - setTokenYBalanceFromNear( - toReadableNumber( - tokenY.decimals, - tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - }, [tokenX, tokenY, isSignedIn, nearBalance]); - useEffect(() => { - if (listPool.length > 0) { - get_init_pool(); - } - }, [listPool]); - useEffect(() => { - if (currentSelectedPool && tokenX && tokenY) { - const { fee } = currentSelectedPool; - const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); - history.replace(`#${link}`); - if (seed_list && currentSelectedPool.pool_id) { - get_optional_seeds(); - } - } - }, [currentSelectedPool, tokenX, tokenY, seed_list]); - useEffect(() => { - if (tokenX && tokenY) { - searchPools(); - } - }, [tokenX, tokenY, tokenPriceList, listPool]); - useEffect(() => { - if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { - getTopPairs(); - } - }, [listPool, tokenPriceList]); - // new - useEffect(() => { - // init - if (currentSelectedPool?.pool_id) { - const { current_point, point_delta } = currentSelectedPool; - const n = get_slot_number_in_a_bin(); - const bin_width = n * point_delta; - SET_SLOT_NUMBER(n); - SET_BIN_WIDTH(bin_width); - setCurrentPoint(current_point); - set_switch_pool_loading(false); - } - }, [currentSelectedPool]); - // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 - useEffect(() => { - set_only_suppport_spot_shape(false); - if (currentSelectedPool) { - const { point_delta } = currentSelectedPool; - if (leftPoint <= currentPoint && rightPoint > currentPoint) { - // inrange - const binWidth = SLOT_NUMBER * point_delta; - const binNumber = (rightPoint - leftPoint) / binWidth; - if (binNumber == 1) { - setLiquidityShape('Spot'); - set_only_suppport_spot_shape(true); - if (tokenXAmount) { - changeTokenXAmount(tokenXAmount); - } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount); - } - } - } - } - }, [ - leftPoint, - rightPoint, - currentPoint, - currentSelectedPool, - liquidityShape, - ]); - useEffect(() => { - set_token_amount_tip(null); - }, [tokenXAmount, tokenYAmount, currentSelectedPool]); - async function getTopPairs() { - const listPromise = listPool.map(async (p: PoolInfo) => { - const token_x = p.token_x; - const token_y = p.token_y; - - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged) - .toFixed(); - const tvlx = - Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * - Number(pricey); - - p.tvl = tvlx + tvly; - - return p; - }); - const list: PoolInfo[] = await Promise.all(listPromise); - list.sort((b: PoolInfo, a: PoolInfo) => { - return a.tvl - b.tvl; - }); - const top3 = list.slice(0, 3); - setTopPairs(top3); - } - if (!refTokens || !triTokens || !triTokenIds) return ; - async function get_seeds() { - const seeds = await get_all_seeds(); - set_seed_list(seeds); - } - function get_optional_seeds() { - const optional_seeds = get_matched_seeds_for_dcl_pool({ - seeds: seed_list, - pool_id: currentSelectedPool.pool_id, - }); - if (optional_seeds.length) { - set_related_seeds(optional_seeds); - } else { - set_related_seeds([]); - } - } - async function get_init_pool() { - let tokenx_id, tokeny_id, pool_fee; - const hash = decodeURIComponent(location.hash); - if (hash.indexOf('<>') > -1) { - // new link - [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); - } else { - // old link - [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); - } - if (tokenx_id && tokeny_id && pool_fee) { - const tokenx = await ftGetTokenMetadata(tokenx_id); - const tokeny = await ftGetTokenMetadata(tokeny_id); - setTokenX(tokenx); - setTokenY(tokeny); - } - } - function goYourLiquidityPage() { - if (history.length == 2) { - history.push('/yourliquidity'); - } else { - history.goBack(); - } - } - async function get_list_pools() { - const list: PoolInfo[] = await list_pools(); - if (list.length > 0) { - setListPool(list); - } - } - function searchPools() { - const hash = decodeURIComponent(location.hash); - let url_fee; - if (hash.indexOf('<>') > -1) { - url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; - } else { - url_fee = +hash.slice(1).split('|')[2]; - } - const currentPoolsMap = {}; - if (listPool.length > 0 && tokenX && tokenY) { - set_switch_pool_loading(true); - const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { - // TODO 增加pool 状态的判断 - const { token_x, token_y, state } = pool; - if ( - (token_x == tokenX.id && token_y == tokenY.id) || - (token_x == tokenY.id && token_y == tokenX.id) - ) - return true; - }); - if (availablePools.length > 0) { - /*** percent start */ - const tvlList: number[] = []; - availablePools.map((p: PoolInfo) => { - const { - total_x, - total_y, - token_x, - token_y, - total_fee_x_charged, - total_fee_y_charged, - } = p; - const firstToken = tokenX.id == token_x ? tokenX : tokenY; - const secondToken = tokenY.id == token_y ? tokenY : tokenX; - const firstTokenPrice = - (tokenPriceList && - tokenPriceList[firstToken.id] && - tokenPriceList[firstToken.id].price) || - '0'; - const secondTokenPrice = - (tokenPriceList && - tokenPriceList[secondToken.id] && - tokenPriceList[secondToken.id].price) || - '0'; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged || 0) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged || 0) - .toFixed(); - const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) - .times(firstTokenPrice) - .toNumber(); - const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) - .times(secondTokenPrice) - .toNumber(); - const totalTvl = tvlx + tvly; - p.tvl = totalTvl; - tvlList.push(totalTvl); - return p; - }); - const sumOfTvlList = _.sum(tvlList); - const tvlPercents = - sumOfTvlList === 0 - ? ['0', '0', '0', '0'] - : availablePools.map((p: PoolInfo) => - scientificNotationToString( - ((p.tvl / sumOfTvlList) * 100).toString() - ) - ); - const nonZeroIndexes: number[] = []; - tvlPercents.forEach((p, index) => { - if (Number(p) > 0) { - nonZeroIndexes.push(index); - } - }); - const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); - const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); - const finalPercents = tvlPercents.map((p, index) => { - if (nonZeroIndexes.includes(index)) { - const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; - return newP; - } - return p; - }); - const maxPercent = _.max(finalPercents); - let maxPercentPool; - availablePools.forEach((pool: PoolInfo, index) => { - const f = pool.fee; - const temp: PoolInfo = { - ...pool, - percent: finalPercents[index], - tvl: tvlList[index], - }; - currentPoolsMap[f] = temp; - if (finalPercents[index] == maxPercent) { - maxPercentPool = temp; - } - }); - // url-fee-pool - const urlFeePool = url_fee - ? currentPoolsMap[url_fee] || { fee: url_fee } - : null; - setCurrentPools(currentPoolsMap); - setCurrentSelectedPool(urlFeePool || maxPercentPool); - } else { - setCurrentPools({}); - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } else { - setCurrentPools({}); - if (tokenX && tokenY) { - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } - } - function switchSelectedFee(fee: number) { - if (tokenX && tokenY && currentPools) { - const pool = currentPools[fee]; - setCurrentSelectedPool(pool || { fee }); - if (!pool) { - setOnlyAddXToken(false); - setOnlyAddYToken(false); - setInvalidRange(false); - } - } - } - function changeTokenXAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenXAmount(amount); - /*if (sort) {*/ - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenYAmount(amount_result); - } - /*} else { - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } - }*/ - } - function changeTokenYAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenYAmount(amount); - /*if (sort) {*/ - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - /*} else { - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenXAmount(amount_result); - } - }*/ - } - function getTokenYAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - // X-->L - const L = - +amount * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); - // L-->current Y - const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); - // L--> Y - const Y = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1)); - const decimalsRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - - const Y_result = (Y + Yc) * decimalsRate; - return Y_result.toString(); - } - } - function getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - let L; - // Yc-->L - if (leftPoint == currentPoint) { - L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); - } else { - // Y-->L - L = - +amount * - ((Math.sqrt(CONSTANT_D) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); - } - const X = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); - const decimalsRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const X_result = X * decimalsRate; - return X_result.toString(); - } - } - function pointChange({ - leftPoint, - rightPoint, - currentPoint, - }: { - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setInvalidRange(false); - setOnlyAddXToken(false); - setOnlyAddYToken(false); - // invalid point - if (leftPoint >= rightPoint) { - setInvalidRange(true); - setTokenXAmount(''); - setTokenYAmount(''); - return; - } - // can only add x token - if (leftPoint > currentPoint) { - setOnlyAddXToken(true); - if (sort) { - setTokenYAmount(''); - } else { - setTokenXAmount(''); - } - return; - } - // can only add y token - if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { - setOnlyAddYToken(true); - if (sort) { - setTokenXAmount(''); - } else { - setTokenYAmount(''); - } - return; - } - - if (liquidityShape !== 'Spot') return; - if (sort) { - if (tokenXAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } else { - if (tokenXAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - } - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; - } - } - // start - function get_slot_number_in_a_bin() { - const pool_id = currentSelectedPool?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - /** - * step1 当数据发生改变 - * leftPoint, rightPoint 有效 - * tokenXAmount, tokenYAmount 至少有一个有值 - * ===> 可以触发 - * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 - * step3 把新增的liquidity传递给Chart组件, - * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 - * step5 疑问;?实时修改图表 会导致效率什么问题吗? - */ - function generate_new_user_chart() { - if ( - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - (+tokenXAmount > 0 || +tokenYAmount > 0) - ) { - let new_nfts: any; - if (liquidityShape == 'Spot') { - const new_nft = getLiquiditySpot(); - new_nfts = [new_nft]; - } else { - new_nfts = getLiquidityForCurveAndBidAskMode(); - if (!new_nfts) return; - } - const processed_new_nfts = process_liquidities(new_nfts); - set_new_user_liquidities(processed_new_nfts); - } else { - set_new_user_liquidities([]); - } - } - function getLiquiditySpot() { - const { pool_id } = currentSelectedPool; - return { - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - token_x: tokenX, - token_y: tokenY, - // amount_x: tokenSort - // ? toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - // : toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - // amount_y: tokenSort - // ? toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - // : toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - // token_x: tokenSort ? tokenX : tokenY, - // token_y: tokenSort ? tokenY : tokenX, - }; - } - function formatWithCommas_for_tip(v: string, commas?: boolean) { - const v_big = Big(v || 0); - let v_temp; - if (v_big.lt(0.001)) { - v_temp = v_big.toFixed(6, 3); - } else { - if (commas) { - v_temp = formatWithCommas(v_big.toFixed(3, 3)); - } else { - v_temp = v_big.toFixed(3, 3); - } - } - return v_temp; - } - function getLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_x_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 - * step2 分配好后,获得对右侧的最小token数量要求 - * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 - */ - const { point_delta, current_point } = currentSelectedPool; - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'floor' - ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'ceil' - ); - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(null); - if (liquidityShape == 'Curve') { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_curve({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 1 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_decline_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_curve({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 2 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } else { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_bid_ask({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 3 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_bid_ask({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 4 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } - } - return nftList; - } - /** - * curve 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == exclude_cur_total_nft_number) { - amount_x = dis - .mul(exclude_cur_total_nft_number + 1) - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 1).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == exclude_cur_total_nft_number) { - amount_y = dis - .mul(exclude_cur_total_nft_number + 1) - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 2).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == 0) { - amount_y = dis - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - // 不同点1 - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - // 不同点2 - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 2).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == 0) { - amount_x = dis - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 和 bid ask 上升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_rise_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - const left_i_point = left_point + nftWidth * i; - let right_i_point; - if (i == total_nft_number - 1) { - right_i_point = right_point; - } else { - right_i_point = left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - /** - * curve 和 bid ask 下降升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_decline_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐下降模式 - * nft 从右往左计算 - * e.g. - * 公式推导: - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - let left_i_point; - let right_i_point; - if (i == total_nft_number - 1) { - left_i_point = left_point; - } else { - left_i_point = right_point - nftWidth * (i + 1); - } - right_i_point = right_point - nftWidth * i; - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - function formula_of_token_x(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ); - } - function formula_of_token_y(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - } - /** - * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 - */ - function process_liquidities(liquidities: IAddLiquidityInfo[]) { - const { pool_id } = currentSelectedPool; - const new_liquidities: UserLiquidityInfo[] = []; - liquidities.forEach((l: IAddLiquidityInfo) => { - const { left_point, right_point, amount_x, amount_y } = l; - const L = get_l_amount_by_condition({ - left_point, - right_point, - token_x_amount: amount_x, - token_y_amount: amount_y, - poolDetail: currentSelectedPool, - }); - new_liquidities.push({ - pool_id, - left_point, - right_point, - amount: L, - }); - }); - return new_liquidities; - } - function pointAndshapeChange() { - set_token_amount_tip(null); - if (liquidityShape == 'Spot') { - if (tokenXAmount) { - changeTokenXAmount(tokenXAmount); - } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount); - } - } - } - const tokenSort = tokenX?.id == currentSelectedPool?.token_x; - const mobileDevice = isMobile(); - // todo - return ( - - {/* mobile head */} -
-
- -
- - - -
-
- {/* pc head */} -
{ - history.goBack(); - }} - > -
- -
- - - -
- - {/* content */} -
-
- {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} - {/* right area */} -
-
-
- -
- - { - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
- - - - {token_amount_tip ? ( -
- - {token_amount_tip} -
- ) : null} - -
-
- -
- -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - - -
{ - setHoverFeeBox(false); - }} - onMouseEnter={() => { - setHoverFeeBox(true); - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
- -
- -
- -
- {[SpotShape, CurveShape, BidAskShape].map( - (Shape, index: number) => { - let disabled = false; - if ( - (index == 1 || index == 2) && - only_suppport_spot_shape - ) { - disabled = true; - } - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - if (index === 0) setLiquidityShape('Spot'); - else if (index === 1 && !only_suppport_spot_shape) - setLiquidityShape('Curve'); - else if (index == 2 && !only_suppport_spot_shape) - setLiquidityShape('BidAsk'); - }} - > - - - - {index === 0 && ( - - )} - {index === 1 && ( - - )} - - {index === 2 && ( - - )} - -
- ); - } - )} -
- {/* new user chart part */} - {isSignedIn && currentSelectedPool ? ( -
-
-
- -
-
- Generate -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- {/* */} -
- )} -
- ) : null} - - {currentSelectedPool && currentSelectedPool.pool_id && ( - - )} -
- - -
-
-
-
-
-
- ); -} -/** - * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 传递的时候只传实际使用值 - * @returns - */ -function AddLiquidityButton() { - const { - currentSelectedPool, - tokenX, - tokenY, - binNumber, - SLOT_NUMBER, - leftPoint, - rightPoint, - currentPoint, - liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - getLiquiditySpot, - getLiquidityForCurveAndBidAskMode, - } = useContext(LiquidityProviderData); - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - - function addLiquiditySpot() { - setAddLiquidityButtonLoading(true); - const new_liquidity = getLiquiditySpot(); - add_liquidity(new_liquidity); - } - function addLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - nftList = getLiquidityForCurveAndBidAskMode(); - if (!nftList) { - setAddLiquidityButtonLoading(false); - return; - } - /** - * 计算出 nftList token x tokeny 的数量,这是需要的总数量 - * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 - * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 - */ - let last_total_needed_token_x_amount = Big(0); - let last_total_needed_token_y_amount = Big(0); - nftList.forEach((nft: IAddLiquidityInfo) => { - const { amount_x, amount_y } = nft; - last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( - amount_x || 0 - ); - last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( - amount_y || 0 - ); - }); - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: last_total_needed_token_x_amount.toFixed(), - amount_y: last_total_needed_token_y_amount.toFixed(), - }); - } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - } - function getButtonText() { - let txt: any = ( - - ); - if (invalidRange) { - txt = ( - - ); - } else if ( - (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || - (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || - (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - !onlyAddXToken && - !onlyAddYToken && - (+tokenXAmount == 0 || +tokenYAmount == 0) - ) { - txt = ( - - ); - } else if ( - +tokenXAmount > 0 && - new BigNumber(tokenXAmount).isGreaterThan( - getMax(tokenX, tokenXBalanceFromNear) - ) - ) { - txt = ( - - ); - } else if ( - +tokenYAmount > 0 && - new BigNumber(tokenYAmount).isGreaterThan( - getMax(tokenY, tokenYBalanceFromNear) - ) - ) { - txt = ( - - ); - } - return txt; - } - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - if (tokenSort) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } else { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - } else if (onlyAddYToken) { - if (tokenSort) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } else { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - return !(condition1 && condition2); - } - const isAddLiquidityDisabled = getButtonStatus(); - - const add_lp_func = - liquidityShape === 'Spot' - ? addLiquiditySpot - : addLiquidityForCurveAndBidAskMode; - - return ( -
- {isSignedIn ? ( - - <>{getButtonText()}} - /> - - ) : ( - - )} -
- ); -} -function SetPointsComponent() { - const { - binNumber, - setBinNumber, - currentSelectedPool, - tokenX, - tokenY, - tokenXAmount, - tokenYAmount, - pointAndshapeChange, - - pointChange, - currentPoint, - liquidityShape, - - leftPoint, - setLeftPoint, - rightPoint, - setRightPoint, - - SLOT_NUMBER, - BIN_WIDTH, - - switch_pool_loading, - - isSignedIn, - } = useContext(LiquidityProviderData); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const [radius, setRadius] = useState(); - const [targetCustomPrice, setTargetCustomPrice] = useState(''); - const [leftCustomPrice, setLeftCustomPrice] = useState(''); - const [rightCustomPrice, setRightCustomPrice] = useState(''); - const [targetPoint, setTargetPoint] = useState(); - - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [targetInputStatus, setTargetInputStatus] = useState(false); - const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - - const [slider_point_min, set_slider_point_min] = useState(); - const [slider_point_max, set_slider_point_max] = useState(); - - const [slider_left_value, set_slider_left_value] = useState(); - const [slider_right_value, set_slider_right_value] = useState(); - - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - const tokenSort = tokenX.id == currentSelectedPool.token_x; - - // init - useEffect(() => { - if (currentSelectedPool?.pool_id && !switch_pool_loading) { - const { current_point } = currentSelectedPool; - const right_point = handlePointToAppropriatePoint( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; - setTargetPoint(current_point); - setRadius(RADIUS_DEFAULT_NUMBER); - setLeftPoint(left_point); - setRightPoint(right_point); - set_slider_point_range(); - setPriceRangeMode('by_range'); - setChartTab('liquidity'); - } - }, [currentSelectedPool, switch_pool_loading]); + pair_is_reverse, - // 中文 左侧改变===》点位 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - // effect bin - const diff = rightPoint - leftPoint; - const bin_number_temp = diff / BIN_WIDTH; - setBinNumber(bin_number_temp); - // effect slider - const slider_left_value = get_slider_value_by_point(leftPoint); - const slider_right_value = get_slider_value_by_point(rightPoint); + leftPoint, + setLeftPoint, + rightPoint, + setRightPoint, - set_slider_left_value(slider_left_value); - set_slider_right_value(slider_right_value); - // effect right area - pointChange({ leftPoint, rightPoint, currentPoint }); - } - }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); - // 数据有变动==》去掉token 太少提示 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - pointAndshapeChange(); - } - }, [liquidityShape, leftPoint, rightPoint]); + get_slot_number_in_a_bin, + binNumber, + setBinNumber, + pointChange, + setCurrentSelectedPool, + currentPoint, + SLOT_NUMBER, + BIN_WIDTH, + liquidityShape, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + isSignedIn, + token_amount_tip, + set_token_amount_tip, + switch_pool_loading, - // 修改bin --> 合适的右点位 --->合适的bin - function changeBin(bin: number) { - let appropriate_right_point = leftPoint + BIN_WIDTH * bin; - if (appropriate_right_point > POINTRIGHTRANGE) { - appropriate_right_point = POINTRIGHTRANGE; - } - const appropriate_bin_number = - (appropriate_right_point - leftPoint) / BIN_WIDTH; - setRightPoint(appropriate_right_point); - setBinNumber(appropriate_bin_number); - } + get_y_nfts_contain_current_curve, + get_x_nfts_contain_current_curve, + get_x_nfts_contain_current_bid_ask, + get_y_nfts_contain_current_bid_ask, + get_rise_pattern_nfts, + get_decline_pattern_nfts, + formula_of_token_x, + formula_of_token_y, - // 修改radius-->合适的左右点位 --->合适的radius - function changeRadius(radius: number) { - let appropriate_right_point = handlePointToAppropriatePoint( - targetPoint + BIN_WIDTH * radius - ); - let appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - const appropriate_radius = - (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - setRadius(appropriate_radius); - } - // 修改 targetPrice-->合适的左右点位--->合适的targetPrice - function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_target_point = handlePriceToAppropriatePoint(price); - const appropriate_right_point = handlePointToAppropriatePoint( - appropriate_target_point + BIN_WIDTH * radius - ); - const appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - return appropriate_target_point; - } - // 设置slider可以操作的point 左右点位区间 - function set_slider_point_range() { - const { current_point } = currentSelectedPool; - const max_point = handlePointToAppropriatePoint( - current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) - ); - const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; - set_slider_point_min(min_point); - set_slider_point_max(max_point); - } - function get_slider_value_by_point(point: number) { - const value = (point - slider_point_min) / BIN_WIDTH; - return value; - } - function get_point_by_slider_value(v: number) { - const new_point = slider_point_min + v * BIN_WIDTH; - return new_point; - } - function handlePointToAppropriatePoint(point: number) { - const { point_delta } = currentSelectedPool; - return getBinPointByPoint(point_delta, SLOT_NUMBER, point); - } - function handlePriceToAppropriatePoint(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const appropriate_point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return appropriate_point; - } - function getLeftPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(leftPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getRightPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(rightPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getTargetPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(targetPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getPair() { - if (tokenSort) { - return `(${tokenX.symbol}/${tokenY.symbol})`; - } else { - return `(${tokenY.symbol}/${tokenX.symbol})`; - } - } - return ( -
- {/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - setChartTab('yours'); - }} - > - Yours - -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( - - )} -
- {isSignedIn && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- {/* set price range area */} -
- {/* price range mode area */} -
-
- - - - {getPair()} - -
- -
- { - setPriceRangeMode('by_range'); - }} - > - - - { - setPriceRangeMode('by_radius'); - changeRadius(radius); - }} - > - - -
-
- {/* content */} - {/*
+ {/* head */} +
{ + history.goBack(); + }} > - -
*/} -
- {/* target price input box */}
- - - - +
+ + + +
- {/* radius input box */} -
- - - - -
+ {/* content */} +
+
+ {/* no Data */} + {currentSelectedPool ? null : } + + {/* empty pool */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + + {/* add Liquidity part */} + + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + + {/* right area */} +
+
+
+ +
+ + { + setTokenX(token); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+
+ + +
+ {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null} + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + setHoverFeeBox(false); + }} + onMouseEnter={() => { + setHoverFeeBox(true); + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ +
+ +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1 && !only_suppport_spot_shape) + setLiquidityShape('Curve'); + else if (index == 2 && !only_suppport_spot_shape) + setLiquidityShape('BidAsk'); + }} + > + - {/* min price input box */} -
- - - - -
+ + {index === 0 && ( + + )} + {index === 1 && ( + + )} - {/* max price input box */} -
- - - - -
+ {index === 2 && ( + + )} +
+
+ ); + } + )} +
+ {/* user chart part */} + {isSignedIn && currentSelectedPool ? ( +
+
+
+ +
+
+ Generate +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ ) : null} - {/* bin number input box */} -
- - - - + {currentSelectedPool && currentSelectedPool.pool_id && ( + + )} +
+ + +
+
- {/* tip in foot */} -
- *Only NEAR is needed in the price range you choose. -
-
+ ); } -// todo -function SetPointsComponentReverse() { +function PointsComponent() { const { binNumber, setBinNumber, currentSelectedPool, tokenX, tokenY, - pointAndshapeChange, + set_token_amount_tip, pointChange, - currentPoint, liquidityShape, - leftPoint, - setLeftPoint, - rightPoint, - setRightPoint, - SLOT_NUMBER, BIN_WIDTH, switch_pool_loading, isSignedIn, + pair_is_reverse, } = useContext(LiquidityProviderData); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' @@ -5272,19 +2214,31 @@ function SetPointsComponentReverse() { const [leftInputStatus, setLeftInputStatus] = useState(false); const [rightInputStatus, setRightInputStatus] = useState(false); const [targetInputStatus, setTargetInputStatus] = useState(false); + + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - const token_x_decimals = tokenX.decimals - const token_y_decimals = tokenY.decimals + const token_x_decimals = tokenX.decimals; + const token_y_decimals = tokenY.decimals; // init useEffect(() => { if (currentSelectedPool?.pool_id && !switch_pool_loading) { const { current_point } = currentSelectedPool; - const left_point = get_bin_point_by_point( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - const right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + let left_point, right_point; + if (pair_is_reverse) { + left_point = get_bin_point_by_point( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + } else { + right_point = get_bin_point_by_point( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + } setTargetPoint(current_point); setRadius(RADIUS_DEFAULT_NUMBER); setLeftPoint(left_point); @@ -5294,67 +2248,141 @@ function SetPointsComponentReverse() { } }, [currentSelectedPool, switch_pool_loading]); - // 中文 左侧点位改变 useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { // effect bin - const diff = leftPoint - rightPoint; + let diff; + if (pair_is_reverse) { + diff = leftPoint - rightPoint; + } else { + diff = rightPoint - leftPoint; + } const bin_number_temp = diff / BIN_WIDTH; setBinNumber(bin_number_temp); // effect right area - pointChange({ leftPoint, rightPoint, currentPoint }); + if (pair_is_reverse) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } else { + pointChange({ leftPoint, rightPoint }); + } } + console.log( + '00000000--leftPoint, rightPoint--00000000', + leftPoint, + rightPoint + ); }, [leftPoint, rightPoint, BIN_WIDTH]); - // 数据有变动==》去掉token 太少提示 + + useEffect(() => { + if ( + liquidityShape == 'Spot' && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) + ) { + if (pair_is_reverse) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } else { + pointChange({ leftPoint, rightPoint }); + } + } + }, [liquidityShape]); + + // clean tip useEffect(() => { if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - pointAndshapeChange(); + set_token_amount_tip(null); } }, [liquidityShape, leftPoint, rightPoint]); - // 修改bin --> 合适的右点位 --->合适的bin + // to change bin --> to get proper right point --->for proper bin function changeBin(bin: number) { - let appropriate_right_point = leftPoint - BIN_WIDTH * bin; - if (appropriate_right_point < POINTLEFTRANGE) { - appropriate_right_point = POINTLEFTRANGE; + let appropriate_right_point, appropriate_bin_number; + if (pair_is_reverse) { + appropriate_right_point = leftPoint - BIN_WIDTH * bin; + if (appropriate_right_point < POINTLEFTRANGE) { + appropriate_right_point = POINTLEFTRANGE; + } + appropriate_bin_number = + (leftPoint - appropriate_right_point) / BIN_WIDTH; + } else { + appropriate_right_point = leftPoint + BIN_WIDTH * bin; + if (appropriate_right_point > POINTRIGHTRANGE) { + appropriate_right_point = POINTRIGHTRANGE; + } + appropriate_bin_number = + (appropriate_right_point - leftPoint) / BIN_WIDTH; } - const appropriate_bin_number = - (leftPoint - appropriate_right_point) / BIN_WIDTH; setRightPoint(appropriate_right_point); setBinNumber(appropriate_bin_number); } - // 修改radius-->合适的左右点位 --->合适的radius + // to change radius-->to get proper left point and right point --->for proper radius function changeRadius(radius: number) { - let appropriate_left_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * radius - ); - let appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - const appropriate_radius = - (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); + let appropriate_left_point, appropriate_right_point, appropriate_radius; + if (pair_is_reverse) { + appropriate_left_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * radius + ); + appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + appropriate_radius = + (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); + } else { + appropriate_right_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * radius + ); + appropriate_left_point = get_bin_point_by_point( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_radius = + (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + } + setLeftPoint(appropriate_left_point); setRightPoint(appropriate_right_point); setRadius(appropriate_radius); } - // 修改 targetPrice-->合适的左右点位--->合适的targetPrice + // to change targetPrice-->o get proper left point and right point--->for proper targetPrice function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_target_point = get_bin_point_by_price(reverse_price(price)); - const appropriate_left_point = get_bin_point_by_point( - appropriate_target_point + BIN_WIDTH * radius - ); - const appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; + let appropriate_left_point, + appropriate_right_point, + appropriate_target_point; + if (pair_is_reverse) { + appropriate_target_point = get_bin_point_by_price(reverse_price(price)); + appropriate_left_point = get_bin_point_by_point( + appropriate_target_point + BIN_WIDTH * radius + ); + appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; + } else { + appropriate_target_point = get_bin_point_by_price(price); + appropriate_right_point = get_bin_point_by_point( + appropriate_target_point + BIN_WIDTH * radius + ); + appropriate_left_point = get_bin_point_by_point( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + } setLeftPoint(appropriate_left_point); setRightPoint(appropriate_right_point); return appropriate_target_point; } function getLeftPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(leftPoint)) { - const price = reverse_price(get_price_by_point(leftPoint)); + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(leftPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(leftPoint)); + } else { + price = get_price_by_point(leftPoint); + } if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -5365,8 +2393,17 @@ function SetPointsComponentReverse() { } } function getRightPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(rightPoint)) { - const price = reverse_price(get_price_by_point(rightPoint)) + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(rightPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(rightPoint)); + } else { + price = get_price_by_point(rightPoint); + } if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -5377,8 +2414,18 @@ function SetPointsComponentReverse() { } } function getTargetPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id && !isInvalid(targetPoint)) { - let price = reverse_price(get_price_by_point(targetPoint)); + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(targetPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(targetPoint)); + } else { + price = get_price_by_point(targetPoint); + } + if (new BigNumber(price).isLessThan('0.00000001')) { return price; } else { @@ -5390,17 +2437,17 @@ function SetPointsComponentReverse() { } function get_point_by_price(price: string) { const { point_delta } = currentSelectedPool; - const decimalRate_point = Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const decimalRate_point = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); const point = getPointByPrice(point_delta, price, decimalRate_point); return point; } function get_price_by_point(point: number) { const decimalRate_price = - Math.pow(10, token_x_decimals) / - Math.pow(10, token_y_decimals); + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); return getPriceByPoint(point, decimalRate_price); } - function get_bin_point_by_price(price:string) { + function get_bin_point_by_price(price: string) { const { point_delta } = currentSelectedPool; const decimalRate = Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); @@ -5412,12 +2459,16 @@ function SetPointsComponentReverse() { ); return appropriate_point; } - function get_bin_point_by_point(point:number) { + function get_bin_point_by_point(point: number) { const { point_delta } = currentSelectedPool; return getBinPointByPoint(point_delta, SLOT_NUMBER, point); } function getPair() { - return `(${tokenY.symbol}/${tokenX.symbol})`; + if (pair_is_reverse) { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } else { + return `(${tokenX.symbol}/${tokenY.symbol})`; + } } return (
)}
@@ -5476,12 +2527,12 @@ function SetPointsComponentReverse() { !isInvalid(rightPoint) && !switch_pool_loading && (
- {/* */} + reverse={pair_is_reverse} + >
)}
@@ -5590,8 +2641,12 @@ function SetPointsComponentReverse() { > { - return get_bin_point_by_price(reverse_price(price)); + handlePriceToAppropriatePoint={(price: string) => { + if (pair_is_reverse) { + return get_bin_point_by_price(reverse_price(price)); + } else { + return get_bin_point_by_price(price); + } }} disbaled={priceRangeMode === 'by_radius'} customPrice={leftCustomPrice} @@ -5613,8 +2668,12 @@ function SetPointsComponentReverse() { > { - return get_bin_point_by_price(reverse_price(price)); + handlePriceToAppropriatePoint={(price: string) => { + if (pair_is_reverse) { + return get_bin_point_by_price(reverse_price(price)); + } else { + return get_bin_point_by_price(price); + } }} customPrice={rightCustomPrice} getPrice={getRightPrice} @@ -5656,250 +2715,245 @@ function SetPointsComponentReverse() {
); } - /** - * step1 slider设置数字区间[0, 20] - * step2 数字区间 和 point区间做一个 双向 映射 - * step step 为 一个bin,区间设定逻辑是 以当前价格为起点,向左向右各辐射30个point(可配置) - * @param param0 - * @returns - */ -function Slider({ - value, - set_slider_left_value, - set_slider_right_value, - set_left_point, - set_right_point, - get_point_by_slider_value, -}: { - value: any; - set_slider_left_value: Function; - set_slider_right_value: Function; - set_left_point: Function; - set_right_point: Function; - get_point_by_slider_value: Function; -}) { - return ( - { - const [num1, num2] = v; - set_slider_left_value(num1); - set_slider_right_value(num2); - const left_point = get_point_by_slider_value(num1); - const right_point = get_point_by_slider_value(num2); - set_left_point(left_point); - set_right_point(right_point); - }} - value={value} - min={0} - max={SLIDER_BIN_NUMBER} - step={1} - pearling={true} - /> - ); -} - -function CreatePoolComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - buttonSort, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - buttonSort: boolean; -}) { - const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); - const [createPoolRate, setCreatePoolRate] = useState(''); - const [rateStatus, setRateStatus] = useState(true); + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 传递的时候只传实际使用值 + * @returns + */ +function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + binNumber, + SLOT_NUMBER, + leftPoint, + rightPoint, + currentPoint, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + getLiquiditySpot, + getLiquidityForCurveAndBidAskMode, + } = useContext(LiquidityProviderData); + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; - useEffect(() => { - if (createPoolRate) { - const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); - setCreatePoolRate(toPrecision(rateString, 6)); - } - }, [buttonSort]); - function getCurrentPriceValue(token: TokenMetadata) { - if (token) { - const price = tokenPriceList[token.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; + const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const new_liquidity = getLiquiditySpot(); + add_liquidity(new_liquidity); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + nftList = getLiquidityForCurveAndBidAskMode(); + if (!nftList) { + setAddLiquidityButtonLoading(false); + return; } + /** + * 计算出 nftList token x tokeny 的数量,这是需要的总数量 + * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 + * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 + */ + let last_total_needed_token_x_amount = Big(0); + let last_total_needed_token_y_amount = Big(0); + nftList.forEach((nft: IAddLiquidityInfo) => { + const { amount_x, amount_y } = nft; + last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( + amount_x || 0 + ); + last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( + amount_y || 0 + ); + }); + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: last_total_needed_token_x_amount.toFixed(), + amount_y: last_total_needed_token_y_amount.toFixed(), + }); } - function createPool() { - setCreatePoolButtonLoading(true); - const { fee } = currentSelectedPool; - const pointDelta = POINTDELTAMAP[fee]; - let decimalRate = - Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); - let init_point = getPointByPrice( - pointDelta, - createPoolRate, - decimalRate, - true + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + function getButtonText() { + let txt: any = ( + ); - const arr = [tokenX.id, tokenY.id]; - arr.sort(); - if (arr[0] !== tokenX.id) { - decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - init_point = getPointByPrice( - pointDelta, - new BigNumber(1).dividedBy(createPoolRate).toFixed(), - decimalRate, - true + if (invalidRange) { + txt = ( + + ); + } else if ( + (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || + (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || + (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + !onlyAddXToken && + !onlyAddYToken && + (+tokenXAmount == 0 || +tokenYAmount == 0) + ) { + txt = ( + + ); + } else if ( + +tokenXAmount > 0 && + new BigNumber(tokenXAmount).isGreaterThan( + getMax(tokenX, tokenXBalanceFromNear) + ) + ) { + txt = ( + + ); + } else if ( + +tokenYAmount > 0 && + new BigNumber(tokenYAmount).isGreaterThan( + getMax(tokenY, tokenYBalanceFromNear) + ) + ) { + txt = ( + ); - create_pool({ - token_a: tokenY.id, - token_b: tokenX.id, - fee: currentSelectedPool.fee, - init_point, - }); - } else { - create_pool({ - token_a: tokenX.id, - token_b: tokenY.id, - fee: currentSelectedPool.fee, - init_point, - }); } + return txt; } - function switchRate() { - setRateStatus(!rateStatus); - } - function getPoolRate() { - if (createPoolRate) { - const rate = 1 / +createPoolRate; - return toPrecision(rate.toString(), 6); + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + if (tokenSort) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + } else if (onlyAddYToken) { + if (tokenSort) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); } - return ''; + return !(condition1 && condition2); } - const mobileDevice = isMobile(); + const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquiditySpot + : addLiquidityForCurveAndBidAskMode; + return (
-
- - : -
-
- -
-
-

- -

-
-
-

- -

-
- - 1 {toRealSymbol(tokenX?.symbol)} = - -
- { - setCreatePoolRate(target.value); - }} - /> - - {toRealSymbol(tokenY?.symbol)} - -
-
-
- - - -
- {rateStatus ? ( -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue(tokenX)}) - - - - {createPoolRate} {toRealSymbol(tokenY?.symbol)} - -
- ) : ( -
- 1 {toRealSymbol(tokenY?.symbol)} - - ({getCurrentPriceValue(tokenY)}) - - - - {getPoolRate()} {toRealSymbol(tokenX?.symbol)} - -
- )} - - -
-
-
-
-
{isSignedIn ? ( ( - <> - - - )} + loading={addLiquidityButtonLoading} + Text={() => <>{getButtonText()}} /> ) : ( - + )}
); } - function NoDataComponent(props: any) { const { isNoPool } = props; const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); @@ -6046,7 +3100,6 @@ function PointInputComponent({
); } - export function IntegerInputComponent({ value, setValue, @@ -6146,7 +3199,7 @@ function InputAmount({ changeAmount: any; amount: string; currentSelectedPool: PoolInfo; - hidden: Boolean; + hidden?: Boolean; }) { const [inputPrice, setInputPrice] = useState(''); const [showNearTip, setShowNearTip] = useState(false); @@ -6255,4 +3308,4 @@ function InputAmount({ ) : null}
); -} \ No newline at end of file +} diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx new file mode 100644 index 000000000..f99f4c764 --- /dev/null +++ b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx @@ -0,0 +1,6164 @@ +import React, { + useState, + useContext, + useEffect, + useRef, + createContext, +} from 'react'; + +import { + ReturnIcon, + AddIcon, + SelectIcon, + BgIcon, + SwitchButton, + AddButton, + ReduceButton, + SwitchIcon, + BoxDarkBg, + SideIcon, + InvalidIcon, + WarningIcon, + EmptyIcon, + WarningMark, + SwitchLRButton, + SwitchArrowL, + SwitchArrowR, +} from '~components/icon/V3'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { useHistory } from 'react-router-dom'; +import { + GradientButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from '~components/button/Button'; +import SelectToken from '~components/forms/SelectToken'; +import { useTriTokens, useWhitelistTokens } from '../../state/token'; +import { useTriTokenIdsOnRef } from '../../services/aurora/aurora'; +import { + TokenMetadata, + ftGetBalance, + ftGetTokenMetadata, +} from '../../services/ft-contract'; +import { getTokenPriceList } from '../../services/indexer'; +import { + getBoostTokenPrices, + Seed, + FarmBoost, + classificationOfCoins, +} from '../../services/farm'; +import { useTokenBalances, useDepositableBalance } from '../../state/token'; +import Loading from '~components/layout/Loading'; +import { + list_pools, + add_liquidity, + create_pool, + PoolInfo, + get_pool_marketdepth, + regularizedPoint, + batch_add_liquidity, +} from '../../services/swapV3'; +import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; +import { + getPriceByPoint, + getPointByPrice, + CONSTANT_D, + FEELIST, + POINTDELTAMAP, + DEFAULTSELECTEDFEE, + POINTLEFTRANGE, + POINTRIGHTRANGE, + useAddAndRemoveUrlHandle, + get_matched_seeds_for_dcl_pool, + get_all_seeds, + get_pool_id, + get_pool_name, + openUrl, + getBinPointByPrice, + getBinPointByPoint, + get_l_amount_by_condition, + UserLiquidityInfo, + reverse_price, + sort_tokens_by_base, +} from '../../services/commonV3'; +import { + formatWithCommas, + toPrecision, + toReadableNumber, + toNonDivisibleNumber, + checkAllocations, + scientificNotationToString, + getAllocationsLeastOne, + toInternationalCurrencySystem, + ONLY_ZEROS, +} from '~utils/numbers'; +import { WalletContext } from '../../utils/wallets-integration'; +import _, { forEach, set } from 'lodash'; +import BigNumber from 'bignumber.js'; +import { toRealSymbol } from '../../utils/token'; +import ReactTooltip from 'react-tooltip'; +import { getURLInfo } from '../../components/layout/transactionTipPopUp'; +import { BlueCircleLoading } from '../../components/layout/Loading'; +import { isMobile } from '../../utils/device'; +import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; +import ReactSlider from 'react-slider'; +import Big from 'big.js'; +import { SelectTokenDCL } from '../../components/forms/SelectToken'; +import { SliderCurColor } from '~components/icon/Info'; +import { + CurveShape, + SpotShape, + BidAskShape, +} from '../Orderly/components/Common/Icons'; +import DclChart from '../../components/d3Chart/DclChart'; +import { IChartData } from '../../components/d3Chart/interfaces'; +import { + IAddLiquidityInfo, + IAddLiquidityInfoHelp, + LiquidityShape, + PriceRangeModeType, +} from './interfaces'; +import { + get_custom_config_for_chart, + get_default_config_for_chart, + RADIUS_DEFAULT_NUMBER, + max_nft_divisional_per_side, +} from '../../components/d3Chart/config'; +import { + IChartItemConfig, + IChartConfig, +} from '../../components/d3Chart/interfaces'; +import { isInvalid } from '../../components/d3Chart/utils'; +const LiquidityProviderData = createContext(null); +const AddLiquidityProviderData = createContext(null); +const SLIDER_BIN_NUMBER = 20; +export default function AddYourLiquidityPageV3() { + // return + return ; +} + +function AddYourLiquidityPageV3Forward() { + const [tokenX, setTokenX] = useState(null); + const [tokenY, setTokenY] = useState(null); + const [tokenXAmount, setTokenXAmount] = useState(''); + const [tokenYAmount, setTokenYAmount] = useState(''); + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + const [currentPoint, setCurrentPoint] = useState(); + const [onlyAddYToken, setOnlyAddYToken] = useState(false); + const [onlyAddXToken, setOnlyAddXToken] = useState(false); + const [invalidRange, setInvalidRange] = useState(false); + const [currentSelectedPool, setCurrentSelectedPool] = + useState(null); + const [listPool, setListPool] = useState([]); + const [tokenPriceList, setTokenPriceList] = useState>({}); + const [currentPools, setCurrentPools] = + useState>(null); + const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); + const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); + + const [feeBoxStatus, setFeeBoxStatus] = useState(true); + const [buttonSort, setButtonSort] = useState(false); + const [selectHover, setSelectHover] = useState(false); + const [hoverFeeBox, setHoverFeeBox] = useState(false); + + // abandon + const [seed_list, set_seed_list] = useState(); + const [related_seeds, set_related_seeds] = useState([]); + + // new + const [binNumber, setBinNumber] = useState(); + const [liquidityShape, setLiquidityShape] = useState('Spot'); + const [topPairs, setTopPairs] = useState([]); + const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); + const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); + const [token_amount_tip, set_token_amount_tip] = + useState(); + const [only_suppport_spot_shape, set_only_suppport_spot_shape] = + useState(false); + const [switch_pool_loading, set_switch_pool_loading] = + useState(true); + const [new_user_liquidities, set_new_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + + // callBack handle + useAddAndRemoveUrlHandle(); + const history = useHistory(); + const triTokenIds = useTriTokenIdsOnRef(); + const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); + const triTokens = useTriTokens(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const nearBalance = useDepositableBalance('NEAR'); + const intl = useIntl(); + const OPEN_CREATE_POOL_ENTRY = false; + + useEffect(() => { + getBoostTokenPrices().then(setTokenPriceList); + get_list_pools(); + get_seeds(); + }, []); + useEffect(() => { + if (tokenX) { + const tokenXId = tokenX.id; + if (tokenXId) { + if (isSignedIn) { + ftGetBalance(tokenXId).then((available: string) => + setTokenXBalanceFromNear( + toReadableNumber( + tokenX.decimals, + tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + if (tokenY) { + const tokenYId = tokenY.id; + if (tokenYId) { + if (isSignedIn) { + ftGetBalance(tokenYId).then((available: string) => + setTokenYBalanceFromNear( + toReadableNumber( + tokenY.decimals, + tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + }, [tokenX, tokenY, isSignedIn, nearBalance]); + useEffect(() => { + if (listPool.length > 0) { + get_init_pool(); + } + }, [listPool]); + useEffect(() => { + if (currentSelectedPool && tokenX && tokenY) { + const { fee } = currentSelectedPool; + const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); + history.replace(`#${link}`); + if (seed_list && currentSelectedPool.pool_id) { + get_optional_seeds(); + } + } + }, [currentSelectedPool, tokenX, tokenY, seed_list]); + useEffect(() => { + if (tokenX && tokenY) { + searchPools(); + } + }, [tokenX, tokenY, tokenPriceList, listPool]); + useEffect(() => { + if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { + getTopPairs(); + } + }, [listPool, tokenPriceList]); + // new + useEffect(() => { + // init + if (currentSelectedPool?.pool_id) { + const { current_point, point_delta } = currentSelectedPool; + const n = get_slot_number_in_a_bin(); + const bin_width = n * point_delta; + SET_SLOT_NUMBER(n); + SET_BIN_WIDTH(bin_width); + setCurrentPoint(current_point); + set_switch_pool_loading(false); + } + }, [currentSelectedPool]); + // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 + useEffect(() => { + set_only_suppport_spot_shape(false); + if (currentSelectedPool) { + const { point_delta } = currentSelectedPool; + if (leftPoint <= currentPoint && rightPoint > currentPoint) { + // inrange + const binWidth = SLOT_NUMBER * point_delta; + const binNumber = (rightPoint - leftPoint) / binWidth; + if (binNumber == 1) { + setLiquidityShape('Spot'); + set_only_suppport_spot_shape(true); + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount); + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount); + } + } + } + } + }, [ + leftPoint, + rightPoint, + currentPoint, + currentSelectedPool, + liquidityShape, + ]); + useEffect(() => { + set_token_amount_tip(null); + }, [tokenXAmount, tokenYAmount, currentSelectedPool]); + async function getTopPairs() { + const listPromise = listPool.map(async (p: PoolInfo) => { + const token_x = p.token_x; + const token_y = p.token_y; + + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged) + .toFixed(); + const tvlx = + Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * + Number(pricex); + const tvly = + Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * + Number(pricey); + + p.tvl = tvlx + tvly; + + return p; + }); + const list: PoolInfo[] = await Promise.all(listPromise); + list.sort((b: PoolInfo, a: PoolInfo) => { + return a.tvl - b.tvl; + }); + const top3 = list.slice(0, 3); + setTopPairs(top3); + } + if (!refTokens || !triTokens || !triTokenIds) return ; + async function get_seeds() { + const seeds = await get_all_seeds(); + set_seed_list(seeds); + } + function get_optional_seeds() { + const optional_seeds = get_matched_seeds_for_dcl_pool({ + seeds: seed_list, + pool_id: currentSelectedPool.pool_id, + }); + if (optional_seeds.length) { + set_related_seeds(optional_seeds); + } else { + set_related_seeds([]); + } + } + async function get_init_pool() { + let tokenx_id, tokeny_id, pool_fee; + const hash = decodeURIComponent(location.hash); + if (hash.indexOf('<>') > -1) { + // new link + [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); + } else { + // old link + [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); + } + if (tokenx_id && tokeny_id && pool_fee) { + const tokenx = await ftGetTokenMetadata(tokenx_id); + const tokeny = await ftGetTokenMetadata(tokeny_id); + setTokenX(tokenx); + setTokenY(tokeny); + } + } + function goYourLiquidityPage() { + if (history.length == 2) { + history.push('/yourliquidity'); + } else { + history.goBack(); + } + } + async function get_list_pools() { + const list: PoolInfo[] = await list_pools(); + if (list.length > 0) { + setListPool(list); + } + } + function searchPools() { + const hash = decodeURIComponent(location.hash); + let url_fee; + if (hash.indexOf('<>') > -1) { + url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; + } else { + url_fee = +hash.slice(1).split('|')[2]; + } + const currentPoolsMap = {}; + if (listPool.length > 0 && tokenX && tokenY) { + set_switch_pool_loading(true); + const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { + // TODO 增加pool 状态的判断 + const { token_x, token_y, state } = pool; + if ( + (token_x == tokenX.id && token_y == tokenY.id) || + (token_x == tokenY.id && token_y == tokenX.id) + ) + return true; + }); + if (availablePools.length > 0) { + /*** percent start */ + const tvlList: number[] = []; + availablePools.map((p: PoolInfo) => { + const { + total_x, + total_y, + token_x, + token_y, + total_fee_x_charged, + total_fee_y_charged, + } = p; + const firstToken = tokenX.id == token_x ? tokenX : tokenY; + const secondToken = tokenY.id == token_y ? tokenY : tokenX; + const firstTokenPrice = + (tokenPriceList && + tokenPriceList[firstToken.id] && + tokenPriceList[firstToken.id].price) || + '0'; + const secondTokenPrice = + (tokenPriceList && + tokenPriceList[secondToken.id] && + tokenPriceList[secondToken.id].price) || + '0'; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged || 0) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged || 0) + .toFixed(); + const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) + .times(firstTokenPrice) + .toNumber(); + const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) + .times(secondTokenPrice) + .toNumber(); + const totalTvl = tvlx + tvly; + p.tvl = totalTvl; + tvlList.push(totalTvl); + return p; + }); + const sumOfTvlList = _.sum(tvlList); + const tvlPercents = + sumOfTvlList === 0 + ? ['0', '0', '0', '0'] + : availablePools.map((p: PoolInfo) => + scientificNotationToString( + ((p.tvl / sumOfTvlList) * 100).toString() + ) + ); + const nonZeroIndexes: number[] = []; + tvlPercents.forEach((p, index) => { + if (Number(p) > 0) { + nonZeroIndexes.push(index); + } + }); + const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); + const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); + const finalPercents = tvlPercents.map((p, index) => { + if (nonZeroIndexes.includes(index)) { + const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; + return newP; + } + return p; + }); + const maxPercent = _.max(finalPercents); + let maxPercentPool; + availablePools.forEach((pool: PoolInfo, index) => { + const f = pool.fee; + const temp: PoolInfo = { + ...pool, + percent: finalPercents[index], + tvl: tvlList[index], + }; + currentPoolsMap[f] = temp; + if (finalPercents[index] == maxPercent) { + maxPercentPool = temp; + } + }); + // url-fee-pool + const urlFeePool = url_fee + ? currentPoolsMap[url_fee] || { fee: url_fee } + : null; + setCurrentPools(currentPoolsMap); + setCurrentSelectedPool(urlFeePool || maxPercentPool); + } else { + setCurrentPools({}); + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } else { + setCurrentPools({}); + if (tokenX && tokenY) { + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } + } + function switchSelectedFee(fee: number) { + if (tokenX && tokenY && currentPools) { + const pool = currentPools[fee]; + setCurrentSelectedPool(pool || { fee }); + if (!pool) { + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setInvalidRange(false); + } + } + } + function changeTokenXAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenXAmount(amount); + if (!onlyAddXToken && liquidityShape === 'Spot') { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenYAmount(amount_result); + } + } + function changeTokenYAmount(amount: string = '0') { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setTokenYAmount(amount); + if (!onlyAddYToken && liquidityShape === 'Spot') { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + function getTokenYAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + // X-->L + const L = + +amount * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); + // L-->current Y + const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); + // L--> Y + const Y = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1)); + const decimalsRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + + const Y_result = (Y + Yc) * decimalsRate; + return Y_result.toString(); + } + } + function getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + let L; + // Yc-->L + if (leftPoint == currentPoint) { + L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); + } else { + // Y-->L + L = + +amount * + ((Math.sqrt(CONSTANT_D) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); + } + const X = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); + const decimalsRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const X_result = X * decimalsRate; + return X_result.toString(); + } + } + function pointChange({ + leftPoint, + rightPoint, + currentPoint, + }: { + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const sort = tokenX.id == token_x; + setInvalidRange(false); + setOnlyAddXToken(false); + setOnlyAddYToken(false); + // invalid point + if (leftPoint >= rightPoint) { + setInvalidRange(true); + setTokenXAmount(''); + setTokenYAmount(''); + return; + } + // can only add x token + if (leftPoint > currentPoint) { + setOnlyAddXToken(true); + if (sort) { + setTokenYAmount(''); + } else { + setTokenXAmount(''); + } + return; + } + // can only add y token + if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { + setOnlyAddYToken(true); + if (sort) { + setTokenXAmount(''); + } else { + setTokenYAmount(''); + } + return; + } + + if (liquidityShape !== 'Spot') return; + if (sort) { + if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } else { + if (tokenXAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } else if (tokenYAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + } + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; + } + } + // start + function get_slot_number_in_a_bin() { + const pool_id = currentSelectedPool?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + /** + * step1 当数据发生改变 + * leftPoint, rightPoint 有效 + * tokenXAmount, tokenYAmount 至少有一个有值 + * ===> 可以触发 + * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 + * step3 把新增的liquidity传递给Chart组件, + * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 + * step5 疑问;?实时修改图表 会导致效率什么问题吗? + */ + function generate_new_user_chart() { + if ( + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + (+tokenXAmount > 0 || +tokenYAmount > 0) + ) { + let new_nfts: any; + if (liquidityShape == 'Spot') { + const new_nft = getLiquiditySpot(); + new_nfts = [new_nft]; + } else { + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; + } + const processed_new_nfts = process_liquidities(new_nfts); + set_new_user_liquidities(processed_new_nfts); + } else { + set_new_user_liquidities([]); + } + } + function getLiquiditySpot() { + const { pool_id } = currentSelectedPool; + return { + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + }; + } + function formatWithCommas_for_tip(v: string, commas?: boolean) { + const v_big = Big(v || 0); + let v_temp; + if (v_big.lt(0.001)) { + v_temp = v_big.toFixed(6, 3); + } else { + if (commas) { + v_temp = formatWithCommas(v_big.toFixed(3, 3)); + } else { + v_temp = v_big.toFixed(3, 3); + } + } + return v_temp; + } + function getLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'ceil' + ); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(null); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 1 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 2 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 3 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 4 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } + } + return nftList; + } + /** + * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == exclude_cur_total_nft_number) { + amount_x = dis + .mul(exclude_cur_total_nft_number + 1) + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 1).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == exclude_cur_total_nft_number) { + amount_y = dis + .mul(exclude_cur_total_nft_number + 1) + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 2).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == 0) { + amount_y = dis + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + // 不同点1 + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + // 不同点2 + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 2).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == 0) { + amount_x = dis + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 和 bid ask 上升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_rise_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + const left_i_point = left_point + nftWidth * i; + let right_i_point; + if (i == total_nft_number - 1) { + right_i_point = right_point; + } else { + right_i_point = left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + /** + * curve 和 bid ask 下降升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_decline_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐下降模式 + * nft 从右往左计算 + * e.g. + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + let left_i_point; + let right_i_point; + if (i == total_nft_number - 1) { + left_i_point = left_point; + } else { + left_i_point = right_point - nftWidth * (i + 1); + } + right_i_point = right_point - nftWidth * i; + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + /** + * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 + */ + function process_liquidities(liquidities: IAddLiquidityInfo[]) { + const { pool_id } = currentSelectedPool; + const new_liquidities: UserLiquidityInfo[] = []; + liquidities.forEach((l: IAddLiquidityInfo) => { + const { left_point, right_point, amount_x, amount_y } = l; + const L = get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount: amount_x, + token_y_amount: amount_y, + poolDetail: currentSelectedPool, + }); + new_liquidities.push({ + pool_id, + left_point, + right_point, + amount: L, + }); + }); + return new_liquidities; + } + const tokenSort = tokenX?.id == currentSelectedPool?.token_x; + const mobileDevice = isMobile(); + + return ( + +
+ {/* 缩略图 */} + {/* */} + {/* 详情页图 */} + {/* */} + {/* 添加页图 */} + {/* */} + {/* 用户流动性图表*/} + {/* */} + {/* 删除流动性图表 从右侧部分删除 */} + {/* */} + {/* 删除流动性图表 从左侧部分删除 */} + {/* */} + {/* 删除流动性图表 全部删除 */} + {/* */} +
+ + {/* mobile head */} +
+
+ +
+ + + +
+
+ {/* pc head */} +
{ + history.goBack(); + }} + > +
+ +
+ + + +
+ + {/* content */} +
+
+ {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + {/* right area */} +
+
+
+ +
+ + { + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ + + + {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null} + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + setHoverFeeBox(false); + }} + onMouseEnter={() => { + setHoverFeeBox(true); + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ +
+ +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1 && !only_suppport_spot_shape) + setLiquidityShape('Curve'); + else if (index == 2 && !only_suppport_spot_shape) + setLiquidityShape('BidAsk'); + }} + > + + + + {index === 0 && ( + + )} + {index === 1 && ( + + )} + + {index === 2 && ( + + )} + +
+ ); + } + )} +
+ {/* new user chart part */} + {isSignedIn && currentSelectedPool ? ( +
+
+
+ +
+
+ Generate +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ ) : null} + + {currentSelectedPool && currentSelectedPool.pool_id && ( + + )} +
+ + +
+
+
+
+
+
+ ); +} +function AddYourLiquidityPageV3Reverse() { + const [tokenX, setTokenX] = useState(null); + const [tokenY, setTokenY] = useState(null); + const [tokenXAmount, setTokenXAmount] = useState(''); + const [tokenYAmount, setTokenYAmount] = useState(''); + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + const [currentPoint, setCurrentPoint] = useState(); + const [onlyAddYToken, setOnlyAddYToken] = useState(false); + const [onlyAddXToken, setOnlyAddXToken] = useState(false); + const [invalidRange, setInvalidRange] = useState(false); + const [currentSelectedPool, setCurrentSelectedPool] = + useState(null); + + const [listPool, setListPool] = useState([]); + const [tokenPriceList, setTokenPriceList] = useState>({}); + const [currentPools, setCurrentPools] = + useState>(null); + const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); + const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); + + const [feeBoxStatus, setFeeBoxStatus] = useState(true); + const [buttonSort, setButtonSort] = useState(false); + const [selectHover, setSelectHover] = useState(false); + const [hoverFeeBox, setHoverFeeBox] = useState(false); + + // abandon + const [seed_list, set_seed_list] = useState(); + const [related_seeds, set_related_seeds] = useState([]); + + // new + const [binNumber, setBinNumber] = useState(); + const [liquidityShape, setLiquidityShape] = useState('Spot'); + const [topPairs, setTopPairs] = useState([]); + const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); + const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); + const [token_amount_tip, set_token_amount_tip] = + useState(); + const [only_suppport_spot_shape, set_only_suppport_spot_shape] = + useState(false); + const [switch_pool_loading, set_switch_pool_loading] = + useState(true); + const [new_user_liquidities, set_new_user_liquidities] = useState< + UserLiquidityInfo[] + >([]); + + // callBack handle + useAddAndRemoveUrlHandle(); + const history = useHistory(); + const triTokenIds = useTriTokenIdsOnRef(); + const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); + const triTokens = useTriTokens(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const nearBalance = useDepositableBalance('NEAR'); + const intl = useIntl(); + const OPEN_CREATE_POOL_ENTRY = false; + + useEffect(() => { + getBoostTokenPrices().then(setTokenPriceList); + get_list_pools(); + get_seeds(); + }, []); + useEffect(() => { + if (tokenX) { + const tokenXId = tokenX.id; + if (tokenXId) { + if (isSignedIn) { + ftGetBalance(tokenXId).then((available: string) => + setTokenXBalanceFromNear( + toReadableNumber( + tokenX.decimals, + tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + if (tokenY) { + const tokenYId = tokenY.id; + if (tokenYId) { + if (isSignedIn) { + ftGetBalance(tokenYId).then((available: string) => + setTokenYBalanceFromNear( + toReadableNumber( + tokenY.decimals, + tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available + ) + ) + ); + } + } + } + }, [tokenX, tokenY, isSignedIn, nearBalance]); + useEffect(() => { + if (listPool.length > 0) { + get_init_pool(); + } + }, [listPool]); + useEffect(() => { + if (currentSelectedPool && tokenX && tokenY) { + const { fee } = currentSelectedPool; + const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); + history.replace(`#${link}`); + if (seed_list && currentSelectedPool.pool_id) { + get_optional_seeds(); + } + } + }, [currentSelectedPool, tokenX, tokenY, seed_list]); + useEffect(() => { + if (tokenX && tokenY) { + searchPools(); + } + }, [tokenX, tokenY, tokenPriceList, listPool]); + useEffect(() => { + if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { + getTopPairs(); + } + }, [listPool, tokenPriceList]); + // new + useEffect(() => { + // init + if (currentSelectedPool?.pool_id) { + console.log('come in currentSelectedPool', currentSelectedPool); + const { current_point, point_delta } = currentSelectedPool; + const n = get_slot_number_in_a_bin(); + const bin_width = n * point_delta; + SET_SLOT_NUMBER(n); + SET_BIN_WIDTH(bin_width); + setCurrentPoint(current_point); + set_switch_pool_loading(false); + } + }, [currentSelectedPool]); + // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 + useEffect(() => { + set_only_suppport_spot_shape(false); + if (currentSelectedPool) { + const { point_delta } = currentSelectedPool; + if (leftPoint <= currentPoint && rightPoint > currentPoint) { + // inrange + const binWidth = SLOT_NUMBER * point_delta; + const binNumber = (rightPoint - leftPoint) / binWidth; + if (binNumber == 1) { + setLiquidityShape('Spot'); + set_only_suppport_spot_shape(true); + if (tokenXAmount) { + changeTokenXAmount(tokenXAmount); + } else if (tokenYAmount) { + changeTokenYAmount(tokenYAmount); + } + } + } + } + }, [ + leftPoint, + rightPoint, + currentPoint, + currentSelectedPool, + liquidityShape, + ]); + useEffect(() => { + set_token_amount_tip(null); + }, [tokenXAmount, tokenYAmount, currentSelectedPool]); + async function getTopPairs() { + const listPromise = listPool.map(async (p: PoolInfo) => { + const token_x = p.token_x; + const token_y = p.token_y; + + p.token_x_metadata = await ftGetTokenMetadata(token_x); + p.token_y_metadata = await ftGetTokenMetadata(token_y); + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged) + .toFixed(); + const tvlx = + Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * + Number(pricex); + const tvly = + Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * + Number(pricey); + + p.tvl = tvlx + tvly; + + return p; + }); + const list: PoolInfo[] = await Promise.all(listPromise); + list.sort((b: PoolInfo, a: PoolInfo) => { + return a.tvl - b.tvl; + }); + const top3 = list.slice(0, 3); + setTopPairs(top3); + } + if (!refTokens || !triTokens || !triTokenIds) return ; + async function get_seeds() { + const seeds = await get_all_seeds(); + set_seed_list(seeds); + } + function get_optional_seeds() { + const optional_seeds = get_matched_seeds_for_dcl_pool({ + seeds: seed_list, + pool_id: currentSelectedPool.pool_id, + }); + if (optional_seeds.length) { + set_related_seeds(optional_seeds); + } else { + set_related_seeds([]); + } + } + async function get_init_pool() { + let tokenx_id, tokeny_id, pool_fee; + const hash = decodeURIComponent(location.hash); + if (hash.indexOf('<>') > -1) { + // new link + [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); + } else { + // old link + [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); + } + if (tokenx_id && tokeny_id && pool_fee) { + const tokenx = await ftGetTokenMetadata(tokenx_id); + const tokeny = await ftGetTokenMetadata(tokeny_id); + setTokenX(tokenx); + setTokenY(tokeny); + } + } + function goYourLiquidityPage() { + if (history.length == 2) { + history.push('/yourliquidity'); + } else { + history.goBack(); + } + } + async function get_list_pools() { + const list: PoolInfo[] = await list_pools(); + if (list.length > 0) { + setListPool(list); + } + } + function searchPools() { + const hash = decodeURIComponent(location.hash); + let url_fee; + if (hash.indexOf('<>') > -1) { + url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; + } else { + url_fee = +hash.slice(1).split('|')[2]; + } + const currentPoolsMap = {}; + if (listPool.length > 0 && tokenX && tokenY) { + set_switch_pool_loading(true); + const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { + // TODO 增加pool 状态的判断 + const { token_x, token_y, state } = pool; + if ( + (token_x == tokenX.id && token_y == tokenY.id) || + (token_x == tokenY.id && token_y == tokenX.id) + ) + return true; + }); + if (availablePools.length > 0) { + /*** percent start */ + const tvlList: number[] = []; + availablePools.map((p: PoolInfo) => { + const { + total_x, + total_y, + token_x, + token_y, + total_fee_x_charged, + total_fee_y_charged, + } = p; + const firstToken = tokenX.id == token_x ? tokenX : tokenY; + const secondToken = tokenY.id == token_y ? tokenY : tokenX; + const firstTokenPrice = + (tokenPriceList && + tokenPriceList[firstToken.id] && + tokenPriceList[firstToken.id].price) || + '0'; + const secondTokenPrice = + (tokenPriceList && + tokenPriceList[secondToken.id] && + tokenPriceList[secondToken.id].price) || + '0'; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged || 0) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged || 0) + .toFixed(); + const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) + .times(firstTokenPrice) + .toNumber(); + const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) + .times(secondTokenPrice) + .toNumber(); + const totalTvl = tvlx + tvly; + p.tvl = totalTvl; + tvlList.push(totalTvl); + return p; + }); + const sumOfTvlList = _.sum(tvlList); + const tvlPercents = + sumOfTvlList === 0 + ? ['0', '0', '0', '0'] + : availablePools.map((p: PoolInfo) => + scientificNotationToString( + ((p.tvl / sumOfTvlList) * 100).toString() + ) + ); + const nonZeroIndexes: number[] = []; + tvlPercents.forEach((p, index) => { + if (Number(p) > 0) { + nonZeroIndexes.push(index); + } + }); + const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); + const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); + const finalPercents = tvlPercents.map((p, index) => { + if (nonZeroIndexes.includes(index)) { + const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; + return newP; + } + return p; + }); + const maxPercent = _.max(finalPercents); + let maxPercentPool; + availablePools.forEach((pool: PoolInfo, index) => { + const f = pool.fee; + const temp: PoolInfo = { + ...pool, + percent: finalPercents[index], + tvl: tvlList[index], + }; + currentPoolsMap[f] = temp; + if (finalPercents[index] == maxPercent) { + maxPercentPool = temp; + } + }); + // url-fee-pool + const urlFeePool = url_fee + ? currentPoolsMap[url_fee] || { fee: url_fee } + : null; + setCurrentPools(currentPoolsMap); + setCurrentSelectedPool(urlFeePool || maxPercentPool); + } else { + setCurrentPools({}); + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } else { + setCurrentPools({}); + if (tokenX && tokenY) { + setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); + } + } + } + function switchSelectedFee(fee: number) { + if (tokenX && tokenY && currentPools) { + const pool = currentPools[fee]; + setCurrentSelectedPool(pool || { fee }); + if (!pool) { + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setInvalidRange(false); + } + } + } + function changeTokenXAmount(amount: string = '0') { + setTokenXAmount(amount); + if (!onlyAddXToken && liquidityShape === 'Spot') { + const amount_result = getTokenYAmountByCondition({ + amount, + leftPoint: leftPoint, + rightPoint: rightPoint, + currentPoint: currentPoint, + }); + setTokenYAmount(amount_result); + } + } + function changeTokenYAmount(amount: string = '0') { + setTokenYAmount(amount); + if (!onlyAddYToken && liquidityShape === 'Spot') { + const amount_result = getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } + } + function getTokenYAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + // X-->L + const L = + +amount * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); + // L-->current Y + const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); + // L--> Y + const Y = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1)); + const decimalsRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + + const Y_result = (Y + Yc) * decimalsRate; + return Y_result.toString(); + } + } + function getTokenXAmountByCondition({ + amount, + leftPoint, + rightPoint, + currentPoint, + }: { + amount: string; + leftPoint: number; + rightPoint: number; + currentPoint: number; + }) { + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + if (+amount == 0) { + return ''; + } else { + let L; + // Yc-->L + if (leftPoint == currentPoint) { + L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); + } else { + // Y-->L + L = + +amount * + ((Math.sqrt(CONSTANT_D) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); + } + const X = + L * + ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); + const decimalsRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const X_result = X * decimalsRate; + return X_result.toString(); + } + } + function pointChange({ + leftPoint, + rightPoint, + }: { + leftPoint: number; + rightPoint: number; + }) { + const sort = true; + setInvalidRange(false); + setOnlyAddXToken(false); + setOnlyAddYToken(false); + setLeftPoint(leftPoint); + setRightPoint(rightPoint); + // invalid point + if (leftPoint >= rightPoint) { + setInvalidRange(true); + setTokenXAmount(''); + setTokenYAmount(''); + return; + } + // can only add x token + if (leftPoint > currentPoint) { + setOnlyAddXToken(true); + setTokenYAmount(''); + return; + } + // can only add y token + if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { + setOnlyAddYToken(true); + setTokenXAmount(''); + return; + } + if (liquidityShape !== 'Spot') return; + if (tokenYAmount) { + const amount_result = getTokenXAmountByCondition({ + amount: tokenYAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenXAmount(amount_result); + } else if (tokenXAmount) { + const amount_result = getTokenYAmountByCondition({ + amount: tokenXAmount, + leftPoint, + rightPoint, + currentPoint, + }); + setTokenYAmount(amount_result); + } + } + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; + } + } + // start + function get_slot_number_in_a_bin() { + const pool_id = currentSelectedPool?.pool_id; + const { bin } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const slots = custom_config[pool_id]?.bin || bin; + return slots; + } + /** + * step1 当数据发生改变 + * leftPoint, rightPoint 有效 + * tokenXAmount, tokenYAmount 至少有一个有值 + * ===> 可以触发 + * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 + * step3 把新增的liquidity传递给Chart组件, + * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 + * step5 疑问;?实时修改图表 会导致效率什么问题吗? + */ + function generate_new_user_chart() { + if ( + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + (+tokenXAmount > 0 || +tokenYAmount > 0) + ) { + let new_nfts: any; + if (liquidityShape == 'Spot') { + const new_nft = getLiquiditySpot(); + new_nfts = [new_nft]; + } else { + new_nfts = getLiquidityForCurveAndBidAskMode(); + if (!new_nfts) return; + } + const processed_new_nfts = process_liquidities(new_nfts); + set_new_user_liquidities(processed_new_nfts); + } else { + set_new_user_liquidities([]); + } + } + function getLiquiditySpot() { + const { pool_id } = currentSelectedPool; + return { + pool_id, + left_point: leftPoint, + right_point: rightPoint, + amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), + amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), + token_x: tokenX, + token_y: tokenY, + }; + } + function formatWithCommas_for_tip(v: string, commas?: boolean) { + const v_big = Big(v || 0); + let v_temp; + if (v_big.lt(0.001)) { + v_temp = v_big.toFixed(6, 3); + } else { + if (commas) { + v_temp = formatWithCommas(v_big.toFixed(3, 3)); + } else { + v_temp = v_big.toFixed(3, 3); + } + } + return v_temp; + } + function getLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + let nftList: IAddLiquidityInfo[] = []; + const get_x_nfts = + liquidityShape == 'Curve' + ? get_decline_pattern_nfts + : get_rise_pattern_nfts; + const get_y_nfts = + liquidityShape == 'Curve' + ? get_rise_pattern_nfts + : get_decline_pattern_nfts; + if (onlyAddYToken) { + nftList = get_y_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenY, + token_amount: tokenYAmount, + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + if (onlyAddXToken) { + nftList = get_x_nfts({ + left_point: leftPoint, + right_point: rightPoint, + token: tokenX, + token_amount: tokenXAmount, + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + if (!onlyAddXToken && !onlyAddYToken) { + /** + * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 + * step2 分配好后,获得对右侧的最小token数量要求 + * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 + */ + const { point_delta, current_point } = currentSelectedPool; + const current_l_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'floor' + ); + const current_r_point = getBinPointByPoint( + point_delta, + SLOT_NUMBER, + current_point, + 'ceil' + ); + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const bin_number_left = (current_point - leftPoint) / binWidth; + set_token_amount_tip(null); + if (liquidityShape == 'Curve') { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_curve({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 1 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_decline_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_curve({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 2 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_rise_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } else { + if (bin_number_left > 1) { + // 左侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_x_amount_needed } = + get_y_nfts_contain_current_bid_ask({ + left_point: leftPoint, + right_point: current_r_point, + }); + nftList_y = addLiquidityInfoList; + const remain_token_x_amount = Big(tokenXAmount).minus( + min_token_x_amount_needed + ); + if (remain_token_x_amount.lt(0)) { + // 给出提示 token x 数量太少不能添加 3 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_x = get_rise_pattern_nfts({ + left_point: current_r_point, + right_point: rightPoint, + token: tokenX, + token_amount: remain_token_x_amount.toFixed(), + formula_fun: formula_of_token_x, + is_token_x: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } else { + // 右侧做等差 + let nftList_x: IAddLiquidityInfo[] = []; + let nftList_y: IAddLiquidityInfo[] = []; + const { addLiquidityInfoList, min_token_y_amount_needed } = + get_x_nfts_contain_current_bid_ask({ + left_point: current_l_point, + right_point: rightPoint, + }); + nftList_x = addLiquidityInfoList; + + const remain_token_y_amount = Big(tokenYAmount).minus( + min_token_y_amount_needed + ); + if (remain_token_y_amount.lt(0)) { + // 给出提示 token y 数量太少不能添加 4 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + You need at least + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } else { + nftList_y = get_decline_pattern_nfts({ + left_point: leftPoint, + right_point: current_l_point, + token: tokenY, + token_amount: remain_token_y_amount.toFixed(), + formula_fun: formula_of_token_y, + is_token_y: true, + }); + } + nftList = nftList_x.concat(nftList_y); + } + } + } + return nftList; + } + /** + * curve 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == exclude_cur_total_nft_number) { + amount_x = dis + .mul(exclude_cur_total_nft_number + 1) + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_curve({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 1).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(exclude_cur_total_nft_number + 1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[exclude_cur_total_nft_number] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == exclude_cur_total_nft_number) { + amount_y = dis + .mul(exclude_cur_total_nft_number + 1) + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_x_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + + // 不同点1 + const contain_cur_nft_left_point = left_point; + const contain_cur_nft_right_point = left_point + binWidth; + + // 不同点2 + const exclude_cur_left_point = contain_cur_nft_right_point; + const exclude_cur_right_point = right_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + const left_i_point = exclude_cur_left_point + nftWidth * i; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + right_i_point = exclude_cur_right_point; + } else { + right_i_point = exclude_cur_left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 2).mul( + formula_of_token_x(left_i_point, right_i_point) + ); + + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_y_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_x = Big(dis).mul(const_value).toFixed(0); + let amount_y; + if (i == 0) { + amount_y = dis + .mul(formula_of_token_y(left_point, current_point + 1)) + .toFixed(0); + min_token_y_amount_needed_nonDivisible = amount_y; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x, + amount_y: amount_y || '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_y_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_y_amount_needed: toReadableNumber( + tokenY.decimals, + min_token_y_amount_needed_nonDivisible + ), + }; + } + /** + * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 + * 此区间bin的数量要求 > 1 + * 双边 + * @param param0 + * @returns + */ + function get_y_nfts_contain_current_bid_ask({ + left_point, + right_point, + }: { + left_point: number; + right_point: number; + }) { + /** + * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 + * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 + * 给的条件是左点位,大于当前点位的右点位 + * step 1 把包含当前点位bin 单独划分出来作为一个nft + * step 2 把剩余的bin 划分若干个nft + * step 3 总的nft 它们 token amount 之和固定,求出等差 + * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 + */ + + /** + * 从左往右逐渐下降模式 + * 从右往左计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + + const { pool_id, point_delta, current_point } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + // 不同点1 + const contain_cur_nft_left_point = right_point - binWidth; + const contain_cur_nft_right_point = right_point; + + // 不同点2 + const exclude_cur_left_point = left_point; + const exclude_cur_right_point = contain_cur_nft_left_point; + + const exclude_cur_total_bin_number = + (exclude_cur_right_point - exclude_cur_left_point) / binWidth; + let exclude_cur_total_nft_number; + let exclude_cur_bin_number_in_a_nft; + if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { + exclude_cur_bin_number_in_a_nft = 1; + exclude_cur_total_nft_number = exclude_cur_total_bin_number; + } else { + exclude_cur_bin_number_in_a_nft = Math.floor( + exclude_cur_total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!( + exclude_cur_total_bin_number % max_nft_divisional_per_side + ); + exclude_cur_total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = + point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < exclude_cur_total_nft_number; i++) { + // 不同点3 + let left_i_point; + let right_i_point; + if (i == exclude_cur_total_nft_number - 1) { + left_i_point = exclude_cur_left_point; + } else { + left_i_point = exclude_cur_right_point - nftWidth * (i + 1); + } + right_i_point = exclude_cur_right_point - nftWidth * i; + const const_i = Big(i + 2).mul( + formula_of_token_y(left_i_point, right_i_point) + ); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i + 1] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + + // 不同点4 + const const_last = Big(1).mul( + formula_of_token_y(contain_cur_nft_left_point, current_point + 1) + ); + total_const = total_const.plus(const_last); + + addLiquidityInfoHelp[0] = { + left_point: contain_cur_nft_left_point, + right_point: contain_cur_nft_right_point, + const_value: const_last.toFixed(), + }; + + // 不同点5 + let min_token_x_amount_needed_nonDivisible; + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') + ).div(total_const); + for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_y = Big(dis).mul(const_value).toFixed(0); + let amount_x; + if (i == 0) { + amount_x = dis + .mul( + formula_of_token_x(current_point + 1, contain_cur_nft_right_point) + ) + .toFixed(0); + min_token_x_amount_needed_nonDivisible = amount_x; + } + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: amount_x || '0', + amount_y: amount_y, + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + + return { + min_token_x_amount_needed_nonDivisible, + addLiquidityInfoList, + min_token_x_amount_needed: toReadableNumber( + tokenX.decimals, + min_token_x_amount_needed_nonDivisible + ), + }; + } + /** + * curve 和 bid ask 上升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_rise_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐上升模式 + * 从左往右计算 + * e.g. + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; + * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + const left_i_point = left_point + nftWidth * i; + let right_i_point; + if (i == total_nft_number - 1) { + right_i_point = right_point; + } else { + right_i_point = left_point + nftWidth * (i + 1); + } + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + /** + * curve 和 bid ask 下降升模式下 + * 单边 + * @param param0 + * @returns + */ + function get_decline_pattern_nfts({ + left_point, + right_point, + token, + token_amount, + formula_fun, + is_token_x, + is_token_y, + }: { + left_point: number; + right_point: number; + token: TokenMetadata; + token_amount: string; + formula_fun: Function; + is_token_x?: boolean; + is_token_y?: boolean; + }) { + /** + * 从左往右逐渐下降模式 + * nft 从右往左计算 + * e.g. + * 公式推导: + * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount + * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; + * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) + * ===>求出dis后,就可以知道每个nft的amount + * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) + * */ + const { pool_id, point_delta } = currentSelectedPool; + const slot_number_in_a_bin = SLOT_NUMBER; + const binWidth = slot_number_in_a_bin * point_delta; + const total_bin_number = (right_point - left_point) / binWidth; + let total_nft_number; + let bin_number_in_a_nft; + if (total_bin_number < max_nft_divisional_per_side) { + const unbroken_nft_number = Math.floor(total_bin_number); + const has_remaining = !!(total_bin_number % 1); + bin_number_in_a_nft = 1; + total_nft_number = has_remaining + ? unbroken_nft_number + 1 + : unbroken_nft_number; + } else { + bin_number_in_a_nft = Math.floor( + total_bin_number / max_nft_divisional_per_side + ); + const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); + total_nft_number = has_remaining + ? max_nft_divisional_per_side + 1 + : max_nft_divisional_per_side; + } + const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; + let total_const = Big(0); + const addLiquidityInfoList: IAddLiquidityInfo[] = []; + const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; + for (let i = 0; i < total_nft_number; i++) { + let left_i_point; + let right_i_point; + if (i == total_nft_number - 1) { + left_i_point = left_point; + } else { + left_i_point = right_point - nftWidth * (i + 1); + } + right_i_point = right_point - nftWidth * i; + const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); + total_const = total_const.plus(const_i); + addLiquidityInfoHelp[i] = { + left_point: left_i_point, + right_point: right_i_point, + const_value: const_i.toFixed(), + }; + } + if (total_const.gt(0)) { + const dis = Big( + toNonDivisibleNumber(token.decimals, token_amount || '0') + ).div(total_const); + for (let i = 0; i < total_nft_number; i++) { + const { left_point, right_point, const_value } = + addLiquidityInfoHelp[i]; + const amount_i = Big(dis).mul(const_value).toFixed(0); + addLiquidityInfoList.push({ + pool_id, + left_point, + right_point, + amount_x: is_token_x ? amount_i : '0', + amount_y: is_token_y ? amount_i : '0', + min_amount_x: '0', + min_amount_y: '0', + }); + } + } + return addLiquidityInfoList; + } + function formula_of_token_x(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ); + } + function formula_of_token_y(leftPoint: number, rightPoint: number) { + return ( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + } + /** + * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 + */ + function process_liquidities(liquidities: IAddLiquidityInfo[]) { + const { pool_id } = currentSelectedPool; + const new_liquidities: UserLiquidityInfo[] = []; + liquidities.forEach((l: IAddLiquidityInfo) => { + const { left_point, right_point, amount_x, amount_y } = l; + const L = get_l_amount_by_condition({ + left_point, + right_point, + token_x_amount: amount_x, + token_y_amount: amount_y, + poolDetail: currentSelectedPool, + }); + new_liquidities.push({ + pool_id, + left_point, + right_point, + amount: L, + }); + }); + return new_liquidities; + } + const mobileDevice = isMobile(); + return ( + + {/* mobile head */} +
+
+ +
+ + + +
+
+ {/* pc head */} +
{ + history.goBack(); + }} + > +
+ +
+ + + +
+ + {/* content */} +
+
+ {/* no Data */} + {currentSelectedPool ? null : } + {/* add pool part */} + {currentSelectedPool && + !currentSelectedPool.pool_id && + OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY ? ( + + ) : null} + {/* add Liquidity part */} + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} + {/* right area todo */} +
+
+
+ +
+ + { + setTokenX(token); + setTokenXBalanceFromNear(token?.near?.toString()); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + setTokenYBalanceFromNear(token?.near?.toString()); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ + + {token_amount_tip ? ( +
+ + {token_amount_tip} +
+ ) : null} + +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + setHoverFeeBox(false); + }} + onMouseEnter={() => { + setHoverFeeBox(true); + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = + currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > + 0 ? ( + + {displayTvl(currentPools[fee].tvl)} + + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ +
+ +
+ +
+ {[SpotShape, CurveShape, BidAskShape].map( + (Shape, index: number) => { + let disabled = false; + if ( + (index == 1 || index == 2) && + only_suppport_spot_shape + ) { + disabled = true; + } + return ( +
{ + e.preventDefault(); + e.stopPropagation(); + if (index === 0) setLiquidityShape('Spot'); + else if (index === 1 && !only_suppport_spot_shape) + setLiquidityShape('Curve'); + else if (index == 2 && !only_suppport_spot_shape) + setLiquidityShape('BidAsk'); + }} + > + + + + {index === 0 && ( + + )} + {index === 1 && ( + + )} + + {index === 2 && ( + + )} + +
+ ); + } + )} +
+ {/* new user chart part */} + {isSignedIn && currentSelectedPool ? ( +
+
+
+ +
+
+ Generate +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ ) : null} + + {currentSelectedPool && currentSelectedPool.pool_id && ( + + )} +
+ + +
+
+
+
+
+
+ ); +} +/** + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 传递的时候只传实际使用值 + * @returns + */ +function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + binNumber, + SLOT_NUMBER, + leftPoint, + rightPoint, + currentPoint, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + getLiquiditySpot, + getLiquidityForCurveAndBidAskMode, + } = useContext(LiquidityProviderData); + const tokenSort = tokenX.id == currentSelectedPool.token_x; + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const new_liquidity = getLiquiditySpot(); + add_liquidity(new_liquidity); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + nftList = getLiquidityForCurveAndBidAskMode(); + if (!nftList) { + setAddLiquidityButtonLoading(false); + return; + } + /** + * 计算出 nftList token x tokeny 的数量,这是需要的总数量 + * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 + * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 + */ + let last_total_needed_token_x_amount = Big(0); + let last_total_needed_token_y_amount = Big(0); + nftList.forEach((nft: IAddLiquidityInfo) => { + const { amount_x, amount_y } = nft; + last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( + amount_x || 0 + ); + last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( + amount_y || 0 + ); + }); + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: last_total_needed_token_x_amount.toFixed(), + amount_y: last_total_needed_token_y_amount.toFixed(), + }); + } + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + function getButtonText() { + let txt: any = ( + + ); + if (invalidRange) { + txt = ( + + ); + } else if ( + (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || + (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || + (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) + ) { + txt = ( + + ); + } else if ( + !onlyAddXToken && + !onlyAddYToken && + (+tokenXAmount == 0 || +tokenYAmount == 0) + ) { + txt = ( + + ); + } else if ( + +tokenXAmount > 0 && + new BigNumber(tokenXAmount).isGreaterThan( + getMax(tokenX, tokenXBalanceFromNear) + ) + ) { + txt = ( + + ); + } else if ( + +tokenYAmount > 0 && + new BigNumber(tokenYAmount).isGreaterThan( + getMax(tokenY, tokenYBalanceFromNear) + ) + ) { + txt = ( + + ); + } + return txt; + } + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + if (tokenSort) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + } else if (onlyAddYToken) { + if (tokenSort) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + return !(condition1 && condition2); + } + const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquiditySpot + : addLiquidityForCurveAndBidAskMode; + + return ( +
+ {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} +
+ ); +} +function PointsComponent() { + const { pair_is_reverse } = useContext(AddLiquidityProviderData); + if (pair_is_reverse) { + return ; + } else { + return ; + } +} +function SetPointsComponent() { + const { + binNumber, + setBinNumber, + currentSelectedPool, + tokenX, + tokenY, + tokenXAmount, + tokenYAmount, + set_token_amount_tip, + + pointChange, + currentPoint, + liquidityShape, + + leftPoint, + setLeftPoint, + rightPoint, + setRightPoint, + + SLOT_NUMBER, + BIN_WIDTH, + + switch_pool_loading, + + isSignedIn, + } = useContext(LiquidityProviderData); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const [radius, setRadius] = useState(); + const [targetCustomPrice, setTargetCustomPrice] = useState(''); + const [leftCustomPrice, setLeftCustomPrice] = useState(''); + const [rightCustomPrice, setRightCustomPrice] = useState(''); + const [targetPoint, setTargetPoint] = useState(); + + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + + const [slider_point_min, set_slider_point_min] = useState(); + const [slider_point_max, set_slider_point_max] = useState(); + + const [slider_left_value, set_slider_left_value] = useState(); + const [slider_right_value, set_slider_right_value] = useState(); + + const { token_x, token_y } = currentSelectedPool; + const token_x_decimals = + tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; + const token_y_decimals = + tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; + const tokenSort = tokenX.id == currentSelectedPool.token_x; + + // init + useEffect(() => { + if (currentSelectedPool?.pool_id && !switch_pool_loading) { + const { current_point } = currentSelectedPool; + const right_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + setTargetPoint(current_point); + setRadius(RADIUS_DEFAULT_NUMBER); + setLeftPoint(left_point); + setRightPoint(right_point); + set_slider_point_range(); + setPriceRangeMode('by_range'); + setChartTab('liquidity'); + } + }, [currentSelectedPool, switch_pool_loading]); + + // 中文 左侧改变===》点位 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + // effect bin + const diff = rightPoint - leftPoint; + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect slider + const slider_left_value = get_slider_value_by_point(leftPoint); + const slider_right_value = get_slider_value_by_point(rightPoint); + + set_slider_left_value(slider_left_value); + set_slider_right_value(slider_right_value); + // effect right area + pointChange({ leftPoint, rightPoint, currentPoint }); + } + }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); + + useEffect(() => { + if ( + liquidityShape == 'Spot' && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) + ) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } + }, [liquidityShape]); + + // 数据有变动==》去掉token 太少提示 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + set_token_amount_tip(null); + } + }, [liquidityShape, leftPoint, rightPoint]); + + // 修改bin --> 合适的右点位 --->合适的bin + function changeBin(bin: number) { + let appropriate_right_point = leftPoint + BIN_WIDTH * bin; + if (appropriate_right_point > POINTRIGHTRANGE) { + appropriate_right_point = POINTRIGHTRANGE; + } + const appropriate_bin_number = + (appropriate_right_point - leftPoint) / BIN_WIDTH; + setRightPoint(appropriate_right_point); + setBinNumber(appropriate_bin_number); + } + + // 修改radius-->合适的左右点位 --->合适的radius + function changeRadius(radius: number) { + let appropriate_right_point = handlePointToAppropriatePoint( + targetPoint + BIN_WIDTH * radius + ); + let appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + const appropriate_radius = + (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + setRadius(appropriate_radius); + } + // 修改 targetPrice-->合适的左右点位--->合适的targetPrice + function handleTargetPriceToAppropriatePoint(price: string) { + let appropriate_target_point = handlePriceToAppropriatePoint(price); + const appropriate_right_point = handlePointToAppropriatePoint( + appropriate_target_point + BIN_WIDTH * radius + ); + const appropriate_left_point = handlePointToAppropriatePoint( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + return appropriate_target_point; + } + // 设置slider可以操作的point 左右点位区间 + function set_slider_point_range() { + const { current_point } = currentSelectedPool; + const max_point = handlePointToAppropriatePoint( + current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) + ); + const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; + set_slider_point_min(min_point); + set_slider_point_max(max_point); + } + function get_slider_value_by_point(point: number) { + const value = (point - slider_point_min) / BIN_WIDTH; + return value; + } + function get_point_by_slider_value(v: number) { + const new_point = slider_point_min + v * BIN_WIDTH; + return new_point; + } + function handlePointToAppropriatePoint(point: number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } + function handlePriceToAppropriatePoint(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; + } + function getLeftPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(leftPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getRightPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(rightPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getTargetPrice() { + if (currentSelectedPool && currentSelectedPool.pool_id) { + const { token_x, token_y } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + let price = getPriceByPoint(targetPoint, decimalRate); + // if (tokenX.id == token_y) { + // price = new BigNumber(1).dividedBy(price).toFixed(); + // } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getPair() { + if (tokenSort) { + return `(${tokenX.symbol}/${tokenY.symbol})`; + } else { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } + } + return ( +
+ {/* chart area */} +
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + setChartTab('yours'); + }} + > + Yours + +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )} +
+ {isSignedIn && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ {/* set price range area */} +
+ {/* price range mode area */} +
+
+ + + + {getPair()} + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + changeRadius(radius); + }} + > + + +
+
+ {/* content */} + {/*
+ +
*/} +
+ {/* target price input box */} +
+ + + + +
+ + {/* radius input box */} +
+ + + + +
+ + {/* min price input box */} +
+ + + + +
+ + {/* max price input box */} +
+ + + + +
+ + {/* bin number input box */} +
+ + + + +
+
+ {/* tip in foot */} +
+ *Only NEAR is needed in the price range you choose. +
+
+
+ ); +} +function SetPointsComponentReverse() { + const { + binNumber, + setBinNumber, + currentSelectedPool, + tokenX, + tokenY, + set_token_amount_tip, + + pointChange, + currentPoint, + liquidityShape, + + SLOT_NUMBER, + BIN_WIDTH, + + switch_pool_loading, + + isSignedIn, + } = useContext(LiquidityProviderData); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const [radius, setRadius] = useState(); + const [targetCustomPrice, setTargetCustomPrice] = useState(''); + const [leftCustomPrice, setLeftCustomPrice] = useState(''); + const [rightCustomPrice, setRightCustomPrice] = useState(''); + const [targetPoint, setTargetPoint] = useState(); + + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + + const token_x_decimals = tokenX.decimals; + const token_y_decimals = tokenY.decimals; + + // init + useEffect(() => { + if (currentSelectedPool?.pool_id && !switch_pool_loading) { + const { current_point } = currentSelectedPool; + const left_point = get_bin_point_by_point( + current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + const right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + setTargetPoint(current_point); + setRadius(RADIUS_DEFAULT_NUMBER); + setLeftPoint(left_point); + setRightPoint(right_point); + setPriceRangeMode('by_range'); + setChartTab('liquidity'); + } + }, [currentSelectedPool, switch_pool_loading]); + + // 中文 左侧点位改变 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + // effect bin + const diff = leftPoint - rightPoint; + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect right area + // todo + console.log( + 'real_leftPoint, real_rightPoint, real_current_point', + rightPoint, + leftPoint, + currentPoint + ); + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } + }, [leftPoint, rightPoint, BIN_WIDTH]); + + useEffect(() => { + if ( + liquidityShape == 'Spot' && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) + ) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } + }, [liquidityShape]); + + // 数据有变动==》去掉token 太少提示 + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + set_token_amount_tip(null); + } + }, [liquidityShape, leftPoint, rightPoint]); + + // 修改bin --> 合适的右点位 --->合适的bin + function changeBin(bin: number) { + let appropriate_right_point = leftPoint - BIN_WIDTH * bin; + if (appropriate_right_point < POINTLEFTRANGE) { + appropriate_right_point = POINTLEFTRANGE; + } + const appropriate_bin_number = + (leftPoint - appropriate_right_point) / BIN_WIDTH; + setRightPoint(appropriate_right_point); + setBinNumber(appropriate_bin_number); + } + + // 修改radius-->合适的左右点位 --->合适的radius + function changeRadius(radius: number) { + let appropriate_left_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * radius + ); + let appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + const appropriate_radius = + (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + setRadius(appropriate_radius); + } + // 修改 targetPrice-->合适的左右点位--->合适的targetPrice + function handleTargetPriceToAppropriatePoint(price: string) { + let appropriate_target_point = get_bin_point_by_price(reverse_price(price)); + const appropriate_left_point = get_bin_point_by_point( + appropriate_target_point + BIN_WIDTH * radius + ); + const appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + return appropriate_target_point; + } + function getLeftPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(leftPoint) + ) { + const price = reverse_price(get_price_by_point(leftPoint)); + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getRightPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(rightPoint) + ) { + const price = reverse_price(get_price_by_point(rightPoint)); + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getTargetPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(targetPoint) + ) { + let price = reverse_price(get_price_by_point(targetPoint)); + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function get_point_by_price(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate_point = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const decimalRate_price = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_bin_point_by_price(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; + } + function get_bin_point_by_point(point: number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } + function getPair() { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } + return ( +
+ {/* chart area */} +
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + setChartTab('yours'); + }} + > + Yours + +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )} +
+ {isSignedIn && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ {/* */} +
+ )} +
+ {/* set price range area */} +
+ {/* price range mode area */} +
+
+ + + + {getPair()} + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + changeRadius(radius); + }} + > + + +
+
+ {/* content */} +
+ {/* target price input box */} +
+ + + + +
+ + {/* radius input box */} +
+ + + + +
+ + {/* min price input box */} +
+ + + + { + return get_bin_point_by_price(reverse_price(price)); + }} + disbaled={priceRangeMode === 'by_radius'} + customPrice={leftCustomPrice} + getPrice={getLeftPrice} + setCustomPrice={setLeftCustomPrice} + inputStatus={leftInputStatus} + setInputStatus={setLeftInputStatus} + setPoint={setLeftPoint} + point={leftPoint} + > +
+ + {/* max price input box */} +
+ + + + { + return get_bin_point_by_price(reverse_price(price)); + }} + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + setPoint={setRightPoint} + point={rightPoint} + disbaled={priceRangeMode === 'by_radius'} + > +
+ + {/* bin number input box */} +
+ + + + +
+
+ {/* tip in foot */} +
+ *Only NEAR is needed in the price range you choose. +
+
+
+ ); +} + +/** + * step1 slider设置数字区间[0, 20] + * step2 数字区间 和 point区间做一个 双向 映射 + * step step 为 一个bin,区间设定逻辑是 以当前价格为起点,向左向右各辐射30个point(可配置) + * @param param0 + * @returns + */ +function Slider({ + value, + set_slider_left_value, + set_slider_right_value, + set_left_point, + set_right_point, + get_point_by_slider_value, +}: { + value: any; + set_slider_left_value: Function; + set_slider_right_value: Function; + set_left_point: Function; + set_right_point: Function; + get_point_by_slider_value: Function; +}) { + return ( + { + const [num1, num2] = v; + set_slider_left_value(num1); + set_slider_right_value(num2); + const left_point = get_point_by_slider_value(num1); + const right_point = get_point_by_slider_value(num2); + set_left_point(left_point); + set_right_point(right_point); + }} + value={value} + min={0} + max={SLIDER_BIN_NUMBER} + step={1} + pearling={true} + /> + ); +} + +function CreatePoolComponent({ + currentSelectedPool, + tokenX, + tokenY, + tokenPriceList, + buttonSort, +}: { + currentSelectedPool: PoolInfo; + tokenX: TokenMetadata; + tokenY: TokenMetadata; + tokenPriceList: Record; + buttonSort: boolean; +}) { + const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); + const [createPoolRate, setCreatePoolRate] = useState(''); + const [rateStatus, setRateStatus] = useState(true); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + if (createPoolRate) { + const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); + setCreatePoolRate(toPrecision(rateString, 6)); + } + }, [buttonSort]); + function getCurrentPriceValue(token: TokenMetadata) { + if (token) { + const price = tokenPriceList[token.id]?.price; + return price ? `${'$' + price}` : '$-'; + } else { + return '$-'; + } + } + function createPool() { + setCreatePoolButtonLoading(true); + const { fee } = currentSelectedPool; + const pointDelta = POINTDELTAMAP[fee]; + let decimalRate = + Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); + let init_point = getPointByPrice( + pointDelta, + createPoolRate, + decimalRate, + true + ); + const arr = [tokenX.id, tokenY.id]; + arr.sort(); + if (arr[0] !== tokenX.id) { + decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + init_point = getPointByPrice( + pointDelta, + new BigNumber(1).dividedBy(createPoolRate).toFixed(), + decimalRate, + true + ); + create_pool({ + token_a: tokenY.id, + token_b: tokenX.id, + fee: currentSelectedPool.fee, + init_point, + }); + } else { + create_pool({ + token_a: tokenX.id, + token_b: tokenY.id, + fee: currentSelectedPool.fee, + init_point, + }); + } + } + function switchRate() { + setRateStatus(!rateStatus); + } + function getPoolRate() { + if (createPoolRate) { + const rate = 1 / +createPoolRate; + return toPrecision(rate.toString(), 6); + } + return ''; + } + const mobileDevice = isMobile(); + return ( +
+
+ + : +
+
+ +
+
+

+ +

+
+
+

+ +

+
+ + 1 {toRealSymbol(tokenX?.symbol)} = + +
+ { + setCreatePoolRate(target.value); + }} + /> + + {toRealSymbol(tokenY?.symbol)} + +
+
+
+ + + +
+ {rateStatus ? ( +
+ 1 {toRealSymbol(tokenX?.symbol)} + + ({getCurrentPriceValue(tokenX)}) + + + + {createPoolRate} {toRealSymbol(tokenY?.symbol)} + +
+ ) : ( +
+ 1 {toRealSymbol(tokenY?.symbol)} + + ({getCurrentPriceValue(tokenY)}) + + + + {getPoolRate()} {toRealSymbol(tokenX?.symbol)} + +
+ )} + + +
+
+
+
+
+ {isSignedIn ? ( + + ( + <> + + + )} + /> + + ) : ( + + )} +
+ ); +} + +function NoDataComponent(props: any) { + const { isNoPool } = props; + const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); + const mobileDevice = isMobile(); + return ( +
+
+ +
+ {isNoPool ? ( +
+ +
+ ) : null} +
+ {/* range chart area */} +
+ +
+ {/* input range area */} +
+
+
+ + + +
+
+ +
+ +
+ +
+
+
+
+ + + +
+
+ +
+ +
+ +
+
+
+
+
+
+ + ( + <> + {isNoPool ? ( + + ) : ( + + )} + + )} + /> + +
+ ); +} +function PointInputComponent({ + handlePriceToAppropriatePoint, + customPrice, + setCustomPrice, + + getPrice, + point, + setPoint, + + inputStatus, + setInputStatus, + disbaled, +}: any) { + return ( +
+ { + setInputStatus(false); + if (customPrice) { + const appropriate_point_temp = + handlePriceToAppropriatePoint(customPrice); + setPoint(appropriate_point_temp); + } else { + setPoint(point); + } + }} + disabled={disbaled} + value={inputStatus ? customPrice : getPrice()} + onChange={({ target }) => { + setInputStatus(true); + const inputPrice = target.value; + if (Big(target.value || 0).lt(0)) { + setCustomPrice('0'); + } else { + setCustomPrice(inputPrice); + } + }} + /> +
+ ); +} + +export function IntegerInputComponent({ + value, + setValue, + disabled, + triggerByValue, +}: any) { + const removeLeadingZeros = (s: string) => { + const oldLen = s.length; + s = s.replace(/^0+/, ''); + + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + setValue(val); + if (val) { + triggerByValue(val); + } + }; + + return ( +
+ { + if (!target.value) { + setValue(1); + triggerByValue(1); + } + }} + onChange={({ target }) => { + handleChange(target.value); + }} + /> +
+ ); +} +function OneSide({ show }: { show: boolean }) { + return ( +
+ + +
+ +
+
+ ); +} +function InvalidRange({ show }: { show: boolean }) { + return ( +
+ + +
+ +
+
+ ); +} +function InputAmount({ + token, + balance, + tokenPriceList, + changeAmount, + amount, + currentSelectedPool, + hidden, +}: { + token: TokenMetadata; + balance: string; + tokenPriceList: Record; + changeAmount: any; + amount: string; + currentSelectedPool: PoolInfo; + hidden?: Boolean; +}) { + const [inputPrice, setInputPrice] = useState(''); + const [showNearTip, setShowNearTip] = useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + const price = token ? tokenPriceList[token.id]?.price : ''; + if (price && amount) { + setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); + } else { + setInputPrice(''); + } + if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { + const difference = new BigNumber(maxBalance).minus(amount); + const b = difference.toFixed(); + const r = difference.isLessThan(0); + if (r) { + setShowNearTip(true); + } else { + setShowNearTip(false); + } + } else { + setShowNearTip(false); + } + }, [amount, token, tokenPriceList.length]); + function getBalance() { + let r = '0'; + if (token && balance) { + r = formatWithCommas(toPrecision(balance.toString(), 3)); + } + return isSignedIn ? r : '-'; + } + function showCurrentPrice() { + if (isNoPool) { + return '$-'; + } else if (inputPrice) { + return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); + } + return '$-'; + } + const maxBalance = + token?.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + const isNoPool = !currentSelectedPool?.pool_id; + return ( +
+ + {showNearTip && !isNoPool ? ( +
+ + +
+ ) : null} +
+ ); +} From 2d9b0d93c518fabb57c417162fe1bcf7b1facacf Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 6 Aug 2023 20:12:33 +0800 Subject: [PATCH 103/204] update yarn.lock --- yarn.lock | 5 ----- 1 file changed, 5 deletions(-) diff --git a/yarn.lock b/yarn.lock index ca20e0e6e..430b49adc 100644 --- a/yarn.lock +++ b/yarn.lock @@ -11450,11 +11450,6 @@ ms@2.1.3, ms@^2.1.1: resolved "https://registry.yarnpkg.com/ms/-/ms-2.1.3.tgz#574c8138ce1d2b5861f0b44579dbadd60c6615b2" integrity sha512-6FlzubTLZG3J2a/NVCAleEhjzq5oxgHyaCU9yYXvcLsvoVaHJq/s5xXI6/XXP6tz7R9xAOtHnSO/tXtF3WRTlA== -multi-range-slider-react@^2.0.3: - version "2.0.3" - resolved "https://registry.yarnpkg.com/multi-range-slider-react/-/multi-range-slider-react-2.0.3.tgz#f28233a23bb6711c854c5bf776493cb50b90c66f" - integrity sha512-mMjrVO9rP9uRMHq61MHNmiet5NxCMblrZwX9zEcqNBCguGXdSNlrhQfeluCy9ICpq7NOT9cKn+RhANrB5TBLkQ== - multicast-dns@^7.2.5: version "7.2.5" resolved "https://registry.yarnpkg.com/multicast-dns/-/multicast-dns-7.2.5.tgz#77eb46057f4d7adbd16d9290fa7299f6fa64cced" From 66c7568a7fb1492c557e40e5a464e91c65966016 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 6 Aug 2023 23:41:36 +0800 Subject: [PATCH 104/204] fix bugs about the order of tokens --- src/components/d3Chart/DclChart.tsx | 55 +++++++++++++-------- src/components/pool/RemovePoolV3.tsx | 13 +++-- src/components/swap/SwapLimitOrderChart.tsx | 42 ++++++++++------ src/pages/poolsV3/PoolDetailV3.tsx | 17 ++++++- 4 files changed, 89 insertions(+), 38 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 44b58febe..b0fe5495c 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -146,6 +146,11 @@ export default function DclChart({ get_chart_data(); } }, [pool, accountId]); + useEffect(() => { + if (chartDataList) { + init_price_range(); + } + }, [reverse]); // draw chart useEffect(() => { if ( @@ -155,7 +160,7 @@ export default function DclChart({ drawChart(); setDrawChartDone(true); } - }, [price_range, chartDataList]); + }, [price_range, chartDataList, reverse]); // generate user chart useEffect(() => { if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { @@ -354,8 +359,14 @@ export default function DclChart({ }); const min_point = points[0]; const max_point = points[points.length - 1]; - const min_price = reverse_price(get_price_by_point(max_point)); - const max_price = reverse_price(get_price_by_point(min_point)); + let min_price, max_price; + if (reverse) { + min_price = reverse_price(get_price_by_point(max_point)); + max_price = reverse_price(get_price_by_point(min_point)); + } else { + min_price = get_price_by_point(min_point); + max_price = get_price_by_point(max_point); + } set_user_liquidities_detail({ total_value: formatWithCommas_usd(total_value.toFixed()), min_price: formatPrice(min_price), @@ -387,15 +398,18 @@ export default function DclChart({ setPool(p); } async function get_chart_data() { - const { range } = getConfig(); const list = await get_data_from_back_end(); + setChartDataList(list); + init_price_range(); + } + function init_price_range() { + const { range } = getConfig(); if (chartType !== 'USER') { const [price_l_default, price_r_default] = get_price_range_by_percent(range); set_price_range([+price_l_default, +price_r_default]); setZoom(range); } - setChartDataList(list); } async function get_data_from_back_end() { const { token_x_metadata, token_y_metadata, pool_id } = pool; @@ -792,7 +806,7 @@ export default function DclChart({ if (reverse) { return +d.point_r >= current_point ? colors[1] : colors[0]; } else { - return +d.point_l >= current_point ? colors[0] : colors[1]; + return +d.point_l >= current_point ? colors[1] : colors[0]; } }); } @@ -835,7 +849,7 @@ export default function DclChart({ if (reverse) { return +d.point_r >= current_point ? colors[1] : colors[0]; } else { - return +d.point_l >= current_point ? colors[0] : colors[1]; + return +d.point_l >= current_point ? colors[1] : colors[0]; } }) .attr('opacity', '0.7'); @@ -1551,9 +1565,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${ - reverse ? binDetail?.colors[1] : binDetail?.colors[0] - }`, + backgroundColor: `${binDetail?.colors[1]}`, }} > in Liquidity @@ -1570,9 +1582,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${ - reverse ? binDetail?.colors[1] : binDetail?.colors[0] - }`, + backgroundColor: `${binDetail?.colors[1]}`, }} > by Limit Orders @@ -1601,9 +1611,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${ - reverse ? binDetail?.colors[0] : binDetail?.colors[1] - }`, + backgroundColor: `${binDetail?.colors[0]}`, }} > in Liquidity @@ -1620,9 +1628,7 @@ export default function DclChart({ width: '10px', height: '10px', borderRadius: '3px', - backgroundColor: `${ - reverse ? binDetail?.colors[0] : binDetail?.colors[1] - }`, + backgroundColor: `${binDetail?.colors[0]}`, }} > by Limit Orders @@ -1643,9 +1649,18 @@ export default function DclChart({
Price Range - + {user_liquidities_detail?.min_price} -{' '} {user_liquidities_detail?.max_price} + + {reverse + ? pool?.token_x_metadata?.symbol + + '/' + + pool?.token_y_metadata?.symbol + : pool?.token_y_metadata?.symbol + + '/' + + pool?.token_x_metadata?.symbol} +
diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 753ece80e..38593c66f 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -732,15 +732,22 @@ export const RemovePoolV3 = (props: any) => {
{pair_is_reverse - ? `${tokens[1]?.symbol}/${tokens[0]?.symbol}` - : `${tokens[0]?.symbol}/${tokens[1]?.symbol}`} + ? `${tokens[1]?.symbol}-${tokens[0]?.symbol}` + : `${tokens[0]?.symbol}-${tokens[1]?.symbol}`}
{min_received_total_value}
-
+
+ + ( + {pair_is_reverse + ? `${tokens[0]?.symbol}/${tokens[1]?.symbol}` + : `${tokens[1]?.symbol}/${tokens[0]?.symbol}`} + ) + {maxPoint && ( (); const [orders, setOrders] = useState(); - const [switch_token, set_switch_token] = useState('X'); + const [switch_token, set_switch_token] = useState(); const [buy_token_x_list, set_buy_token_x_list] = useState(); const [sell_token_x_list, set_sell_token_x_list] = @@ -38,6 +38,7 @@ export default function SwapLimitOrderChart() { const [buy_list, set_buy_list] = useState(); const [sell_list, set_sell_list] = useState(); const [market_loading, set_market_loading] = useState(false); + const [pair_is_reverse, set_pair_is_reverse] = useState(false); const { dcl_pool_id } = useContext(SwapProContext); const GEARS = [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; const [zoom, setZoom] = useState(GEARS[0]); @@ -49,7 +50,6 @@ export default function SwapLimitOrderChart() { if (pool_id) { get_points_of_orders(); get_pool_detail(); - set_switch_token('X'); setZoom(GEARS[0]); } }, [pool_id]); @@ -90,27 +90,27 @@ export default function SwapLimitOrderChart() { const x_symbol = toRealSymbol(token_x_metadata.symbol); const y_symbol = toRealSymbol(token_y_metadata.symbol); if (switch_token == 'X') { - const x_icons = ( + const y_icons = ( <> - + ); - return [`${x_symbol}/${y_symbol}`, `${x_symbol}`, x_icons]; + return [`${y_symbol}/${x_symbol}`, `${y_symbol}`, y_icons]; } else if (switch_token == 'Y') { - const y_icons = ( + const x_icons = ( <> - + ); - return [`${y_symbol}/${x_symbol}`, `${y_symbol}`, y_icons]; + return [`${x_symbol}/${y_symbol}`, `${x_symbol}`, x_icons]; } } return []; @@ -133,6 +133,17 @@ export default function SwapLimitOrderChart() { const { token_x, token_y } = p; p.token_x_metadata = await ftGetTokenMetadata(token_x); p.token_y_metadata = await ftGetTokenMetadata(token_y); + const tokens = sort_tokens_by_base([ + p.token_x_metadata, + p.token_y_metadata, + ]); + if (tokens[0].id == p.token_x_metadata.id) { + set_switch_token('X'); + set_pair_is_reverse(false); + } else { + set_switch_token('Y'); + set_pair_is_reverse(true); + } setPool(p); } function process_orders() { @@ -301,7 +312,6 @@ export default function SwapLimitOrderChart() { if (targetPercent) { setZoom(targetPercent); } - console.log('放大中- targetPercent', targetPercent); } return ( {get_rate_element()}
-
+
{ set_switch_token('X'); @@ -359,7 +373,7 @@ export default function SwapLimitOrderChart() {
- {/* 控件按钮*/} + {/* control button*/}
{/*
-1) { + pair_is_reverse = true; + } + useEffect(() => { if (liquidities) { const temp_list: UserLiquidityDetail[] = []; @@ -976,6 +981,7 @@ function YourLiquidityBox(props: { hoverBoxHidden: true, }} chartType="USER" + reverse={pair_is_reverse ? noReverseRange : !noReverseRange} >
@@ -2262,6 +2268,10 @@ export function RecentTransactions({ const swapOut = tokens.find((t) => t.id !== tx.sell_token); if (!swapIn || !swapOut) return null; + let reverse = false; + if (sort_tokens_by_base([swapIn, swapOut])[0].id !== swapIn.id) { + reverse = true; + } const AmountIn = toReadableNumber(swapIn.decimals, tx.amount); const displayInAmount = @@ -2327,7 +2337,11 @@ export function RecentTransactions({ - {toRealSymbol(swapOut.symbol)}/{toRealSymbol(swapIn.symbol)} + {reverse + ? `${toRealSymbol(swapOut.symbol)}/${toRealSymbol(swapIn.symbol)}` + : `${toRealSymbol(swapIn.symbol)}/${toRealSymbol( + swapOut.symbol + )}`} @@ -2797,6 +2811,7 @@ function LiquidityChart(props: any) {
)} From 99f934f5ca0eb4fa49f436d2e1cbe10993eb751f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 8 Aug 2023 00:43:43 +0800 Subject: [PATCH 105/204] update to batch interface --- src/components/button/Button.tsx | 4 +- src/components/farm/FarmsDclDetail.tsx | 42 +++++--- src/components/pool/RemovePoolV3.tsx | 1 - src/components/pool/YourLiquidityV2.tsx | 127 ++++++++++++----------- src/pages/poolsV3/PoolDetailV3.tsx | 128 ++++++++++++------------ src/services/farm.ts | 34 ++++--- src/services/swapV3.ts | 30 +++--- 7 files changed, 195 insertions(+), 171 deletions(-) diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index ca826d503..e7eef9454 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -534,8 +534,8 @@ export function OprationButton(props: any) { onClick={onClick} disabled={disabled} className={`flex items-center justify-center w-full h-full ${ - btnClassName ? btnClassName : '' - }`} + disabled ? 'cursor-not-allowed' : '' + } ${btnClassName ? btnClassName : ''}`} > {props.children} diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index bbaa21a4f..033ed3d5d 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -349,20 +349,12 @@ export default function FarmsDclDetail(props: { } ); if (liquidities_minted_in_another_seed.length > 0) { - const liquidity_another = liquidities_minted_in_another_seed[0]; - const { mft_id } = liquidity_another; - const list_new = JSON.parse(JSON.stringify(list)); - const seed_id_another = - REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); - const { free_amount = '0', locked_amount = '0' } = - user_seeds_map[seed_id_another] || {}; - const user_seed_amount_another = new BigNumber(free_amount) - .plus(locked_amount) - .toFixed(); - const seed_another = all_seeds.find((seed: Seed) => { - return seed.seed_id == seed_id_another; - }); - if (seed_another) { + const another_seeds = get_another_seeds( + liquidities_minted_in_another_seed + ); + Object.values(another_seeds).forEach((another_seed_detail: any) => { + const list_new = JSON.parse(JSON.stringify(list)); + const [seed_another, user_seed_amount_another] = another_seed_detail; const [ temp_farming_another, temp_free_another, @@ -390,7 +382,7 @@ export default function FarmsDclDetail(props: { } } ); - } + }); const temp_unavailable_new: UserLiquidityInfo[] = []; const frees_extra = temp_unavailable.filter( (liquidity: UserLiquidityInfo) => { @@ -426,6 +418,26 @@ export default function FarmsDclDetail(props: { setListLiquiditiesLoading(false); } } + function get_another_seeds(minted_liquidities: UserLiquidityInfo[]) { + const target: any = {}; + minted_liquidities.forEach((liquidity_minted_in_another_seed) => { + const { mft_id } = liquidity_minted_in_another_seed; + const seed_id_another = + REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); + const { free_amount = '0', locked_amount = '0' } = + user_seeds_map[seed_id_another] || {}; + const user_seed_amount_another = new BigNumber(free_amount) + .plus(locked_amount) + .toFixed(); + const seed_another: Seed = all_seeds.find((seed: Seed) => { + return seed.seed_id == seed_id_another; + }); + if (seed_another) { + target[seed_another.seed_id] = [seed_another, user_seed_amount_another]; + } + }); + return target; + } function sortTokens(tokens: TokenMetadata[]) { tokens.sort((a: TokenMetadata, b: TokenMetadata) => { if (a.symbol === 'NEAR') return 1; diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 38593c66f..7a22368ca 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -536,7 +536,6 @@ export const RemovePoolV3 = (props: any) => { batch_remove_liquidity, batch_update_liquidity, mint_liquidities, - // widthdraw_infos, }); } function get_minimum_received_data() { diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 2fe1f1c78..b9c7543f7 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -349,23 +349,23 @@ export function YourLiquidityV2(props: any) { return (
{liquidityLoadingDone && ( -
-
+
+
-
+
-
Trailing 24hr APR
-
+
Trailing 24hr APR
+
(); const [tip_seed, set_tip_seed] = useState(); const [joined_seeds, set_joined_seeds] = useState(); + const [joined_seeds_done, set_joined_seeds_done] = useState(false); + + const [removeButtonTip, setRemoveButtonTip] = useState(false); const tokens = sort_tokens_by_base(tokenMetadata_x_y); const { accountId } = useWalletSelector(); @@ -1688,7 +1691,6 @@ function UserLiquidityLineStyleGroup1({ get_24_apr(); } }, [poolDetail, tokenPriceList, tokenMetadata_x_y]); - // todo useEffect(() => { if ( all_seeds.length && @@ -1801,6 +1803,7 @@ function UserLiquidityLineStyleGroup1({ } ); set_joined_seeds(joined_seeds); + set_joined_seeds_done(true); } }, [groupYourLiquidityList, liquidities_list, tokenPriceList, all_seeds]); @@ -2073,11 +2076,11 @@ function UserLiquidityLineStyleGroup1({ > {/* for PC */}
-
+
{ e.preventDefault(); e.stopPropagation(); @@ -2085,7 +2088,7 @@ function UserLiquidityLineStyleGroup1({ }} > {/* 1/3 */} -
+
{/* 2/3 */} {/* Price Range */} -
+
{intersectionRangeList.map((range: string[], i: number) => { return ( @@ -2162,7 +2165,7 @@ function UserLiquidityLineStyleGroup1({
{/* Trailing 24hr APR */} -
+
{accountAPR || '-'} {joined_seeds ? (
@@ -2209,7 +2212,7 @@ function UserLiquidityLineStyleGroup1({ ) : null}
{/* Your Liquidity */} -
+
{your_liquidity} {joined_seeds ? (
@@ -2275,38 +2278,15 @@ function UserLiquidityLineStyleGroup1({
) : null} - {/* {related_seed_info.your_apr ? ( -
- {in_farm_percent} - - - - - { - e.preventDefault(); - e.stopPropagation(); - go_farm(); - }} - > - - -
- ) : ( - - - )} */}
{/* tip */} {tip_seed ? ( -
+
@@ -2334,7 +2314,7 @@ function UserLiquidityLineStyleGroup1({ hover ? '' : 'hidden' }`} > -
+
{/* unclaimed fees */}
@@ -2382,22 +2362,55 @@ function UserLiquidityLineStyleGroup1({ > - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - rounded="rounded-lg" - disabled={!!joined_seeds} - px="px-0" - py="py-1" - style={{ minWidth: '5rem' }} - className={`flex-grow w-24 text-sm text-greenColor h-8 ${ - !!joined_seeds ? 'opacity-40' : '' +
{ + if (!!joined_seeds) { + setRemoveButtonTip(true); + } + }} + onMouseLeave={() => { + if (!!joined_seeds) { + setRemoveButtonTip(false); + } + }} > - - + { + e.stopPropagation(); + setShowRemoveBox(true); + }} + rounded="rounded-lg" + disabled={!!joined_seeds} + px="px-0" + py="py-1" + style={{ minWidth: '5rem' }} + className={`flex-grow w-24 text-sm text-greenColor h-8 ${ + !!joined_seeds ? 'opacity-40 pointer-events-none' : '' + }`} + > + + +
+ You have liquidity in farm, please unstake from{' '} + { + localStorage.setItem('BOOST_FARM_TAB', 'yours'); + openUrl('/v2farms'); + }} + > + Your Farm + {' '} + first. +
+
diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index d5bef8515..3bf7e4915 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -246,59 +246,6 @@ export default function PoolDetailV3() { } set_user_liquidities(user_liqudities_final); } - function displayRateDom() { - const { - current_point, - token_x_metadata, - token_y_metadata, - token_x, - token_y, - } = poolDetail; - const rate = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - let price = getPriceByPoint(current_point, rate); - let tokenPrice = tokenPriceList[token_x]?.price; - if (!currentRateDirection) { - price = new BigNumber(1).dividedBy(price).toFixed(); - tokenPrice = tokenPriceList[token_y]?.price; - } - let displayTokenPrice; - let displayRate; - if (!tokenPrice) { - displayTokenPrice = '-'; - } else if (new BigNumber(tokenPrice).isLessThan('0.001')) { - displayTokenPrice = '<$0.001'; - } else { - displayTokenPrice = `$${toPrecision(tokenPrice.toString(), 3)}`; - } - if (new BigNumber(price).isLessThan('0.001')) { - displayRate = ' < 0.001'; - } else { - displayRate = ` = ${toPrecision(price.toString(), 3)}`; - } - if (currentRateDirection) { - return ( - - 1 {token_x_metadata.symbol} - - {displayRate} {token_y_metadata.symbol} - - ); - } else { - return ( - - 1 {token_y_metadata.symbol} - - {displayRate} {token_x_metadata.symbol} - - ); - } - } - function switchRateButton() { - const now_direction = !currentRateDirection; - setCurrentRateDirection(now_direction); - } const handleSaveWatchList = () => { if (!isSignedIn) { modal.show(); @@ -631,6 +578,11 @@ function YourLiquidityBox(props: { const [earned_fee_x_amount, set_earned_fee_x_amount] = useState(); const [earned_fee_y_amount, set_earned_fee_y_amount] = useState(); const [operationType, setOperationType] = useState('add'); + const [hover, setHover] = useState(false); + const [noReverseRange, setNoReverseRange] = useState(true); + const [removeButtonTip, setRemoveButtonTip] = useState(false); + const [is_in_farming, set_is_in_farming] = useState(false); + const [is_in_farming_done, set_is_in_farming_done] = useState(false); const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; const { accountId } = useWalletSelector(); const history = useHistory(); @@ -638,7 +590,6 @@ function YourLiquidityBox(props: { if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { pair_is_reverse = true; } - useEffect(() => { if (liquidities) { const temp_list: UserLiquidityDetail[] = []; @@ -696,6 +647,19 @@ function YourLiquidityBox(props: { get_24_apr_and_fee(); } }, [poolDetail, tokenPriceList]); + useEffect(() => { + if (liquidities) { + const target = liquidities.find((l: UserLiquidityInfo) => { + return Big(l.part_farm_ratio || 0).gt(0); + }); + if (target) { + set_is_in_farming(true); + } else { + set_is_in_farming(false); + } + set_is_in_farming_done(true); + } + }, [liquidities]); async function get_24_apr_and_fee() { let apr_24 = '0'; let total_fee_earned = '0'; @@ -878,10 +842,6 @@ function YourLiquidityBox(props: { setOperationType('remove'); setShowSelectLiquidityBox(true); } - const [hover, setHover] = useState(false); - - const [noReverseRange, setNoReverseRange] = useState(true); - function getGroupLiquidities() { const tokenMetadata_x_y = [token_x_metadata, token_y_metadata]; @@ -1102,16 +1062,52 @@ function YourLiquidityBox(props: { > - { - e.stopPropagation(); - removeLiquidity(); +
{ + if (is_in_farming) { + setRemoveButtonTip(true); + } + }} + onMouseLeave={() => { + if (is_in_farming) { + setRemoveButtonTip(false); + } }} - color="#fff" - className={`flex-grow w-1 h-11 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover }`} > - - + { + e.stopPropagation(); + removeLiquidity(); + }} + disabled={is_in_farming || !is_in_farming_done} + color="#fff" + className={`flex-grow w-1 h-11 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ + is_in_farming || !is_in_farming_done + ? 'opacity-30 pointer-events-none' + : '' + } }`} + > + + +
+ You have liquidity in farm, please unstake from{' '} + { + localStorage.setItem('BOOST_FARM_TAB', 'yours'); + openUrl('/v2farms'); + }} + > + Your Farm + {' '} + first. +
+
{ + lpt_ids.push(l.lpt_id); + }); + if (lpt_ids.length) { transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ { - methodName: 'burn_v_liquidity', + methodName: 'batch_burn_v_liquidity', args: { - lpt_id: l.lpt_id, + lpt_ids, }, - gas: '200000000000000', + gas: '250000000000000', }, ], }); - }); + } const neededStorage = await checkTokenNeedsStorageDeposit_boost(); if (neededStorage) { transactions.unshift({ @@ -1394,18 +1398,12 @@ export const batch_stake_boost_nft = async ({ const [fixRange, dcl_pool_id, left_point, right_point] = temp_pool_id.split('&'); const transactions: Transaction[] = []; + const mint_infos: any[] = []; liquidities.forEach((l: UserLiquidityInfo) => { const { lpt_id, mft_id } = l; const functionCalls = []; if (!mft_id) { - functionCalls.push({ - methodName: 'mint_v_liquidity', - args: { - lpt_id, - dcl_farming_type: JSON.parse(fixRange), - }, - gas: '60000000000000', - }); + mint_infos.push([lpt_id, JSON.parse(fixRange)]); } else if (liquidity_is_in_other_seed(seed_id, mft_id)) { functionCalls.push( { @@ -1448,6 +1446,18 @@ export const batch_stake_boost_nft = async ({ }); } }); + if (mint_infos.length) { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_mint_v_liquidity', + args: { mint_infos }, + gas: '200000000000000', + }, + ], + }); + } transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 59b70a381..c62436972 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1261,34 +1261,31 @@ export const batch_remove_liquidity_contract = async ({ batch_remove_liquidity, batch_update_liquidity, mint_liquidities, -}: // widthdraw_infos, -{ +}: { token_x: TokenMetadata; token_y: TokenMetadata; batch_remove_liquidity: IRemoveLiquidityInfo[]; batch_update_liquidity: IBatchUpdateiquidityInfo; mint_liquidities: UserLiquidityInfo[]; - // widthdraw_infos: { - // min_withdraw_token_x_amount: string; - // min_withdraw_token_y_amount: string; - // }; }) => { const max_number = 10; const transactions: Transaction[] = []; if (mint_liquidities.length) { - const functionCallsV3: any = []; + const lpt_ids: any[] = []; mint_liquidities.forEach((l: UserLiquidityInfo) => { - functionCallsV3.push({ - methodName: 'burn_v_liquidity', - args: { - lpt_id: l.lpt_id, - }, - gas: '20000000000000', - }); + lpt_ids.push(l.lpt_id); }); transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: functionCallsV3, + functionCalls: [ + { + methodName: 'batch_burn_v_liquidity', + args: { + lpt_ids, + }, + gas: '250000000000000', + }, + ], }); } if (batch_remove_liquidity) { @@ -1395,9 +1392,6 @@ export const batch_remove_liquidity_contract = async ({ ], }); } - - // width draw - return executeMultipleTransactions(transactions); }; From 9d7ce741c388effdfad0cc6fdceeb10074bb70f9 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 8 Aug 2023 22:36:47 +0800 Subject: [PATCH 106/204] compelete function --- src/components/farm/FarmsDclDetail.tsx | 97 +- src/components/pool/YourLiquidityV2.tsx | 2096 +++++------------ src/components/pool/utils.ts | 13 + .../Orderly/components/MyOrder/index.tsx | 98 +- 4 files changed, 760 insertions(+), 1544 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 033ed3d5d..b1f0da012 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1102,8 +1102,43 @@ export default function FarmsDclDetail(props: { seed_id: detailData.seed_id, }; } + // unavailable 有没有把 min 过滤掉???? + function get_unavailable_text() { + let tip = ''; + const { seed_id, min_deposit } = detailData; + const { liquidities, total_v_liquidity } = get_stake_info(); + if (liquidities.length == 0) { + for (let i = 0; i < listLiquidities_unavailable.length; i++) { + const liquidity = listLiquidities_unavailable[i]; + const [left_point, right_point] = get_valid_range(liquidity, seed_id); + const inrange = +right_point > +left_point; + if (!inrange) { + tip = intl.formatMessage({ id: 'your_price_range_tip' }); + break; + } else if (liquidity.status_in_other_seed == 'staked') { + tip = intl.formatMessage({ id: 'position_has_staked_tip' }); + } else { + // min deposit + const rate = new BigNumber(min_deposit).dividedBy(total_v_liquidity); + let rate_display = rate.toFixed(1); + if (rate.isGreaterThan(rate_display)) { + rate_display = new BigNumber(rate_display).plus(0.1).toFixed(); + } + // your liquidity + tip = + intl.formatMessage({ id: 'minimum_tip' }) + + ' ' + + `${rate_display}x` + + ' ' + + intl.formatMessage({ id: 'your_liquidity_3' }); + } + } + } + } /** new end */ const isEmpty = !canStake && !canUnStake; + const stakeDisabled = !canStake || nft_stake_loading; + return (
@@ -1411,41 +1446,41 @@ export default function FarmsDclDetail(props: { {yp_farming_value}
- {/*
- {yp_percent} of your liquidity that can be farmed -
*/}
-
- - {yp_unFarm_value} - {' '} - available to stake -
-
- {canStake ? ( - +
+ - ( - - )} - /> - - ) : null} + {yp_unFarm_value} + {' '} + available to stake +
+
+ The minimum staking amount is 2x your liquidity +
+
+
+ + ( + + )} + /> + {canUnStake ? ( { + debugger; const groupedLiquidity = yourLiquidityList.reduce((acc, cur) => { const { pool_id } = cur; const [token_x, token_y] = pool_id.split('|'); @@ -378,12 +379,13 @@ export function YourLiquidityV2(props: any) { Object.entries(groupYourLiquidity).map( ([id, liquidity]: any, index: number) => { return ( - l.liquidityDetail)} tokenPriceList={tokenPriceList} all_seeds={all_seeds} + styleType={styleType} /> ); } @@ -391,396 +393,11 @@ export function YourLiquidityV2(props: any) {
); } -function UserLiquidityLine({ - liquidity, - all_seeds, - styleType, - tokenPriceList, - poolDetail, - liquidities_tokens_metas, - liquidityDetail, -}: { - liquidity: UserLiquidityInfo; - all_seeds: Seed[]; - styleType: string; - tokenPriceList: Record; - poolDetail: PoolInfo; - liquidities_tokens_metas: Record; - liquidityDetail: UserLiquidityInfo; -}) { - const [hover, setHover] = useState(false); - const [isInrange, setIsInrange] = useState(true); - const [your_liquidity, setYour_liquidity] = useState(''); - const [claimLoading, setClaimLoading] = useState(false); - const [showRemoveBox, setShowRemoveBox] = useState(false); - const [showAddBox, setShowAddBox] = useState(false); - const [related_farms, set_related_farms] = useState([]); - const [is_in_farming, set_is_in_farming] = useState(false); - const [related_seed_info, set_related_seed_info] = useState< - Record - >({}); - - const { lpt_id, pool_id, left_point, right_point, amount: L } = liquidity; - const [token_x, token_y, fee] = pool_id.split('|'); - const tokenMetadata_x_y = liquidities_tokens_metas - ? [liquidities_tokens_metas[token_x], liquidities_tokens_metas[token_y]] - : null; - const rate_need_to_reverse_display = useMemo(() => { - if (tokenMetadata_x_y) { - const [tokenX] = tokenMetadata_x_y; - if (TOKEN_LIST_FOR_RATE.indexOf(tokenX.symbol) > -1) return true; - return false; - } - }, [tokenMetadata_x_y?.length]); - - const history = useHistory(); - useEffect(() => { - get_pool_related_farms(); - }, []); - useEffect(() => { - if (poolDetail) { - judge_is_in_range(); - } - }, [poolDetail]); - useEffect(() => { - if (tokenMetadata_x_y && poolDetail && tokenPriceList) { - const { current_point } = poolDetail; - get_your_liquidity(current_point); - } - }, [poolDetail, tokenMetadata_x_y, tokenPriceList]); - useEffect(() => {}, []); - useEffect(() => { - const info = get_detail_the_liquidity_refer_to_seed({ - liquidity, - all_seeds, - is_in_farming, - related_farms, - tokenPriceList, - }); - set_related_seed_info(info); - }, [liquidity, all_seeds, is_in_farming, tokenPriceList, related_farms]); - - async function get_pool_related_farms() { - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - - if (is_in_farming) { - const id = liquidity.mft_id.slice(1); - const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + id; - const farmList = await list_seed_farms(seed_id); - set_related_farms(farmList); - } - - set_is_in_farming(is_in_farming); - } - async function judge_is_in_range() { - if (poolDetail) { - const { current_point } = poolDetail; - if (current_point >= left_point && right_point > current_point) { - setIsInrange(true); - } else { - setIsInrange(false); - } - } - } - function getRate(direction: string) { - let value = ''; - if (tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - if (direction == 'left') { - value = getPriceByPoint(left_point, decimalRate); - } else if (direction == 'right') { - value = getPriceByPoint(right_point, decimalRate); - } - if (rate_need_to_reverse_display && +value !== 0) { - value = new BigNumber(1).dividedBy(value).toFixed(); - } - } - return displayNumberToAppropriateDecimals(value); - } - function getLpt_id() { - return lpt_id.split('#')[1]; - } - - function get_your_liquidity(current_point: number) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const priceX = tokenPriceList[tokenX.id]?.price || 0; - const priceY = tokenPriceList[tokenY.id]?.price || 0; - let total_price; - // in range - if (current_point >= left_point && right_point > current_point) { - let tokenYAmount = getY(left_point, current_point, L, tokenY) || 0; - let tokenXAmount = getX(current_point + 1, right_point, L, tokenX) || 0; - const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); - tokenXAmount = new BigNumber(tokenXAmount).plus(amountx).toFixed(); - tokenYAmount = new BigNumber(tokenYAmount).plus(amounty).toFixed(); - const tokenYTotalPrice = new BigNumber(tokenYAmount).multipliedBy(priceY); - const tokenXTotalPrice = new BigNumber(tokenXAmount).multipliedBy(priceX); - total_price = tokenYTotalPrice.plus(tokenXTotalPrice).toFixed(); - } - // only y token - if (current_point >= right_point) { - const tokenYAmount = getY(left_point, right_point, L, tokenY); - const tokenYTotalPrice = new BigNumber(tokenYAmount).multipliedBy(priceY); - total_price = tokenYTotalPrice.toFixed(); - } - // only x token - if (left_point > current_point) { - const tokenXAmount = getX(left_point, right_point, L, tokenX); - const tokenXTotalPrice = new BigNumber(tokenXAmount).multipliedBy(priceX); - total_price = tokenXTotalPrice.toFixed(); - } - setYour_liquidity(formatWithCommas(toPrecision(total_price, 2))); - } - function getY( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const y = new BigNumber(L).multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - const y_result = y.toFixed(); - return toReadableNumber(token.decimals, toPrecision(y_result, 0)); - } - function getX( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const x = new BigNumber(L) - .multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ) - .toFixed(); - return toReadableNumber(token.decimals, toPrecision(x, 0)); - } - function get_X_Y_In_CurrentPoint( - tokenX: TokenMetadata, - tokenY: TokenMetadata, - L: string - ) { - const { liquidity, liquidity_x, current_point } = poolDetail; - const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); - let Ly = '0'; - let Lx = '0'; - // only remove y - if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { - Ly = L; - } else { - // have x and y - Ly = liquidity_y_big.toFixed(); - Lx = new BigNumber(L).minus(Ly).toFixed(); - } - const amountX = getXAmount_per_point_by_Lx(Lx, current_point); - const amountY = getYAmount_per_point_by_Ly(Ly, current_point); - const amountX_read = toReadableNumber( - tokenX.decimals, - toPrecision(amountX, 0) - ); - const amountY_read = toReadableNumber( - tokenY.decimals, - toPrecision(amountY, 0) - ); - return { amountx: amountX_read, amounty: amountY_read }; - } - function claimRewards(e: any) { - e.stopPropagation(); - if (!canClaim()) return; - setClaimLoading(true); - const [tokenX, tokenY] = tokenMetadata_x_y; - remove_liquidity({ - token_x: tokenX, - token_y: tokenY, - lpt_id, - amount: '0', - mft_id: '', - min_amount_x: '0', - min_amount_y: '0', - }); - } - function goYourLiquidityDetailPage(goType?: string) { - const pool_id = lpt_id.split('#')[0]; - const lptId = lpt_id.split('#')[1]; - const pool_name = get_pool_name(pool_id); - const link = `${pool_name}@${lptId}`; - if (goType == 'new window') { - openUrl(`/yoursLiquidityDetailV2/${link}`); - } else { - history.push(`/yoursLiquidityDetailV2/${link}`); - } - } - function goPoolDetailPage() { - const params_str = get_pool_name(liquidity.pool_id); - openUrl(`/poolV2/${params_str}`); - } - function getTokenFeeAmount(p: string) { - if (liquidityDetail && tokenMetadata_x_y && tokenPriceList) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const { unclaimed_fee_x, unclaimed_fee_y } = liquidityDetail; - const fee_x_amount = toReadableNumber( - tokenX.decimals, - unclaimed_fee_x || '0' - ); - const fee_y_amount = toReadableNumber( - tokenY.decimals, - unclaimed_fee_y || '0' - ); - if (p == 'l') { - if (new BigNumber(fee_x_amount).isEqualTo('0')) { - return 0; - } else if (new BigNumber(fee_x_amount).isLessThan('0.001')) { - return '<0.001'; - } else { - return toPrecision(fee_x_amount, 3); - } - } else if (p == 'r') { - if (new BigNumber(fee_y_amount).isEqualTo('0')) { - return 0; - } else if (new BigNumber(fee_y_amount).isLessThan('0.001')) { - return '<0.001'; - } else { - return toPrecision(fee_y_amount, 3); - } - } else if (p == 'p') { - const tokenxSinglePrice = tokenPriceList[tokenX.id]?.price || '0'; - const tokenySinglePrice = tokenPriceList[tokenY.id]?.price || '0'; - const priceX = new BigNumber(fee_x_amount).multipliedBy( - tokenxSinglePrice - ); - const priceY = new BigNumber(fee_y_amount).multipliedBy( - tokenySinglePrice - ); - const totalPrice = priceX.plus(priceY); - if (totalPrice.isEqualTo('0')) { - return $0; - } else if (totalPrice.isLessThan('0.01')) { - return '<$0.001'; - } else { - return '$' + toPrecision(totalPrice.toFixed(), 2); - } - } - } - } - function canClaim() { - if (liquidityDetail) { - const { unclaimed_fee_x, unclaimed_fee_y } = liquidityDetail; - if (+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0) return true; - } - return false; - } - function getRateMapTokens() { - if (tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - if (rate_need_to_reverse_display) { - return `${tokenX.symbol}/${tokenY.symbol}`; - } else { - return `${tokenY.symbol}/${tokenX.symbol}`; - } - } - } - function mobile_ReferenceToken(direction: string) { - if (tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - if (direction == 'left') { - if (rate_need_to_reverse_display) { - return tokenY.symbol; - } else { - return tokenX.symbol; - } - } else if (direction == 'right') { - if (rate_need_to_reverse_display) { - return tokenX.symbol; - } else { - return tokenY.symbol; - } - } - } - } - function go_farm() { - const [fixRange, pool_id, left_point, right_point] = - liquidity.mft_id.split('&'); - const link_params = `${get_pool_name( - pool_id - )}[${left_point}-${right_point}]`; - const actives = related_farms.filter((farm: FarmBoost) => { - return farm.status != 'Ended'; - }); - let url; - if (related_farms.length > 0 && actives.length == 0) { - url = `/v2farms/${link_params}-e`; - } else { - url = `/v2farms/${link_params}-r`; - } - openUrl(url); - } - const { - Icon: Liquidity_icon, - your_apr: liquidity_your_apr, - link: liquidity_link, - inRange: liquidity_inRange, - status: liquidity_staked_farm_status, - } = related_seed_info; - return ( - <> - - {styleType == '1' ? ( - - ) : ( - - )} - - - ); -} // a function just to return your liquidity data async function getYourLiquidityData({ liquidity, all_seeds, - styleType, tokenPriceList, poolDetail, liquidities_tokens_metas, @@ -788,7 +405,6 @@ async function getYourLiquidityData({ }: { liquidity: UserLiquidityInfo; all_seeds: Seed[]; - styleType: string; tokenPriceList: Record; poolDetail: PoolInfo; liquidities_tokens_metas: Record; @@ -1015,6 +631,7 @@ async function getYourLiquidityData({ }); const tokenFeeLeft = getTokenFeeAmount('l'); const tokenFeeRight = getTokenFeeAmount('r'); + const tokenFeeValue = getTokenFeeAmount('p'); const rangeMin = getRate(rate_need_to_reverse_display ? 'right' : 'left'); const rangeMax = getRate(rate_need_to_reverse_display ? 'left' : 'right'); const ratedMapTokens = getRateMapTokens(); @@ -1037,595 +654,13 @@ async function getYourLiquidityData({ ratedMapTokens, tokenMetadata_x_y, liquidityDetail, + tokenFeeValue, }; } - -function UserLiquidityLineStyle1() { - const { - hover, - setHover, - getLpt_id, - goYourLiquidityDetailPage, - tokenMetadata_x_y, - fee, - Liquidity_icon, - liquidity_link, - is_in_farming, - liquidity_inRange, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, - liquidity_your_apr, - liquidity_staked_farm_status, - your_liquidity, - setShowAddBox, - setShowRemoveBox, - getTokenFeeAmount, - canClaim, - go_farm, - mobile_ReferenceToken, - claimRewards, - claimLoading, - showRemoveBox, - poolDetail, - tokenPriceList, - liquidityDetail, - showAddBox, - } = useContext(LiquidityContext); - - const history = useHistory(); - - function goDetailV2() { - const url_pool_id = get_pool_name(poolDetail.pool_id); - history.push(`/poolV2/${url_pool_id}`); - } - - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - return ( -
setHover(true)} - onMouseLeave={() => setHover(false)} - > - {/* for PC */} -
-
-
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - -
- - - - {+fee / 10000}% -
- {Liquidity_icon ? ( -
{ - e.stopPropagation(); - if (liquidity_link) { - openUrl(liquidity_link); - } - }} - className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ - liquidity_link ? 'cursor-pointer' : '' - } ${ - is_in_farming || liquidity_inRange ? '' : 'opacity-40' - }`} - > - - - {' '} - -
- ) : null} -
-
- - - - - {getRate(rate_need_to_reverse_display ? 'right' : 'left')} - - - - - - - {getRate(rate_need_to_reverse_display ? 'left' : 'right')} - - - {getRateMapTokens()} - -
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
-
-
- {liquidity_your_apr && - (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( -
- - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )}{' '} - {liquidity_your_apr} - -
{ - openUrl(liquidity_link); - }} - > - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - - -
-
- ) : null} - -
-
- - - - - ${your_liquidity || '-'} - - { - e.stopPropagation(); - setShowAddBox(true); - }} - color="#fff" - minWidth="5rem" - disabled={is_in_farming} - borderRadius="8px" - btnClassName={is_in_farming ? 'cursor-not-allowed' : ''} - className={`px-3 h-8 text-center text-sm text-white gotham_bold focus:outline-none mr-2.5 ${ - is_in_farming ? 'opacity-40 ' : '' - }`} - > - - - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - rounded="rounded-lg" - disabled={is_in_farming} - px="px-0" - py="py-1" - style={{ minWidth: '5rem' }} - className={`flex-grow gotham_bold text-sm text-greenColor h-8 ${ - is_in_farming ? 'opacity-40' : '' - }`} - > - - - {is_in_farming ? ( -
- -
{ - e.stopPropagation(); - go_farm(); - }} - > - - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - - -
-
- ) : null} -
-
- - - - - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - -
- } - /> -
-
-
-
-
-
- {/* for Mobile */} -
-
-
-
- - - NFT ID #{getLpt_id()} - -
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - - {Liquidity_icon ? ( -
{ - e.stopPropagation(); - if (liquidity_link) { - openUrl(liquidity_link); - } - }} - className={`flex items-center justify-center border border-greenColor rounded-lg px-1 ml-2 ${ - is_in_farming || liquidity_inRange ? '' : 'opacity-40' - }`} - > - - - {' '} - -
- ) : null} -
-
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
-
-
- - - - {+fee / 10000}% -
-
- - (1{' '} - {mobile_ReferenceToken('left')}) - - - {getRate(rate_need_to_reverse_display ? 'right' : 'left')}  - {mobile_ReferenceToken('right')} - -
-
- - (1{' '} - {mobile_ReferenceToken('left')}) - - - {getRate(rate_need_to_reverse_display ? 'left' : 'right')}  - {mobile_ReferenceToken('right')} - -
-
- - - -
- - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - -
- } - /> -
-
-
-
-
-
-
- - - - ${your_liquidity || '-'} -
-
- { - e.stopPropagation(); - setShowAddBox(true); - }} - disabled={is_in_farming ? true : false} - color="#fff" - className={`w-1 flex-grow h-8 text-center text-sm text-white focus:outline-none mr-3 ${ - is_in_farming ? 'opacity-40' : '' - }`} - > - - - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - disabled={is_in_farming ? true : false} - rounded="rounded-md" - px="px-0" - py="py-1" - className={`w-1 flex-grow text-sm text-greenColor h-8 ${ - is_in_farming ? 'opacity-40' : '' - }`} - > - - -
- {is_in_farming ? ( -
- - { - e.stopPropagation(); - go_farm(); - }} - > - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - - -
- ) : null} - {liquidity_your_apr && - (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( -
-
-
- -
- -
{ - openUrl(liquidity_link); - }} - > - - - - -
-
-
-
- - {liquidity_your_apr} -
-
-
-
- - - - -
-
-
{ - openUrl(liquidity_link); - }} - > - - - - -
- , - - {liquidity_your_apr} -
-
-
- ) : null} -
-
- {showRemoveBox ? ( - { - setShowRemoveBox(false); - }} - tokenMetadata_x_y={tokenMetadata_x_y} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={liquidityDetail} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > - ) : null} - { - setShowAddBox(false); - }} - tokenMetadata_x_y={tokenMetadata_x_y} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={liquidityDetail} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > -
- ); -} - -export function findRangeIntersection(arr: number[][]) { - if (!Array.isArray(arr) || arr.length === 0) { - return []; - } +export function findRangeIntersection(arr: number[][]) { + if (!Array.isArray(arr) || arr.length === 0) { + return []; + } arr.sort((a, b) => a[0] - b[0]); @@ -1647,17 +682,19 @@ export function findRangeIntersection(arr: number[][]) { return intersection; } - -function UserLiquidityLineStyleGroup1({ +const GroupData = createContext(null); +function UserLiquidityLineStyleGroup({ groupYourLiquidityList, liquidities_list, tokenPriceList, all_seeds, + styleType, }: { groupYourLiquidityList: any[]; liquidities_list: UserLiquidityInfo[]; tokenPriceList: any; all_seeds: Seed[]; + styleType: string; }) { const publicData = groupYourLiquidityList[0]; const { @@ -1884,6 +921,10 @@ function UserLiquidityLineStyleGroup1({ return new Big(prev || '0').plus(new Big(cur.tokenFeeRight || '0')); }, new Big(0)); + const tokenFeeValue = groupYourLiquidityList.reduce((prev, cur) => { + return new Big(prev || '0').plus(new Big(cur.tokenFeeValue || '0')); + }, new Big(0)); + const rangeList = groupYourLiquidityList.map((g) => [ Number(g.rangeMin), Number(g.rangeMax), @@ -1953,6 +994,7 @@ function UserLiquidityLineStyleGroup1({ your_liquidity: formatWithCommas_usd(your_liquidity.toFixed()), tokenFeeLeft: formatNumber(tokenFeeLeft.toFixed()), tokenFeeRight: formatNumber(tokenFeeRight.toFixed()), + tokenFeeValue: formatWithCommas_usd_down(tokenFeeValue.toFixed()), intersectionRangeList: intersectionRangeList.map((range) => [ new Big(range[0]).toFixed(), new Big(range[1]).toFixed(), @@ -1988,6 +1030,7 @@ function UserLiquidityLineStyleGroup1({ your_liquidity, tokenFeeLeft, tokenFeeRight, + tokenFeeValue, intersectionRangeList, isInRange, canClaim, @@ -2068,6 +1111,81 @@ function UserLiquidityLineStyleGroup1({ ) { return b.seed_status_num - a.seed_status_num; } + return ( + + {styleType == '2' ? ( + + ) : ( + + )} + + ); +} +function UserLiquidityLineStyleGroupStyle1() { + const { + hover, + setHover, + tip_seed, + goDetailV2, + tokens, + fee, + farm_icon, + intersectionRangeList, + ratedMapTokens, + isInRange, + accountAPR, + joined_seeds, + sort_joined_seeds, + your_liquidity, + tokenMetadata_x_y, + tokenFeeLeft, + tokenFeeRight, + canClaim, + claimRewards, + claim_loading, + history, + joined_seeds_done, + setRemoveButtonTip, + setShowRemoveBox, + removeButtonTip, + showRemoveBox, + liquidities_list, + poolDetail, + tokenPriceList, + } = useContext(GroupData); return (
- } - /> -
- { - e.stopPropagation(); - history.push('/addLiquidityV2'); - }} - color="#fff" - minWidth="5rem" - borderRadius="8px" - className={`px-3 w-24 h-8 text-center text-sm text-white focus:outline-none mr-2.5`} - > - - -
{ - if (!!joined_seeds) { - setRemoveButtonTip(true); - } - }} - onMouseLeave={() => { - if (!!joined_seeds) { - setRemoveButtonTip(false); - } - }} - > - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - rounded="rounded-lg" - disabled={!!joined_seeds} - px="px-0" - py="py-1" - style={{ minWidth: '5rem' }} - className={`flex-grow w-24 text-sm text-greenColor h-8 ${ - !!joined_seeds ? 'opacity-40 pointer-events-none' : '' - }`} - > - - -
- You have liquidity in farm, please unstake from{' '} - { - localStorage.setItem('BOOST_FARM_TAB', 'yours'); - openUrl('/v2farms'); - }} - > - Your Farm - {' '} - first. -
-
-
-
-
-
-
- {/* for Mobile */} - {/*
-
-
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - -
-
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
-
-
- - - - {+fee / 10000}% -
-
- - (1{' '} - {mobile_ReferenceToken('left')}) - - - {getRate(rate_need_to_reverse_display ? 'right' : 'left')}  - {mobile_ReferenceToken('right')} - -
-
- - (1{' '} - {mobile_ReferenceToken('left')}) - - - {getRate(rate_need_to_reverse_display ? 'left' : 'right')}  - {mobile_ReferenceToken('right')} - -
-
- - - -
- - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - -
- } - /> -
-
-
-
-
-
-
- - - - ${your_liquidity || '-'} -
-
- { - e.stopPropagation(); - setShowAddBox(true); - }} - disabled={is_in_farming ? true : false} - color="#fff" - className={`w-1 flex-grow h-8 text-center text-sm text-white focus:outline-none mr-3 ${ - is_in_farming ? 'opacity-40' : '' - }`} - > - - - { - e.stopPropagation(); - setShowRemoveBox(true); - }} - disabled={is_in_farming ? true : false} - rounded="rounded-md" - px="px-0" - py="py-1" - className={`w-1 flex-grow text-sm text-greenColor h-8 ${ - is_in_farming ? 'opacity-40' : '' - }`} - > - - -
- {is_in_farming ? ( -
- - { - e.stopPropagation(); - go_farm(); - }} - > - {liquidity_staked_farm_status == 'end' ? ( - - ) : ( - - )} - - -
- ) : null} - {liquidity_your_apr && - (!is_in_farming || liquidity_staked_farm_status == 'end') ? ( -
-
-
- -
- -
{ - openUrl(liquidity_link); - }} - > - - - - -
-
-
-
- - {liquidity_your_apr} -
-
-
-
- - - - + className={`flex mr-2.5 w-24 h-8 items-center justify-center rounded-lg text-sm px-3 py-1 ml-5 ${ + !canClaim + ? 'bg-deepBlue text-white opacity-30 cursor-not-allowed' + : 'bg-deepBlue text-white hover:bg-lightBlue cursor-pointer' + }`} + onClick={claimRewards} + > + } + />
-
-
{ - openUrl(liquidity_link); + { + e.stopPropagation(); + history.push('/addLiquidityV2'); + }} + color="#fff" + minWidth="5rem" + borderRadius="8px" + className={`px-3 w-24 h-8 text-center text-sm text-white focus:outline-none mr-2.5`} + > + + +
{ + if (!!joined_seeds) { + setRemoveButtonTip(true); + } + }} + onMouseLeave={() => { + if (!!joined_seeds) { + setRemoveButtonTip(false); + } + }} + > + { + e.stopPropagation(); + setShowRemoveBox(true); }} + rounded="rounded-lg" + disabled={!!joined_seeds} + px="px-0" + py="py-1" + style={{ minWidth: '5rem' }} + className={`flex-grow w-24 text-sm text-greenColor h-8 ${ + !!joined_seeds ? 'opacity-40 pointer-events-none' : '' + }`} > - - - - + + +
+ You have liquidity in farm, please unstake from{' '} + { + localStorage.setItem('BOOST_FARM_TAB', 'yours'); + openUrl('/v2farms'); + }} + > + Your Farm + {' '} + first.
- , - - {liquidity_your_apr}
- ) : null} +
-
*/} +
{showRemoveBox ? ( ); } - -function UserLiquidityLineStyle2() { +function UserLiquidityLineStyleGroupStyle2() { const { - getLpt_id, - goPoolDetailPage, - tokenMetadata_x_y, + tip_seed, + tokens, fee, - liquidity_link, - is_in_farming, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, + intersectionRangeList, + ratedMapTokens, + isInRange, + accountAPR, + joined_seeds, + sort_joined_seeds, your_liquidity, - getTokenFeeAmount, - go_farm, - } = useContext(LiquidityContext); + tokenMetadata_x_y, + tokenFeeLeft, + tokenFeeRight, + poolDetail, + tokenFeeValue, + } = useContext(GroupData); const [switch_off, set_switch_off] = useState(true); - - function getUsageDiv() { - let div; - if (is_in_farming) { - div = ( -
- {' '} - { - e.stopPropagation(); - go_farm(); - }} - className="text-primaryText hover:text-white cursor-pointer ml-1.5" - > -
- ); - } else if (liquidity_link) { - div = ( -
- - { - e.stopPropagation(); - openUrl(liquidity_link); - }} - className="text-primaryText hover:text-white cursor-pointer ml-1.5" - > -
- ); - } else { - div = ( - - - - ); - } - return div; + function goPoolDetailPage() { + const params_str = get_pool_name(poolDetail.pool_id); + openUrl(`/poolV2/${params_str}`); } return ( <> - {is_mobile ? ( - - ) : ( - - )} - - ); -} -function UserLiquidityLineStyle2Mobile({ - switch_off, - set_switch_off, - getUsageDiv, -}: { - switch_off: boolean; - set_switch_off: any; - getUsageDiv: any; -}) { - const { - getLpt_id, - goYourLiquidityDetailPage, - goPoolDetailPage, - tokenMetadata_x_y, - fee, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, - your_liquidity, - getTokenFeeAmount, - } = useContext(LiquidityContext); - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - return ( -
-
-
+
+
-
+
- + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} -
- - ${your_liquidity || '-'} - -
-
-
- { - goYourLiquidityDetailPage('new window'); - }} - className="flex items-center bg-portfolioRainbowColor rounded-md gotham_bold text-xs text-white cursor-pointer px-1.5 py-0.5" - > - NFT #{getLpt_id()} - - { - goPoolDetailPage(); - }} - className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md cursor-pointer whitespace-nowrap py-0.5 px-1.5 ml-1.5" - > - DCL - -
-
- - - {getTokenFeeAmount('p')} - - { - set_switch_off(!switch_off); - }} - switch_off={switch_off} - > -
-
-
- -
-
- - - - - ${your_liquidity || '-'} - -
-
-
- - - -
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
- - {getRate(rate_need_to_reverse_display ? 'right' : 'left')} -{' '} - {getRate(rate_need_to_reverse_display ? 'left' : 'right')} - - - {getRateMapTokens()} - -
-
-
- - - - {getUsageDiv()} -
-
- - - -
- - - {getTokenFeeAmount('l') || '-'} + + {+fee / 10000}% - - - {getTokenFeeAmount('r') || '-'} + { + goPoolDetailPage(); + }} + className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md px-1.5 cursor-pointer hover:text-white py-0.5 mr-1.5" + > + {' '} +
+
+
+ + ${your_liquidity || '-'} + +
+ + + {tokenFeeValue} + +
+
+ { + set_switch_off(!switch_off); + }} + switch_off={switch_off} + > +
+
+
+
+
+ + + +
+
+ + + {isInRange ? ( + + ) : ( + + )} + +
+
+ {intersectionRangeList.map((range: string[], i: number) => { + return ( +
+ + {displayNumberToAppropriateDecimals(range[0])} + + - + + + {displayNumberToAppropriateDecimals(range[1])} + + {intersectionRangeList.length > 1 && + i < intersectionRangeList.length - 1 && ( + , + )} +
+ ); + })} + + {ratedMapTokens} + +
+
+
+
+ APR(24h) +
+ {accountAPR || '-'} + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { seed_apr, seed_status } = joined_seed_info; + if (seed_status == 'ended') return null; + if (length == 1) { + return ( +
+ + + + {seed_apr} +
+ ); + } else { + return ( +
+ + ({seed_status == 'run' ? 'new' : 'pre.'}) APR + + {seed_apr} +
+ ); + } + })} +
+ ) : tip_seed ? ( +
+ + + + 0% +
+ ) : null} +
+
+ {joined_seeds || tip_seed ? ( +
+ + + + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { + seed_status, + value_of_investment, + go_farm_url_link, + } = joined_seed_info; + if (length == 1) { + return ( + + ); + } else { + return ( + + ); + } + })} +
+ ) : tip_seed ? ( + + ) : null} +
+ ) : null} + +
+ + + +
+ + + {tokenFeeLeft || '-'} + + + + {tokenFeeRight || '-'} + + + {tokenFeeValue} + +
+
+
-
+ ); } + function UserLiquidityLineStyle2Pc({ switch_off, set_switch_off, @@ -3076,6 +2009,173 @@ function UserLiquidityLineStyle2Pc({
); } +function UserLiquidityLineStyle2Mobile({ + switch_off, + set_switch_off, + getUsageDiv, +}: { + switch_off: boolean; + set_switch_off: any; + getUsageDiv: any; +}) { + const { + getLpt_id, + goYourLiquidityDetailPage, + goPoolDetailPage, + tokenMetadata_x_y, + fee, + getRate, + rate_need_to_reverse_display, + getRateMapTokens, + isInrange, + your_liquidity, + getTokenFeeAmount, + } = useContext(LiquidityContext); + const tokens = sort_tokens_by_base(tokenMetadata_x_y); + return ( +
+
+
+
+
+ + +
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + +
+ + ${your_liquidity || '-'} + +
+
+
+ { + goYourLiquidityDetailPage('new window'); + }} + className="flex items-center bg-portfolioRainbowColor rounded-md gotham_bold text-xs text-white cursor-pointer px-1.5 py-0.5" + > + NFT #{getLpt_id()} + + { + goPoolDetailPage(); + }} + className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md cursor-pointer whitespace-nowrap py-0.5 px-1.5 ml-1.5" + > + DCL + +
+
+ + + {getTokenFeeAmount('p')} + + { + set_switch_off(!switch_off); + }} + switch_off={switch_off} + > +
+
+
+ +
+
+ + + + + ${your_liquidity || '-'} + +
+
+
+ + + +
+ + + {isInrange ? ( + + ) : ( + + )} + +
+
+
+ + {getRate(rate_need_to_reverse_display ? 'right' : 'left')} -{' '} + {getRate(rate_need_to_reverse_display ? 'left' : 'right')} + + + {getRateMapTokens()} + +
+
+
+ + + + {getUsageDiv()} +
+
+ + + +
+ + + {getTokenFeeAmount('l') || '-'} + + + + {getTokenFeeAmount('r') || '-'} + +
+
+
+
+ ); +} + interface IUserJoinedSeed { seed_id?: IUserJoinedSeedDetail; } diff --git a/src/components/pool/utils.ts b/src/components/pool/utils.ts index 63621b50d..3118c8832 100644 --- a/src/components/pool/utils.ts +++ b/src/components/pool/utils.ts @@ -16,6 +16,19 @@ export const formatWithCommas_usd = (v: string | number) => { return '$' + formatWithCommas(big.toFixed(0, 1)); } }; +export const formatWithCommas_usd_down = (v: string | number) => { + if (isInvalid(v)) return '$-'; + const big = Big(v); + if (big.eq(0)) { + return '$0'; + } else if (big.lt(0.01)) { + return '<$0.01'; + } else if (big.lt(10000)) { + return '$' + formatWithCommas(big.toFixed(2, 0)); + } else { + return '$' + formatWithCommas(big.toFixed(0, 0)); + } +}; export const formatPercentage = (v: string | number) => { if (isInvalid(v)) return '-%'; diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index c931541ec..fca375384 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -78,6 +78,7 @@ import { HiOutlineExternalLink } from 'react-icons/hi'; import getConfig from '../../../../services/config'; import _ from 'lodash'; import { HistoryOrderSwapInfo } from '../../../../services/indexer'; +import { useDclPoolIdByUrl } from '../../../../state/swapV3'; const ORDER_TYPE_KEY = 'REF_FI_ORDER_TYPE_VALUE'; @@ -2200,11 +2201,40 @@ function OrderCard({ const [historySortBy, setHistorySortBy] = useState<'claimed' | 'created'>( 'created' ); + const [select_type, set_select_type] = useState<'all' | 'current'>('all'); + const [activeOrderList, setActiveOrderList] = useState(); + const [historyOrderList, setHistoryOrderList] = useState(); + const pool_id_by_url = useDclPoolIdByUrl(); + console.log('pool_id_by_url', pool_id_by_url); + useEffect(() => { + if (activeOrder.length) { + if (select_type == 'all') { + setActiveOrderList(activeOrder); + } else { + setActiveOrderList(getCurrentPairOrders(activeOrder)); + } + } + }, [activeOrder, select_type]); + + useEffect(() => { + if (historyOrder.length) { + if (select_type == 'all') { + setHistoryOrderList(historyOrder); + } else { + setHistoryOrderList(getCurrentPairOrders(historyOrder)); + } + } + }, [historyOrder, select_type]); + function getCurrentPairOrders(orders: UserOrderInfo[]) { + return orders.filter((order: UserOrderInfo) => { + return order.pool_id == pool_id_by_url; + }); + } function OrderTab() { if (isMobile()) { return ( -
+
- {activeOrder && activeOrder.length > 0 - ? ` (${activeOrder.length})` + {activeOrderList && activeOrderList.length > 0 + ? ` (${activeOrderList.length})` : null} @@ -2256,8 +2286,8 @@ function OrderCard({ > - {historyOrder && historyOrder.length > 0 - ? ` (${historyOrder.length})` + {historyOrderList && historyOrderList.length > 0 + ? ` (${historyOrderList.length})` : null} @@ -2284,8 +2314,8 @@ function OrderCard({ id="active_orders" defaultMessage={'Active Orders'} /> - {activeOrder && activeOrder.length > 0 - ? ` (${activeOrder.length})` + {activeOrderList && activeOrderList.length > 0 + ? ` (${activeOrderList.length})` : null} @@ -2313,8 +2343,8 @@ function OrderCard({ > - {historyOrder && historyOrder.length > 0 - ? ` (${historyOrder.length})` + {historyOrderList && historyOrderList.length > 0 + ? ` (${historyOrderList.length})` : null} @@ -2406,7 +2436,27 @@ function OrderCard({ return (
- {OrderTab()} +
+ {OrderTab()} +
+ { + set_select_type('current'); + }} + > + Current: NEAR/USDC + + { + set_select_type('all'); + }} + > + All + +
+
{ + activeOrderList && + activeOrderList.sort(activeOrderSorting).map((order, index) => { return ( { + historyOrderList && + historyOrderList.sort(historyOrderSorting).map((order, index) => { return ( + + + + {props.children} + + ); +} + function OrderCardOld({ activeOrder, tokensMap, @@ -3764,7 +3833,6 @@ function MyOrderComponent() { // const result: string = `
${n}
`; // return result; // } - return (
From 408c9408a94cd0e83e9da707dc505f5ed3601926 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 9 Aug 2023 14:34:55 +0800 Subject: [PATCH 107/204] handle call back of transition --- src/components/pool/AddNewPoolV3.tsx | 1 - src/components/pool/YourLiquidityV2.tsx | 2 + src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 10 +-- .../poolsV3/AddYourLiquidityPageV3Copy.tsx | 5 -- src/pages/poolsV3/PoolDetailV3.tsx | 4 +- src/pages/poolsV3/YourLiquidityDetailV3.tsx | 6 -- src/pages/poolsV3/YourLiquidityPageV3.tsx | 3 - src/services/commonV3.ts | 74 ++++++++++--------- 8 files changed, 47 insertions(+), 58 deletions(-) diff --git a/src/components/pool/AddNewPoolV3.tsx b/src/components/pool/AddNewPoolV3.tsx index 03a60637c..fe69c2b42 100644 --- a/src/components/pool/AddNewPoolV3.tsx +++ b/src/components/pool/AddNewPoolV3.tsx @@ -38,7 +38,6 @@ import { DEFAULTSELECTEDFEE, POINTLEFTRANGE, POINTRIGHTRANGE, - useAddAndRemoveUrlHandle, drawChartData, TOKEN_LIST_FOR_RATE, displayNumberToAppropriateDecimals, diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index a8f889a65..cc4148688 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -46,6 +46,7 @@ import { get_valid_range, get_total_value_by_liquidity_amount_dcl, get_token_amount_in_user_liquidities, + useRemoveLiquidityUrlHandle, } from '../../services/commonV3'; import BigNumber from 'bignumber.js'; import { @@ -112,6 +113,7 @@ export function YourLiquidityV2(props: any) { const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; + useRemoveLiquidityUrlHandle(); useEffect(() => { getBoostTokenPrices().then(setTokenPriceList); get_all_seeds().then((seeds: Seed[]) => { diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index e0b583b0d..3dad2d09e 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -50,7 +50,7 @@ import { DEFAULTSELECTEDFEE, POINTLEFTRANGE, POINTRIGHTRANGE, - useAddAndRemoveUrlHandle, + useAddLiquidityUrlHandle, get_pool_id, get_pool_name, openUrl, @@ -145,7 +145,7 @@ export default function AddYourLiquidityPageV3() { const [pair_is_reverse, set_pair_is_reverse] = useState(false); // callBack handle - useAddAndRemoveUrlHandle(); + useAddLiquidityUrlHandle(); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; const nearBalance = useDepositableBalance('NEAR'); @@ -222,7 +222,6 @@ export default function AddYourLiquidityPageV3() { // get base info of currentSelectedPool useEffect(() => { if (currentSelectedPool?.pool_id) { - console.log('000000000---pool_id', currentSelectedPool.pool_id); const { current_point, point_delta } = currentSelectedPool; const n = get_slot_number_in_a_bin(); const bin_width = n * point_delta; @@ -2746,11 +2745,6 @@ function AddLiquidityButton() { useState(false); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; - const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; function addLiquiditySpot() { setAddLiquidityButtonLoading(true); diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx index f99f4c764..72337fe51 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx @@ -68,7 +68,6 @@ import { DEFAULTSELECTEDFEE, POINTLEFTRANGE, POINTRIGHTRANGE, - useAddAndRemoveUrlHandle, get_matched_seeds_for_dcl_pool, get_all_seeds, get_pool_id, @@ -182,8 +181,6 @@ function AddYourLiquidityPageV3Forward() { UserLiquidityInfo[] >([]); - // callBack handle - useAddAndRemoveUrlHandle(); const history = useHistory(); const triTokenIds = useTriTokenIdsOnRef(); const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); @@ -2341,8 +2338,6 @@ function AddYourLiquidityPageV3Reverse() { UserLiquidityInfo[] >([]); - // callBack handle - useAddAndRemoveUrlHandle(); const history = useHistory(); const triTokenIds = useTriTokenIdsOnRef(); const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 3bf7e4915..0a299e622 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -34,7 +34,6 @@ import { CONSTANT_D, getXAmount_per_point_by_Lx, getYAmount_per_point_by_Ly, - useAddAndRemoveUrlHandle, TOKEN_LIST_FOR_RATE, allocation_rule_liquidities, get_matched_seeds_for_dcl_pool, @@ -47,6 +46,7 @@ import { openUrl, get_account_24_apr, get_token_amount_in_user_liquidities, + useRemoveLiquidityUrlHandle, } from '~services/commonV3'; import { ftGetTokensMetadata } from '../../services/ft-contract'; import { @@ -161,7 +161,7 @@ export default function PoolDetailV3() { const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; // callBack handle - useAddAndRemoveUrlHandle(); + useRemoveLiquidityUrlHandle(); useEffect(() => { get_pool_detail(); get_user_list_liquidities(); diff --git a/src/pages/poolsV3/YourLiquidityDetailV3.tsx b/src/pages/poolsV3/YourLiquidityDetailV3.tsx index 1820d16a8..2aacddc5e 100644 --- a/src/pages/poolsV3/YourLiquidityDetailV3.tsx +++ b/src/pages/poolsV3/YourLiquidityDetailV3.tsx @@ -39,7 +39,6 @@ import { getPriceByPoint, CONSTANT_D, UserLiquidityInfo, - useAddAndRemoveUrlHandle, getXAmount_per_point_by_Lx, getYAmount_per_point_by_Ly, drawChartData, @@ -87,17 +86,12 @@ export default function YourLiquidityDetail(props: any) { const [listLiquidities, setListLiquidities] = useState( [] ); - - console.log('listLiquidities: ', listLiquidities); - const [listLiquiditiesDone, setListLiquiditiesDone] = useState(false); const [is_in_farming, set_is_in_farming] = useState(true); const [is_in_farming_done, set_is_in_farming_done] = useState(false); const [related_farms, set_related_farms] = useState([]); const history = useHistory(); - // callBack handle - useAddAndRemoveUrlHandle(); const { id, status } = props.match.params || {}; let is_old_dcl: boolean; let tokenXId, tokenYId, feeV, lId; diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index 5257fd946..2a3fe0221 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -33,7 +33,6 @@ import { getPriceByPoint, CONSTANT_D, UserLiquidityInfo, - useAddAndRemoveUrlHandle, getXAmount_per_point_by_Lx, getYAmount_per_point_by_Ly, TOKEN_LIST_FOR_RATE, @@ -137,8 +136,6 @@ export default function YourLiquidityPageV3() { const [checkedStatus, setCheckedStatus] = useState('all'); const [addLiqudityHover, setAddLiqudityHover] = useState(false); const [all_seeds, set_all_seeds] = useState([]); - // callBack handle - useAddAndRemoveUrlHandle(); const history = useHistory(); const pool_link = sessionStorage.getItem(REF_POOL_NAV_TAB_KEY); diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 94f0d6963..8130534bb 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -138,7 +138,7 @@ export interface IOrderInfoPool { amount_y: string; } -export function useAddAndRemoveUrlHandle() { +export function useAddLiquidityUrlHandle() { const history = useHistory(); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; @@ -157,48 +157,56 @@ export function useAddAndRemoveUrlHandle() { ?.method_name; const methodNameNormal = transaction?.actions[0]?.['FunctionCall']?.method_name; - const argsNormal = transaction?.actions[0]?.['FunctionCall']?.args; - const argsNeth = - receipts?.[0]?.receipt?.Action?.actions?.[0]?.FunctionCall?.args; - const args = isNeth ? argsNeth : argsNormal; const methodName = isNeth ? methodNameNeth : methodNameNormal; const successValue = isNeth ? successValueNeth : successValueNormal; let returnValue; - let argsValue; if (successValue) { const buff = Buffer.from(successValue, 'base64'); const v = buff.toString('ascii'); returnValue = v.substring(1, v.length - 1); } - if (args) { - const buff = Buffer.from(args, 'base64'); - const v = buff.toString('ascii'); - argsValue = v; + let pool_info: string[] = []; + if (methodName == 'add_liquidity') { + pool_info = returnValue.split('|'); + } else if (methodName == 'batch_add_liquidity') { + pool_info = returnValue.replace(/\"/g, '').split(',')[0]?.split('|'); } - if (methodName == 'append_liquidity' && argsValue) { - const parmas = JSON.parse(argsValue); - const { lpt_id } = parmas; - const [tokenX, tokenY, id] = lpt_id.split('|'); - const [fee, hashId] = id.split('#'); - const pool_name = get_pool_name(`${tokenX}|${tokenY}|${fee}`); - const paramsId = `${pool_name}@${hashId}`; - history.replace('/yoursLiquidityDetailV2/' + `${paramsId}`); - } else if (methodName == 'add_liquidity' && returnValue) { - const [tokenX, tokenY, id] = returnValue.split('|'); - const [fee, hashId] = id.split('#'); + if (pool_info.length) { + const [tokenX, tokenY, id] = pool_info; + const [fee] = id.split('#'); const pool_name = get_pool_name(`${tokenX}|${tokenY}|${fee}`); - const paramsId = `${pool_name}@${hashId}`; - history.replace('/yoursLiquidityDetailV2/' + `${paramsId}`); - } else if (methodName == 'remove_liquidity' && argsValue) { - const parmas = JSON.parse(argsValue); - const { amount, min_amount_x, min_amount_y } = parmas; - if (+amount == 0 && +min_amount_x == 0 && +min_amount_y == 0) { - history.replace(`${location.pathname}`); - } else { - history.replace('/yourliquidity'); - } - } else if (methodName == 'create_pool' && returnValue) { - history.replace(`${location.pathname}#${returnValue}`); + history.replace('/poolV2/' + `${pool_name}`); + } + }); + } + }, [txHash, isSignedIn]); +} +export function useRemoveLiquidityUrlHandle() { + const history = useHistory(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + const { txHash } = getURLInfo(); + useEffect(() => { + if (txHash && isSignedIn) { + checkTransaction(txHash).then((res: any) => { + const { transaction, receipts, receipts_outcome } = res; + receipts_outcome?.[1]?.outcome?.status?.SuccessValue; + const isNeth = + transaction?.actions?.[0]?.FunctionCall?.method_name === 'execute'; + const methodNameNeth = + receipts?.[0]?.receipt?.Action?.actions?.[0]?.FunctionCall + ?.method_name; + const methodNameNormal = + transaction?.actions[0]?.['FunctionCall']?.method_name; + const methodName = isNeth ? methodNameNeth : methodNameNormal; + if ( + methodName == 'batch_remove_liquidity' || + methodName == 'batch_update_liquidity' || + methodName == 'withdraw_asset' + ) { + history.replace('/yourliquidity'); + } else { + history.replace(`${location.pathname}`); } }); } From 4c1721243d47ef7a59dae07297aaeb63c168abf2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 9 Aug 2023 17:11:11 +0800 Subject: [PATCH 108/204] update top bin apr logic --- src/components/d3Chart/DclChart.tsx | 8 +-- src/components/d3Chart/DclChartCopy.tsx | 16 ++--- src/components/pool/YourLiquidityV2.tsx | 8 +-- src/pages/pools/LiquidityPage.tsx | 6 +- src/pages/poolsV3/PoolDetailV3.tsx | 71 ++++++++++++---------- src/services/indexer.ts | 19 +++--- src/state/pool.ts | 79 ++++++++++++++++++------- src/state/swapV3.ts | 19 ------ 8 files changed, 125 insertions(+), 101 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index b0fe5495c..9135b8413 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1535,7 +1535,7 @@ export default function DclChart({ {/* show hover box then hover on the bin */}
- Trailing 24hr APR + APR(24h) {binDetail?.feeApr} @@ -1585,7 +1585,7 @@ export default function DclChart({ backgroundColor: `${binDetail?.colors[1]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_x_amount_in_order} @@ -1631,7 +1631,7 @@ export default function DclChart({ backgroundColor: `${binDetail?.colors[0]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_y_amount_in_order} @@ -1673,7 +1673,7 @@ export default function DclChart({
- Trailing 24hr APR + APR(24h) {user_liquidities_detail?.apr_24 || '-'} diff --git a/src/components/d3Chart/DclChartCopy.tsx b/src/components/d3Chart/DclChartCopy.tsx index 1824d6428..d73e7fca3 100644 --- a/src/components/d3Chart/DclChartCopy.tsx +++ b/src/components/d3Chart/DclChartCopy.tsx @@ -1465,7 +1465,7 @@ function DclChartForward({ {/* hover到柱子(bin)上的悬浮框 */}
- Trailing 24hr APR + APR(24h) {binDetail?.feeApr} @@ -1515,7 +1515,7 @@ function DclChartForward({ backgroundColor: `${binDetail?.colors[1]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_x_amount_in_order} @@ -1561,7 +1561,7 @@ function DclChartForward({ backgroundColor: `${binDetail?.colors[0]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_y_amount_in_order} @@ -1594,7 +1594,7 @@ function DclChartForward({
- Trailing 24hr APR + APR(24h) {user_liquidities_detail?.apr_24 || '-'} @@ -3033,7 +3033,7 @@ function DclChartReverse({ {/* hover到柱子(bin)上的悬浮框 */}
- Trailing 24hr APR + APR(24h) {binDetail?.feeApr} @@ -3083,7 +3083,7 @@ function DclChartReverse({ backgroundColor: `${binDetail?.colors[1]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_x_amount_in_order} @@ -3129,7 +3129,7 @@ function DclChartReverse({ backgroundColor: `${binDetail?.colors[0]}`, }} > - by Limit Orders + in Limit Orders
{binDetail.token_y_amount_in_order} @@ -3162,7 +3162,7 @@ function DclChartReverse({
- Trailing 24hr APR + APR(24h) {user_liquidities_detail?.apr_24 || '-'} diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index cc4148688..3baaf8e59 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -173,7 +173,6 @@ export function YourLiquidityV2(props: any) { }) ) ).then((yourLiquidityList) => { - debugger; const groupedLiquidity = yourLiquidityList.reduce((acc, cur) => { const { pool_id } = cur; const [token_x, token_y] = pool_id.split('|'); @@ -367,7 +366,7 @@ export function YourLiquidityV2(props: any) { >
-
Trailing 24hr APR
+
APR(24h)
- {/* Trailing 24hr APR */} + {/* APR(24h) */}
{accountAPR || '-'} {joined_seeds ? ( @@ -1473,7 +1472,8 @@ function UserLiquidityLineStyleGroupStyle1() { { e.stopPropagation(); - history.push('/addLiquidityV2'); + const pool_name = get_pool_name(poolDetail.pool_id); + history.push(`/addLiquidityV2#${pool_name}`); }} color="#fff" minWidth="5rem" diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 70f4f82da..b2ea57d55 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -1742,9 +1742,7 @@ function PoolRowV2({ const history = useHistory(); const displayOfTopBinApr = useDCLTopBinFee({ - pool_id: pool.pool_id, - number: 100, - ownValue: pool.top_bin_apr_display, + pool }); if (!curRowTokens) return <>; @@ -3002,7 +3000,7 @@ function LiquidityPage_({
{allPoolsV2 - .sort(poolv2ReSortingFunc) + // .sort(poolv2ReSortingFunc) .filter(poolv2FilterFunc) .map((pool, i) => ( ; @@ -337,7 +336,7 @@ export default function PoolDetailV3() { :{' '} @@ -643,10 +642,10 @@ function YourLiquidityBox(props: { } }, [liquidities, Object.keys(tokenPriceList).length]); useEffect(() => { - if (poolDetail && tokenPriceList && Object.keys(tokenPriceList).length) { + if (liquidities && poolDetail && tokenPriceList && Object.keys(tokenPriceList).length) { get_24_apr_and_fee(); } - }, [poolDetail, tokenPriceList]); + }, [poolDetail, tokenPriceList, liquidities]); useEffect(() => { if (liquidities) { const target = liquidities.find((l: UserLiquidityInfo) => { @@ -670,19 +669,24 @@ function YourLiquidityBox(props: { account_id: accountId, }); if (dcl_fee_result) { - // 24h 利润 + // 24h profit apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + // total unClaimed fee + const [unClaimed_tvl_fee, unClaimed_amount_x_fee, unClaimed_amount_y_fee] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); // total earned fee const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; - // 总共赚到的fee total_earned_fee_x = toReadableNumber( token_x_metadata.decimals, Big(total_fee_x || 0).toFixed() ); + total_earned_fee_x = Big(total_earned_fee_x).plus(unClaimed_amount_x_fee).toFixed(); + total_earned_fee_y = toReadableNumber( token_y_metadata.decimals, Big(total_fee_y || 0).toFixed() ); + total_earned_fee_y = Big(total_earned_fee_y).plus(unClaimed_amount_y_fee).toFixed(); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); @@ -690,6 +694,7 @@ function YourLiquidityBox(props: { total_fee_earned = total_earned_fee_x_value .plus(total_earned_fee_y_value) .toFixed(); + total_fee_earned = Big(total_fee_earned).plus(unClaimed_tvl_fee).toFixed(); } set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); @@ -1001,7 +1006,7 @@ function YourLiquidityBox(props: {
- Trailing 24hr APR + APR(24h)
{accountAPR || '-'} @@ -1142,28 +1147,7 @@ function UnclaimedFeesBox(props: any) { const [cliam_loading, set_cliam_loading] = useState(false); useEffect(() => { if (liquidities) { - let total_amount_x_fee = 0; - let total_amount_y_fee = 0; - let total_tvl_fee = 0; - liquidities.forEach((liquidity: UserLiquidityInfo) => { - const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; - const unclaimed_fee_x_amount = toReadableNumber( - token_x_metadata.decimals, - unclaimed_fee_x - ); - const unclaimed_fee_y_amount = toReadableNumber( - token_y_metadata.decimals, - unclaimed_fee_y - ); - const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; - const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_fees_price = - Number(unclaimed_fee_x_amount) * Number(token_x_price) + - Number(unclaimed_fee_y_amount) * Number(token_y_price); - total_amount_x_fee += Number(unclaimed_fee_x_amount); - total_amount_y_fee += Number(unclaimed_fee_y_amount); - total_tvl_fee += Number(total_fees_price); - }); + const [total_tvl_fee, total_amount_x_fee, total_amount_y_fee] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList) set_user_liquidities_total({ total_amount_x_fee, total_amount_y_fee, @@ -1276,6 +1260,33 @@ function UnclaimedFeesBox(props: any) {
); } +function get_unClaimed_fee_data(liquidities:UserLiquidityInfo[], poolDetail:PoolInfo, tokenPriceList:any) { + // UnClaimed fee + let total_amount_x_fee = 0; + let total_amount_y_fee = 0; + let total_tvl_fee = 0; + const { token_x_metadata, token_y_metadata } = poolDetail + liquidities.forEach((liquidity: UserLiquidityInfo) => { + const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; + const unclaimed_fee_x_amount = toReadableNumber( + token_x_metadata.decimals, + unclaimed_fee_x + ); + const unclaimed_fee_y_amount = toReadableNumber( + token_y_metadata.decimals, + unclaimed_fee_y + ); + const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; + const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_fees_price = + Number(unclaimed_fee_x_amount) * Number(token_x_price) + + Number(unclaimed_fee_y_amount) * Number(token_y_price); + total_amount_x_fee += Number(unclaimed_fee_x_amount); + total_amount_y_fee += Number(unclaimed_fee_y_amount); + total_tvl_fee += Number(total_fees_price); + }); + return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; +} function RelatedFarmsBox(props: any) { const { poolDetail, tokenPriceList, sole_seed } = props; const [related_seed, set_related_seed] = useState(); diff --git a/src/services/indexer.ts b/src/services/indexer.ts index 8b53bca69..f7c10c211 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -481,19 +481,14 @@ export interface DCLPoolFee { } export const getDCLTopBinFee = async (props: { - pool_id: string | number; - slot_number: string | number; + pool_id: string, + bin: number, + start_point: number, + end_point: number }): Promise => { - const paramString = genUrlParams(props); - - return await fetch(config.indexerUrl + `/get-dcl-points?${paramString}`, { - method: 'GET', - headers: { 'Content-type': 'application/json; charset=UTF-8' }, - }) - .then((res) => res.json()) - .then((res) => { - return res.top_bin_fee_data; - }); + const { pool_id, bin, start_point, end_point } = props; + const result = await getDclPoolPoints(pool_id, bin, start_point, end_point); + return result?.top_bin_fee_data || {}; }; export const getDCLAccountFee = async (props: { diff --git a/src/state/pool.ts b/src/state/pool.ts index 574c1907c..9237dabae 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -98,7 +98,13 @@ import { PoolInfo, get_pool } from '../services/swapV3'; import { useTokenPriceList } from './token'; import { isStablePool } from '../services/near'; import { getStablePoolDecimal } from '../pages/stable/StableSwapEntry'; -import { useWalletSelector } from '~context/WalletSelectorContext'; +import { get_default_config_for_chart, get_custom_config_for_chart } from '../components/d3Chart/config'; +import { IChartItemConfig, IChartConfig } from '../components/d3Chart/interfaces'; +import { getPointByPrice, getPriceByPoint } from '../services/commonV3'; +import { + formatPercentage +} from '../components/d3Chart/utils'; + const REF_FI_STABLE_POOL_INFO_KEY = `REF_FI_STABLE_Pool_INFO_VALUE_${ getConfig().STABLE_POOL_ID }`; @@ -1421,35 +1427,68 @@ export const useDCLPoolTransaction = ({ }; export const useDCLTopBinFee = ({ - pool_id, - number, - ownValue, + pool, }: { - pool_id: string | number; - number: string | number; - ownValue?: string; + pool:PoolInfo, }) => { const [topBinApr, setTopBinApr] = useState('-'); - useEffect(() => { - if (!pool_id) return; - if (ownValue) { - setTopBinApr(ownValue); - return; - } - + if (!pool) return; + const [bin, start_point, end_point ] = get_config_of_dcl_pool(pool); getDCLTopBinFee({ - pool_id, - slot_number: number, + pool_id: pool.pool_id, + bin, + start_point, + end_point }).then((res) => { if (!res || ONLY_ZEROS.test(res.total_liquidity)) return; const apr = - new Big(res.total_fee).div(res.total_liquidity).mul(365).toFixed(2) + - '%'; - + formatPercentage(new Big(res.total_fee).div(res.total_liquidity).mul(365).mul(100).toFixed()) setTopBinApr(apr); }); - }, [pool_id, number, ownValue]); + }, [pool]); return topBinApr; }; + +function get_config_of_dcl_pool(pool:PoolInfo) { + const pool_id = pool.pool_id; + const { bin, rangeGear } = get_default_config_for_chart() as IChartItemConfig; + const custom_config: IChartConfig = get_custom_config_for_chart(); + const bin_final = custom_config[pool_id]?.bin || bin; + const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; + const [price_l, price_r] = get_price_range_by_percent(rangeGear_final[0], pool); + const point_l = get_point_by_price(price_l, pool); + const point_r = get_point_by_price(price_r, pool); + return [bin_final, point_l, point_r] +} + +function get_price_range_by_percent( + percent: number, + pool:PoolInfo, +): [string, string] { + const { current_point } = pool; + const p_l_r = percent / 100; + const price = get_price_by_point(current_point, pool); + const price_l_temp = Big(1 - p_l_r).mul(price); + const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); + const price_r = Big(1 + p_l_r) + .mul(price) + .toFixed(); + + return [price_l, price_r]; +} +function get_price_by_point(point:number, pool:PoolInfo) { + const { token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = Math.pow(10, token_x_metadata.decimals) / Math.pow(10, token_y_metadata.decimals); + const price = getPriceByPoint(point, decimalRate_point) + return price; +} +function get_point_by_price(price: string, pool:PoolInfo) { + const { point_delta, token_x_metadata, token_y_metadata } = pool; + const decimalRate_point = + Math.pow(10, token_y_metadata.decimals) / + Math.pow(10, token_x_metadata.decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; +} \ No newline at end of file diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index e25bd81f1..d8bd4b4fa 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -81,25 +81,6 @@ export const useAllPoolsV2 = () => { Number(pricey); p.tvl = tvlx + tvly; - - try { - const topBinFee = await getDCLTopBinFee({ - pool_id: p.pool_id, - slot_number: 100, - }); - - if (!topBinFee || ONLY_ZEROS.test(topBinFee.total_liquidity)) { - p.top_bin_apr = '0'; - p.top_bin_apr_display = '-'; - } else { - const apr = new Big(topBinFee.total_fee) - .div(topBinFee.total_liquidity) - .mul(365) - .toFixed(2); - p.top_bin_apr = apr; - p.top_bin_apr_display = apr + '%'; - } - } catch (error) {} return p; }) ); From e8f39e6ce5cdaa6a74b3f163c691561a01bf1319 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 9 Aug 2023 23:10:38 +0800 Subject: [PATCH 109/204] fix ui issue --- src/components/d3Chart/DclChart.tsx | 21 ++- src/components/layout/NavigationBar.tsx | 2 +- src/components/swap/SwapLimitOrderChart.tsx | 184 +++++++++++++------- src/pages/pools/DetailsPage.tsx | 34 ++-- src/pages/pools/LiquidityPage.tsx | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 116 ++++++++---- src/services/indexer.ts | 8 +- src/state/pool.ts | 58 +++--- 8 files changed, 268 insertions(+), 157 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 9135b8413..f4ce031a6 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -49,7 +49,7 @@ import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; -import { toReadableNumber } from '~utils/numbers'; +import { toReadableNumber, formatWithCommas } from '~utils/numbers'; import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; export default function DclChart({ pool_id, @@ -160,7 +160,7 @@ export default function DclChart({ drawChart(); setDrawChartDone(true); } - }, [price_range, chartDataList, reverse]); + }, [price_range, chartDataList, reverse, config?.svgWidth]); // generate user chart useEffect(() => { if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { @@ -369,8 +369,8 @@ export default function DclChart({ } set_user_liquidities_detail({ total_value: formatWithCommas_usd(total_value.toFixed()), - min_price: formatPrice(min_price), - max_price: formatPrice(max_price), + min_price: formatPriceWithCommas(min_price), + max_price: formatPriceWithCommas(max_price), total_x_amount: formatNumber(total_x_amount.toFixed()), total_y_amount: formatNumber(total_y_amount.toFixed()), apr_24, @@ -716,8 +716,8 @@ export default function DclChart({ token_y_amount_in_order: formatWithCommas_number(order_y), } : {}), - price_by_token_x: formatPrice(price_by_token_x), - price_by_token_y: formatPrice(price_by_token_y), + price_by_token_x: formatPriceWithCommas(price_by_token_x), + price_by_token_y: formatPriceWithCommas(price_by_token_y), }); } function LeaveBox(e: any, d: IChartData) { @@ -756,9 +756,9 @@ export default function DclChart({ axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { const dBig = new Big(d); if (dBig.gte(10000)) { - return dBig.toFixed(0); + return formatWithCommas(dBig.toFixed(0)); } else { - return d; + return formatWithCommas(dBig.toFixed()); } }); } @@ -1154,7 +1154,10 @@ export default function DclChart({ function draw_radius_mode_bar() { const scale: any = scaleAxis(); const { targetPoint } = config; - const price = get_price_by_point(targetPoint); + let price = get_price_by_point(targetPoint); + if (reverse) { + price = reverse_price(price); + } const x = scale(price); d3.select(`${randomId} .radiusBar`) .attr('transform', `translate(${x}, -${axisHeight})`) diff --git a/src/components/layout/NavigationBar.tsx b/src/components/layout/NavigationBar.tsx index c90a72cbc..65554d0ef 100644 --- a/src/components/layout/NavigationBar.tsx +++ b/src/components/layout/NavigationBar.tsx @@ -1549,7 +1549,7 @@ function MenuBar() { : 'text-primaryText' }`} > - {logo ?
{logo}
: null} + {logo ?
{logo}
: null}
{label}
{children ? ( diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index ca7dafe77..690992d94 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -35,6 +35,7 @@ export default function SwapLimitOrderChart() { useState(); const [sell_token_y_list, set_sell_token_y_list] = useState(); + const [fetch_data_done, set_fetch_data_done] = useState(false); const [buy_list, set_buy_list] = useState(); const [sell_list, set_sell_list] = useState(); const [market_loading, set_market_loading] = useState(false); @@ -48,6 +49,7 @@ export default function SwapLimitOrderChart() { const sellBoxRef = useRef(null); useEffect(() => { if (pool_id) { + set_fetch_data_done(false); get_points_of_orders(); get_pool_detail(); setZoom(GEARS[0]); @@ -61,6 +63,7 @@ export default function SwapLimitOrderChart() { useEffect(() => { if (pool && orders) { process_orders(); + set_fetch_data_done(true); } }, [pool, orders]); useEffect(() => { @@ -313,6 +316,7 @@ export default function SwapLimitOrderChart() { setZoom(targetPercent); } } + const is_empty = fetch_data_done && !sell_list?.length && !buy_list?.length; return (
{/* chart */} - + {is_empty ? ( +
+ + + Not enough data for the chart right now. + +
+ ) : ( + + )}
{/* table area */}
-
- {sell_list?.map((item: IOrderPointItem, index) => { - return ( -
- - {formatPrice(item.price)} - - - {formatNumber( - item.amount_x_readable || item.amount_y_readable - )} - - - {formatNumber( - item.accumulated_x_readable || item.accumulated_y_readable - )} - -
- ); - })} -
-
- Market Pirce - -
-
- {buy_list?.map((item: IOrderPointItem, index) => { - return ( -
- - {formatPrice(item.price)} - - - {formatNumber( - item.amount_x_readable || item.amount_y_readable - )} - - - {formatNumber( - item.accumulated_x_readable || item.accumulated_y_readable - )} - -
- ); - })} -
+ {is_empty ? ( +
+ No order yet +
+ ) : ( +
+
+ {sell_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPrice(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || + item.accumulated_y_readable + )} + +
+ ); + })} +
+
+ Market Pirce + +
+
+ {buy_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPrice(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || + item.accumulated_y_readable + )} + +
+ ); + })} +
+
+ )} @@ -1147,6 +1180,27 @@ function RefreshIcon(props: any) { ); } +function EmptyIcon(props: any) { + return ( + + + + + ); +} function LeftArrowIcon(props: any) { return ( { return (
+ {showLiqudityButton ? ( + + ) : null} - {showLiqudityButton ? ( - - ) : null}
); }; @@ -2566,7 +2566,7 @@ export function PoolDetailsPage() { width="w-full" className="relative rounded-2xl mr-4 mb-4 h-full flex flex-col justify-center items-center" padding="px-7 py-5" - bgcolor={isClientMobie() ? 'bg-transparent' : 'bg-cardBg'} + bgcolor={'bg-transparent'} style={{ height: isClientMobie() ? '370px' : '470px', }} diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index b2ea57d55..388336007 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -1742,7 +1742,7 @@ function PoolRowV2({ const history = useHistory(); const displayOfTopBinApr = useDCLTopBinFee({ - pool + pool, }); if (!curRowTokens) return <>; diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index d84e5c2c7..a8fd90ae9 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -273,7 +273,7 @@ export default function PoolDetailV3() { } const topBinApr = useDCLTopBinFee({ - pool: poolDetail + pool: poolDetail, }); if (!poolDetail) return ; @@ -419,7 +419,6 @@ export default function PoolDetailV3() {
{ - if (liquidities && poolDetail && tokenPriceList && Object.keys(tokenPriceList).length) { + if ( + liquidities && + poolDetail && + tokenPriceList && + Object.keys(tokenPriceList).length + ) { get_24_apr_and_fee(); } }, [poolDetail, tokenPriceList, liquidities]); @@ -672,20 +676,28 @@ function YourLiquidityBox(props: { // 24h profit apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); // total unClaimed fee - const [unClaimed_tvl_fee, unClaimed_amount_x_fee, unClaimed_amount_y_fee] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); + const [ + unClaimed_tvl_fee, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); // total earned fee const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; total_earned_fee_x = toReadableNumber( token_x_metadata.decimals, Big(total_fee_x || 0).toFixed() ); - total_earned_fee_x = Big(total_earned_fee_x).plus(unClaimed_amount_x_fee).toFixed(); + total_earned_fee_x = Big(total_earned_fee_x) + .plus(unClaimed_amount_x_fee) + .toFixed(); total_earned_fee_y = toReadableNumber( token_y_metadata.decimals, Big(total_fee_y || 0).toFixed() ); - total_earned_fee_y = Big(total_earned_fee_y).plus(unClaimed_amount_y_fee).toFixed(); + total_earned_fee_y = Big(total_earned_fee_y) + .plus(unClaimed_amount_y_fee) + .toFixed(); const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; @@ -694,7 +706,9 @@ function YourLiquidityBox(props: { total_fee_earned = total_earned_fee_x_value .plus(total_earned_fee_y_value) .toFixed(); - total_fee_earned = Big(total_fee_earned).plus(unClaimed_tvl_fee).toFixed(); + total_fee_earned = Big(total_fee_earned) + .plus(unClaimed_tvl_fee) + .toFixed(); } set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); @@ -1147,7 +1161,8 @@ function UnclaimedFeesBox(props: any) { const [cliam_loading, set_cliam_loading] = useState(false); useEffect(() => { if (liquidities) { - const [total_tvl_fee, total_amount_x_fee, total_amount_y_fee] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList) + const [total_tvl_fee, total_amount_x_fee, total_amount_y_fee] = + get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); set_user_liquidities_total({ total_amount_x_fee, total_amount_y_fee, @@ -1260,12 +1275,16 @@ function UnclaimedFeesBox(props: any) {
); } -function get_unClaimed_fee_data(liquidities:UserLiquidityInfo[], poolDetail:PoolInfo, tokenPriceList:any) { +function get_unClaimed_fee_data( + liquidities: UserLiquidityInfo[], + poolDetail: PoolInfo, + tokenPriceList: any +) { // UnClaimed fee let total_amount_x_fee = 0; let total_amount_y_fee = 0; let total_tvl_fee = 0; - const { token_x_metadata, token_y_metadata } = poolDetail + const { token_x_metadata, token_y_metadata } = poolDetail; liquidities.forEach((liquidity: UserLiquidityInfo) => { const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; const unclaimed_fee_x_amount = toReadableNumber( @@ -1285,7 +1304,7 @@ function get_unClaimed_fee_data(liquidities:UserLiquidityInfo[], poolDetail:Pool total_amount_y_fee += Number(unclaimed_fee_y_amount); total_tvl_fee += Number(total_fees_price); }); - return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; + return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; } function RelatedFarmsBox(props: any) { const { poolDetail, tokenPriceList, sole_seed } = props; @@ -1945,7 +1964,7 @@ function Chart(props: any) { width="w-full" className="relative rounded-2xl mr-4 mb-4 h-full flex flex-col items-center" padding="px-7 py-5 xsm:px-4" - bgcolor={isClientMobie() ? 'bg-transparent' : 'bg-cardBg'} + bgcolor={'bg-transparent'} style={{ height: isClientMobie() ? '390px' : '470px', }} @@ -2319,37 +2338,45 @@ export function RecentTransactions({
From ad629683a131c6d1bdf88464067ae84e3323da54 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 12 Aug 2023 18:49:54 +0800 Subject: [PATCH 115/204] fix bug --- src/components/d3Chart/DclChart.tsx | 13 +++++++--- .../Orderly/components/MyOrder/index.tsx | 26 +++++++++++++++---- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 16 +++++------- src/services/commonV3.ts | 7 +++++ 5 files changed, 45 insertions(+), 19 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index a8930d598..b11de96ae 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -694,13 +694,20 @@ export default function DclChart({ e.offsetX + disFromHoverBoxToPointer }px, ${e.offsetY / 2}px)` ); - const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = - d; + const { + point_l, + token_x, + token_y, + order_x, + order_y, + fee, + total_liquidity, + } = d; const { colors } = getConfig(); const total_token_x = Big(token_x).plus(order_x); const total_token_y = Big(token_y).plus(order_y); - const price_by_token_x = get_price_by_point(+point); + const price_by_token_x = get_price_by_point(+point_l); const price_by_token_y = reverse_price(price_by_token_x); const apr = Big(total_liquidity).gt(0) ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index 8c1f57985..852077de1 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -2173,6 +2173,7 @@ function OrderCard({ sessionStorage.removeItem(REF_FI_MY_ORDER_SHOW_HISTORY_SWAP_INFO); } }; + const pool_id_by_url = useDclPoolIdByUrl(); const [orderType, setOrderType] = useState<'active' | 'history'>( sessionStorage.getItem(ORDER_TYPE_KEY) || @@ -2207,8 +2208,23 @@ function OrderCard({ const [select_type, set_select_type] = useState<'all' | 'current'>('all'); const [activeOrderList, setActiveOrderList] = useState(); const [historyOrderList, setHistoryOrderList] = useState(); - const pool_id_by_url = useDclPoolIdByUrl(); - console.log('000000000-pool_id_by_url', pool_id_by_url); + + const tokenIds = useMemo(() => { + if (pool_id_by_url) { + const [token_x, token_y, fee] = pool_id_by_url.split('|'); + return [token_x, token_y]; + } + return []; + }, [pool_id_by_url]); + + const tokens = useTokens(tokenIds) || []; + const current_pair_tokens_map = tokens.reduce((acc, cur) => { + return { + ...acc, + [cur.id]: cur, + }; + }, {}); + useEffect(() => { if (activeOrder.length) { if (select_type == 'all') { @@ -2437,10 +2453,10 @@ function OrderCard({ `; } function get_current_pairs() { - if (pool_id_by_url && tokensMap) { + if (pool_id_by_url && current_pair_tokens_map) { const [token_x, token_y, fee] = pool_id_by_url.split('|'); - const token_x_meta = tokensMap[token_x]; - const token_y_meta = tokensMap[token_y]; + const token_x_meta = current_pair_tokens_map[token_x]; + const token_y_meta = current_pair_tokens_map[token_y]; if (token_x_meta?.symbol && token_y_meta?.symbol) { const tokens = sort_tokens_by_base([token_x_meta, token_y_meta]); return `${tokens[1].symbol}/${tokens[0].symbol}`; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 63bee36c0..9b09b2337 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -2724,7 +2724,7 @@ function PointsComponent() { {onlyAddXToken ? `*Only ${currentSelectedPool?.token_x_metadata?.symbol} is needed in the price range you choose.` : ''} - {invalidRange ? `The maket price is outside your price range.` : ''} + {invalidRange ? : ''} diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 486af1c03..0ab321b50 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -2296,7 +2296,8 @@ export function RecentTransactions({ if (!swapIn || !swapOut) return null; let reverse = false; - if (sort_tokens_by_base([swapIn, swapOut])[0].id !== swapIn.id) { + const sort_tokens = sort_tokens_by_base([swapIn, swapOut]); + if (sort_tokens[0].id !== swapIn.id) { reverse = true; } @@ -2330,6 +2331,7 @@ export function RecentTransactions({ ); + const display_price = reverse ? reverse_price(price) : price; return ( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 1c19403bb..6a8ffdf3a 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1203,6 +1203,8 @@ export const TOKEN_LIST_FOR_RATE = [ 'USDt', ]; +export const TOKEN_LIST_FOR_RATE_EXTRA = ['NEAR']; + export const PAUSE_DCL = true; export function pause_v2_tip() { @@ -1223,6 +1225,11 @@ export function pause_old_dcl_claim_tip() { } export function sort_tokens_by_base(tokens: TokenMetadata[]) { const tokens_temp = JSON.parse(JSON.stringify(tokens || [])); + tokens_temp.sort((item2: TokenMetadata, item1: TokenMetadata) => { + if (TOKEN_LIST_FOR_RATE_EXTRA.indexOf(item2.symbol) > -1) return 1; + if (TOKEN_LIST_FOR_RATE_EXTRA.indexOf(item1.symbol) > -1) return -1; + return 0; + }); tokens_temp.sort((item2: TokenMetadata, item1: TokenMetadata) => { if (TOKEN_LIST_FOR_RATE.indexOf(item2.symbol) > -1) return 1; if (TOKEN_LIST_FOR_RATE.indexOf(item1.symbol) > -1) return -1; From 9d445a5eea405e82ef3587ba1da2b5f47ace8e48 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 13 Aug 2023 12:54:54 +0800 Subject: [PATCH 116/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 94 +++++++++++++++++-- src/components/d3Chart/interfaces.tsx | 1 - src/components/pool/YourLiquidityV2.tsx | 4 +- src/components/swap/SwapLimitOrderChart.tsx | 77 ++++++++------- .../Orderly/components/MyOrder/index.tsx | 2 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 24 +++-- src/services/commonV3.ts | 16 ++++ 7 files changed, 166 insertions(+), 52 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index b11de96ae..abefff781 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -56,19 +56,24 @@ export default function DclChart({ pool_id, leftPoint, rightPoint, + targetPoint, setLeftPoint, setRightPoint, + setTargetPoint, config, chartType, removeParams, newlyAddedLiquidities, reverse, + radius, }: { pool_id: string; leftPoint?: number; rightPoint?: number; + targetPoint?: number; setLeftPoint?: Function; setRightPoint?: Function; + setTargetPoint?: Function; config?: IPoolChartConfig; chartType?: 'POOL' | 'USER'; removeParams?: { @@ -79,6 +84,7 @@ export default function DclChart({ }; newlyAddedLiquidities?: UserLiquidityInfo[]; reverse?: boolean; + radius?: number; }) { const [pool, setPool] = useState(); const [price_range, set_price_range] = useState(); @@ -102,6 +108,7 @@ export default function DclChart({ timer: '', }); const dragBarWidth = 28; + const radiusDragBarWidth = 20; const percentBoxWidth = 44; const min_bar_height = 2; const svgWidth = +(appearanceConfig.svgWidth || 520); @@ -234,7 +241,11 @@ export default function DclChart({ newPrice = get_price_by_point(newPoint); } const movePercent = diffPrices(newPrice); - const x = scale(+newPrice); + let x = scale(+newPrice); + // transform attr can not receive too big number + if (Big(x).gt(9999999999)) { + x = 9999999999; + } d3.select(`${randomId} .drag-right`).attr( 'transform', `translate(${x}, -${axisHeight})` @@ -265,7 +276,7 @@ export default function DclChart({ } }, [rightPoint]); useEffect(() => { - if (config?.radiusMode && config?.targetPoint && drawChartDone) { + if (config?.radiusMode && isValid(targetPoint) && drawChartDone) { // hide drag bar and show target price bar draw_radius_mode_bar(); d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); @@ -275,7 +286,15 @@ export default function DclChart({ d3.select(`${randomId} .rightBar`).attr('style', ''); d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); } - }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); + }, [config?.radiusMode, drawChartDone, targetPoint, pool_id, price_range]); + + // bind drag event + useEffect(() => { + if (!config.controlHidden && pool_id && drawChartDone) { + bind_radius_mode_bar_event(); + } + }, [config.controlHidden, drawChartDone, price_range, pool_id, radius]); + // draw remove area for user useEffect(() => { if (removeParams && drawChartDone) { @@ -1166,15 +1185,68 @@ export default function DclChart({ } function draw_radius_mode_bar() { const scale: any = scaleAxis(); - const { targetPoint } = config; let price = get_price_by_point(targetPoint); if (reverse) { price = reverse_price(price); } - const x = scale(price); + const x = scale(price) - 8; d3.select(`${randomId} .radiusBar`) .attr('transform', `translate(${x}, -${axisHeight})`) - .attr('style', 'display:block'); + .attr('style', 'display:block;cursor:ew-resize'); + } + function bind_radius_mode_bar_event() { + const scale: any = scaleAxis(); + const dragTarget = d3.drag().on('drag', (e) => { + let p; + const drag_price = scale.invert(e.x); + if (reverse) { + p = reverse_price(drag_price); + } else { + p = drag_price; + } + // if target price is out of range then limit dragging + if ( + Big(drag_price).lte(price_range[0]) || + Big(drag_price).gte(price_range[price_range.length - 1]) + ) + return; + + const newTargetPoint = get_point_by_price(p); + let left_point, right_point; + const BIN_WIDTH = getConfig().bin * pool.point_delta; + if (reverse) { + left_point = Math.min( + get_bin_point_by_point(newTargetPoint + BIN_WIDTH * radius), + POINTRIGHTRANGE + ); + right_point = Math.max( + left_point - BIN_WIDTH * radius * 2, + POINTLEFTRANGE + ); + } else { + right_point = Math.min( + get_bin_point_by_point(newTargetPoint + BIN_WIDTH * radius), + POINTRIGHTRANGE + ); + left_point = Math.max( + right_point - BIN_WIDTH * radius * 2, + POINTLEFTRANGE + ); + } + + const left_price = get_price_by_point(left_point); + const right_price = get_price_by_point(right_point); + + // if left or right price is out of range then limit dragging + if (Big(left_price).lte(0) || Big(right_price).lte(0)) return; + + setTargetPoint && setTargetPoint(newTargetPoint); + setDragLeftPoint(left_point); + setDragRightPoint(right_point); + setLeftPoint && setLeftPoint(left_point); + setRightPoint && setRightPoint(right_point); + }); + d3.select(`${randomId} .radiusBar`).call(dragTarget); } function get_current_price(forward?: boolean) { const current_price = get_price_by_point(pool.current_point); @@ -1442,7 +1514,7 @@ export default function DclChart({ > - + {/* show bar in radius mode */} +
- ${your_liquidity || '-'} + {your_liquidity || '-'}
@@ -1669,7 +1669,7 @@ function UserLiquidityLineStyleGroupStyle2() { )}
-
+
{intersectionRangeList.map((range: string[], i: number) => { return (
diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 339c8a055..5a2fe3877 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -87,38 +87,49 @@ export default function SwapLimitOrderChart() { sellBoxRef.current.scrollTop = 10000; } }, [sellBoxRef, sell_list]); - const [cur_pairs, cur_token_symbol, cur_pair_icons] = useMemo(() => { - if (pool) { - const classStr = 'w-6 h-6 rounded-full border border-gradientFromHover'; - const { token_x_metadata, token_y_metadata } = pool; - const x_symbol = toRealSymbol(token_x_metadata.symbol); - const y_symbol = toRealSymbol(token_y_metadata.symbol); - if (switch_token == 'X') { - const y_icons = ( - <> - - - - ); - return [`${y_symbol}/${x_symbol}`, `${y_symbol}`, y_icons]; - } else if (switch_token == 'Y') { - const x_icons = ( - <> - - - - ); - return [`${x_symbol}/${y_symbol}`, `${x_symbol}`, x_icons]; + const [cur_pairs, cur_pairs_price_mode, cur_token_symbol, cur_pair_icons] = + useMemo(() => { + if (pool) { + const classStr = 'w-6 h-6 rounded-full border border-gradientFromHover'; + const { token_x_metadata, token_y_metadata } = pool; + const x_symbol = toRealSymbol(token_x_metadata.symbol); + const y_symbol = toRealSymbol(token_y_metadata.symbol); + if (switch_token == 'X') { + const y_icons = ( + <> + + + + ); + return [ + `${y_symbol}/${x_symbol}`, + `${x_symbol}-${y_symbol}`, + `${y_symbol}`, + y_icons, + ]; + } else if (switch_token == 'Y') { + const x_icons = ( + <> + + + + ); + return [ + `${x_symbol}/${y_symbol}`, + `${y_symbol}-${x_symbol}`, + `${x_symbol}`, + x_icons, + ]; + } } - } - return []; - }, [switch_token, pool]); + return []; + }, [switch_token, pool]); async function refresh() { await get_points_of_orders(); await get_pool_detail(); @@ -335,7 +346,9 @@ export default function SwapLimitOrderChart() {
{cur_pair_icons}
- {cur_pairs} + + {cur_pairs_price_mode} +
diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index 852077de1..8bc8d09bd 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -2459,7 +2459,7 @@ function OrderCard({ const token_y_meta = current_pair_tokens_map[token_y]; if (token_x_meta?.symbol && token_y_meta?.symbol) { const tokens = sort_tokens_by_base([token_x_meta, token_y_meta]); - return `${tokens[1].symbol}/${tokens[0].symbol}`; + return `${tokens[0].symbol}-${tokens[1].symbol}`; } } return ''; diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 9b09b2337..8cf7f2e51 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -60,6 +60,7 @@ import { UserLiquidityInfo, reverse_price, sort_tokens_by_base, + getSlotPointByPoint, } from '../../services/commonV3'; import { formatWithCommas, @@ -2223,18 +2224,19 @@ function PointsComponent() { if (currentSelectedPool?.pool_id && !switch_pool_loading) { const { current_point } = currentSelectedPool; let left_point, right_point; + const targetPoint = get_slot_point_by_point(current_point); if (pair_is_reverse) { left_point = get_bin_point_by_point( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER ); right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; } else { right_point = get_bin_point_by_point( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER ); left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; } - setTargetPoint(current_point); + setTargetPoint(targetPoint); setRadius(RADIUS_DEFAULT_NUMBER); setLeftPoint(left_point); setRightPoint(right_point); @@ -2344,23 +2346,23 @@ function PointsComponent() { appropriate_right_point, appropriate_target_point; if (pair_is_reverse) { - appropriate_target_point = get_bin_point_by_price(reverse_price(price)); + appropriate_target_point = get_point_by_price(reverse_price(price)); appropriate_left_point = get_bin_point_by_point( appropriate_target_point + BIN_WIDTH * radius ); appropriate_right_point = get_bin_point_by_point( appropriate_left_point - BIN_WIDTH * radius * 2 ); - appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; + // appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; } else { - appropriate_target_point = get_bin_point_by_price(price); + appropriate_target_point = get_point_by_price(price); appropriate_right_point = get_bin_point_by_point( appropriate_target_point + BIN_WIDTH * radius ); appropriate_left_point = get_bin_point_by_point( appropriate_right_point - BIN_WIDTH * radius * 2 ); - appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + // appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; } setLeftPoint(appropriate_left_point); setRightPoint(appropriate_right_point); @@ -2458,6 +2460,10 @@ function PointsComponent() { const { point_delta } = currentSelectedPool; return getBinPointByPoint(point_delta, SLOT_NUMBER, point); } + function get_slot_point_by_point(point: number) { + const { point_delta } = currentSelectedPool; + return getSlotPointByPoint(point_delta, point); + } function getPair() { if (pair_is_reverse) { return `(${tokenX.symbol}/${tokenY.symbol})`; @@ -2511,9 +2517,11 @@ function PointsComponent() { rightPoint={rightPoint} setLeftPoint={setLeftPoint} setRightPoint={setRightPoint} + setTargetPoint={setTargetPoint} + targetPoint={targetPoint} + radius={radius} config={{ radiusMode: priceRangeMode == 'by_radius', - targetPoint, }} reverse={pair_is_reverse} > diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 6a8ffdf3a..0d2f4967b 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -82,6 +82,22 @@ export function getPointByPrice( } return point_int_slot; } +/** + * caculate point by price + * @param pointDelta + * @param price + * @param decimalRate tokenY/tokenX + * @returns + */ +export function getSlotPointByPoint(pointDelta: number, point: number) { + let point_int_slot = Math.round(point / pointDelta) * pointDelta; + if (point_int_slot < POINTLEFTRANGE) { + return POINTLEFTRANGE; + } else if (point_int_slot > POINTRIGHTRANGE) { + return 800000; + } + return point_int_slot; +} export const CONSTANT_D = 1.0001; export const POINTDELTAMAP = { 100: 1, From 96d233258bec8c2b0af8804ad15036a63619fc63 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 13 Aug 2023 20:22:01 +0800 Subject: [PATCH 117/204] optimization --- src/components/d3Chart/DclChart.tsx | 2 +- src/components/swap/SwapLimitOrderChart.tsx | 23 ++++++++++++--------- 2 files changed, 14 insertions(+), 11 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index abefff781..5bf52dd7c 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -169,7 +169,7 @@ export default function DclChart({ drawChart(); setDrawChartDone(true); } - }, [price_range, chartDataList, reverse, config?.svgWidth]); + }, [price_range, chartDataList, config?.svgWidth]); // generate user chart useEffect(() => { if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 5a2fe3877..2ae13cd04 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -51,9 +51,8 @@ export default function SwapLimitOrderChart() { useEffect(() => { if (pool_id) { set_fetch_data_done(false); - get_points_of_orders(); - get_pool_detail(); setZoom(GEARS[0]); + fetch_data(); } }, [pool_id]); useEffect(() => { @@ -131,17 +130,24 @@ export default function SwapLimitOrderChart() { return []; }, [switch_token, pool]); async function refresh() { - await get_points_of_orders(); - await get_pool_detail(); + await fetch_data(); set_market_loading(false); } + async function fetch_data() { + const orders = await get_points_of_orders(); + const [switch_token, is_reverse, p] = (await get_pool_detail()) as any; + set_switch_token(switch_token); + set_pair_is_reverse(is_reverse); + setPool(p); + setOrders(orders); + } async function get_points_of_orders() { const result = await get_pointorder_range({ pool_id, left_point, right_point, }); - setOrders(result); + return result; } async function get_pool_detail() { const p: PoolInfo = await get_pool(pool_id); @@ -153,13 +159,10 @@ export default function SwapLimitOrderChart() { p.token_y_metadata, ]); if (tokens[0].id == p.token_x_metadata.id) { - set_switch_token('X'); - set_pair_is_reverse(false); + return ['X', false, p]; } else { - set_switch_token('Y'); - set_pair_is_reverse(true); + return ['Y', true, p]; } - setPool(p); } function process_orders() { const list = Object.values(orders); From e899e65150f2eb8b6ea099c7de0b9bd4ba8502a2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 13 Aug 2023 21:54:43 +0800 Subject: [PATCH 118/204] update --- src/components/d3Chart/DclChart.tsx | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 5bf52dd7c..5462eb205 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -431,6 +431,10 @@ export default function DclChart({ get_price_range_by_percent(range); set_price_range([+price_l_default, +price_r_default]); setZoom(range); + } else { + const { sortP } = get_price_and_liquidity_range(); + const range = [+sortP[0], +sortP[sortP.length - 1]]; + set_price_range(range); } } async function get_data_from_back_end() { From 8fa3c65f8e6b4c73d5b2e4f67dbdd563ed2fd0b3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 14 Aug 2023 11:09:17 +0800 Subject: [PATCH 119/204] fix bug --- src/components/d3Chart/DclChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 5462eb205..47f0e2eae 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -677,7 +677,7 @@ export default function DclChart({ const { point_delta } = pool; const list = chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const data: IChartData[] = list.map((o: IChartData) => { + const data: IChartData[] = list?.map((o: IChartData) => { const { point } = o; let price_l, price_r, point_l, point_r; if (reverse) { @@ -708,7 +708,7 @@ export default function DclChart({ point_r, }; }); - return data; + return data || []; } function hoverBox(e: any, d: IChartData) { d3.select(`${randomId} .overBox`).attr( From 0b4da85fce7abbffcc0e6137ba8bf95ff0e406bc Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 15 Aug 2023 21:28:37 +0800 Subject: [PATCH 120/204] update mobile ui --- src/components/d3Chart/DclChart.tsx | 74 +- src/components/pool/RemovePoolV3.tsx | 15 +- src/components/pool/YourLiquidityV2.tsx | 1525 +++++++++++------- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 53 +- src/pages/poolsV3/PoolDetailV3.tsx | 395 +---- src/pages/poolsV3/YourLiquidityPageV3.tsx | 2 +- tailwind.config.js | 3 +- 7 files changed, 1086 insertions(+), 981 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 47f0e2eae..ebd2a4ddd 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -134,6 +134,7 @@ export default function DclChart({ ); /** constant end */ const { accountId } = useWalletSelector(); + const is_mobile = isMobile(); useEffect(() => { // get all token prices getBoostTokenPrices().then((result) => { @@ -711,12 +712,17 @@ export default function DclChart({ return data || []; } function hoverBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); + if (is_mobile) { + d3.select(`${randomId} .overBox`).attr('style', `display:block`); + } else { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } + const { point_l, token_x, @@ -757,28 +763,40 @@ export default function DclChart({ }); } function LeaveBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); + if (is_mobile) { + d3.select(`${randomId} .overBox`).attr('style', `display:none`); + } else { + d3.select(`${randomId} .overBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } } function hoverUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); + if (is_mobile) { + d3.select(`${randomId} .wholeOverBox`).attr('style', `display:block;`); + } else { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:visible;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } } function LeaveUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); + if (is_mobile) { + d3.select(`${randomId} .wholeOverBox`).attr('style', `display:none;`); + } else { + d3.select(`${randomId} .wholeOverBox`).attr( + 'style', + `visibility:hidden;transform:translate(${ + e.offsetX + disFromHoverBoxToPointer + }px, ${e.offsetY / 2}px)` + ); + } } function remove_control() { d3.select(`${randomId} .control`).remove(); @@ -1450,7 +1468,7 @@ export default function DclChart({ return ( <>
{/* control button area*/} -
+
{/* show hover box then hover on the bin */} -
+
APR(24h) @@ -1750,7 +1768,7 @@ export default function DclChart({ ) : null}
-
+
Your Liquidity diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 7a22368ca..33dcec708 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -52,6 +52,7 @@ import { IBatchUpdateiquidityInfo, } from '~pages/poolsV3/interfaces'; import DclChart from '../../components/d3Chart/DclChart'; +import { isMobile } from '~utils/device'; export type RemoveType = 'left' | 'right' | 'all'; @@ -697,11 +698,13 @@ export const RemovePoolV3 = (props: any) => { return [min_token_x_amount, min_token_y_amount]; } const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; + const is_mobile = isMobile(); + const cardWidth = is_mobile ? '95vw' : '550px'; return ( {/* Title */}
@@ -754,7 +757,7 @@ export const RemovePoolV3 = (props: any) => { controlHidden: true, currentBarHidden: true, hoverBoxHidden: true, - svgWidth: '480', + svgWidth: is_mobile ? '320' : '480', svgHeight: '82', }} chartType="USER" @@ -865,7 +868,7 @@ export const RemovePoolV3 = (props: any) => { step={1} > {/* Set points */} -
+
{/* min price */}
@@ -943,13 +946,13 @@ export const RemovePoolV3 = (props: any) => { className="mt-6" style={{ borderTop: '1px solid rgba(110, 124, 133, 0.2)' }} >
-
+
diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 939624447..84b6df573 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -351,7 +351,11 @@ export function YourLiquidityV2(props: any) { return (
{groupYourLiquidity && Object.entries(groupYourLiquidity).length && ( -
+
+ ); + } else { + return ( + + ); + } +} +function UserLiquidityLineStyleGroupStyle1Mobile() { const { hover, setHover, @@ -1188,63 +1202,73 @@ function UserLiquidityLineStyleGroupStyle1() { tokenPriceList, } = useContext(GroupData); return ( -
setHover(true)} - onMouseLeave={() => setHover(false)} - > - {/* for PC */} -
-
-
{ - e.preventDefault(); - e.stopPropagation(); - goDetailV2(); - }} - > - {/* 1/3 */} -
-
- - + <> +
+ {/* head */} +
{ + e.preventDefault(); + e.stopPropagation(); + goDetailV2(); + }} + > +
+ + +
+
+
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + + + {isInRange ? ( + + ) : ( + + )} + +
+ +
+
+ DCL
-
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} +
+ + - -
-
- DCL -
-
- - - - {+fee / 10000}% -
- {farm_icon ? ( -
- -
- ) : null} -
+ {+fee / 10000}%
+ {farm_icon ? ( +
+ +
+ ) : null}
- {/* 2/3 */} - {/* Price Range */} -
-
+
+
+ {/* body */} +
+
+ + Price Range + +
+
{intersectionRangeList.map((range: string[], i: number) => { return (
@@ -1263,31 +1287,20 @@ function UserLiquidityLineStyleGroupStyle1() {
); })} - - {ratedMapTokens} - -
-
- - {isInRange ? ( - - ) : ( - - )} -
+ + {ratedMapTokens} +
- {/* APR(24h) */} -
- {accountAPR || '-'} +
+
+ + APR(24h) + +
+ {accountAPR || '-'} {joined_seeds ? ( -
+
{Object.values(joined_seeds) .sort(sort_joined_seeds) .map((joined_seed_info: IUserJoinedSeedDetail) => { @@ -1296,7 +1309,7 @@ function UserLiquidityLineStyleGroupStyle1() { if (seed_status == 'ended') return null; if (length == 1) { return ( -
+
+
({seed_status == 'run' ? 'new' : 'pre.'}) APR @@ -1319,7 +1332,7 @@ function UserLiquidityLineStyleGroupStyle1() { })}
) : tip_seed ? ( -
+
) : null}
- {/* Your Liquidity */} -
- {your_liquidity} - {joined_seeds ? ( -
- {Object.values(joined_seeds) - .sort(sort_joined_seeds) - .map((joined_seed_info: IUserJoinedSeedDetail) => { - const length = Object.values(joined_seeds).length; - const { - seed_status, - value_of_investment, - go_farm_url_link, - } = joined_seed_info; - if (length == 1) { - return ( - - ); - } else { - return ( - - ); - } - })} -
- ) : tip_seed ? ( - - ) : null} -
- {/* tip */} - {tip_seed ? ( -
-
- - - {' '} - {tip_seed.seed_apr} - -
{ - e.stopPropagation(); - openUrl(tip_seed.go_farm_url_link); - }} - > - - - - -
-
+
+ + + +
+ + + {tokenFeeLeft || '-'} + + + + {tokenFeeRight || '-'} +
- ) : null} - -
-
- {/* unclaimed fees */} -
- - - - - - {tokenFeeLeft || '-'} - - - - {tokenFeeRight || '-'} +
+
+ {/* foot */} +
+
+
+ Your Liquidity +
+ + {your_liquidity} -
- -
+ {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { + seed_status, + value_of_investment, + go_farm_url_link, + } = joined_seed_info; + if (length == 1) { + return ( + + ); + } else { + return ( + + ); + } + })} +
+ ) : tip_seed ? ( + + ) : null} +
+
+
+
+ } + /> +
+ { + e.stopPropagation(); + const pool_name = get_pool_name(poolDetail.pool_id); + history.push(`/addLiquidityV2#${pool_name}`); + }} + color="#fff" + minWidth="5rem" + borderRadius="8px" + className={`px-3 w-24 flex-grow h-8 text-center text-sm text-white focus:outline-none`} + > + + +
{ + if (!!joined_seeds && !is_mobile) { + setRemoveButtonTip(true); + } + }} + onMouseLeave={() => { + if (!!joined_seeds) { + setRemoveButtonTip(false); + } + }} + onClick={() => { + if (is_mobile) { + setRemoveButtonTip(!removeButtonTip); + } + }} + > + { + e.stopPropagation(); + setShowRemoveBox(true); + }} + rounded="rounded-lg" + disabled={!!joined_seeds} + px="px-0" + py="py-1" + style={{ minWidth: '5rem' }} + className={`flex-grow w-24 text-sm text-greenColor h-8 ${ + !!joined_seeds ? 'opacity-40 pointer-events-none' : '' + }`} + > + + +
+ You have liquidity in farm, please unstake from{' '} + { + localStorage.setItem('BOOST_FARM_TAB', 'yours'); + openUrl('/v2farms'); + }} + > + Your Farm + {' '} + first. +
+
+
+
+ {/* tip */} + {tip_seed ? ( +
+ + Farm APR up to{' '} + {tip_seed.seed_apr} + +
{ + e.stopPropagation(); + openUrl(tip_seed.go_farm_url_link); + }} + > + + + + +
+
+ ) : null} +
+
+ {showRemoveBox ? ( + { + setShowRemoveBox(false); + }} + listLiquidities={liquidities_list} + tokenMetadata_x_y={tokenMetadata_x_y} + poolDetail={poolDetail} + tokenPriceList={tokenPriceList} + userLiquidity={list_liquidities[0]} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > + ) : null} + + ); +} +function UserLiquidityLineStyleGroupStyle1Pc() { + const { + hover, + setHover, + tip_seed, + goDetailV2, + tokens, + fee, + farm_icon, + intersectionRangeList, + ratedMapTokens, + isInRange, + accountAPR, + joined_seeds, + sort_joined_seeds, + your_liquidity, + tokenMetadata_x_y, + tokenFeeLeft, + tokenFeeRight, + canClaim, + claimRewards, + claim_loading, + history, + joined_seeds_done, + setRemoveButtonTip, + setShowRemoveBox, + removeButtonTip, + showRemoveBox, + liquidities_list, + poolDetail, + tokenPriceList, + } = useContext(GroupData); + return ( +
setHover(true)} + onMouseLeave={() => setHover(false)} + > + {/* for PC */} +
+
+
{ + e.preventDefault(); + e.stopPropagation(); + goDetailV2(); + }} + > + {/* 1/3 */} +
+
+ + +
+
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + + +
+
+ DCL +
+
+ + + + {+fee / 10000}% +
+ {farm_icon ? ( +
+ +
+ ) : null} +
+
+
+ {/* 2/3 */} + {/* Price Range */} +
+
+ {intersectionRangeList.map((range: string[], i: number) => { + return ( +
+ + {displayNumberToAppropriateDecimals(range[0])} + + - + + + {displayNumberToAppropriateDecimals(range[1])} + + {intersectionRangeList.length > 1 && + i < intersectionRangeList.length - 1 && ( + , + )} +
+ ); + })} + + {ratedMapTokens} + +
+
+ + {isInRange ? ( + + ) : ( + + )} + +
+
+ {/* APR(24h) */} +
+ {accountAPR || '-'} + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { seed_apr, seed_status } = joined_seed_info; + if (seed_status == 'ended') return null; + if (length == 1) { + return ( +
+ + + + {seed_apr} +
+ ); + } else { + return ( +
+ + ({seed_status == 'run' ? 'new' : 'pre.'}) APR + + {seed_apr} +
+ ); + } + })} +
+ ) : tip_seed ? ( +
+ + + + 0% +
+ ) : null} +
+ {/* Your Liquidity */} +
+ {your_liquidity} + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { + seed_status, + value_of_investment, + go_farm_url_link, + } = joined_seed_info; + if (length == 1) { + return ( + + ); + } else { + return ( + + ); + } + })} +
+ ) : tip_seed ? ( + + ) : null} +
+
+ {/* tip */} + {tip_seed ? ( +
+
+ + + {' '} + {tip_seed.seed_apr} + +
{ + e.stopPropagation(); + openUrl(tip_seed.go_farm_url_link); + }} + > + + + + +
+
+
+ ) : null} + +
+
+ {/* unclaimed fees */} +
+ + + + + + {tokenFeeLeft || '-'} + + + + {tokenFeeRight || '-'} + +
+ + +
+
+
+
+
+ {showRemoveBox ? ( + { + setShowRemoveBox(false); + }} + listLiquidities={liquidities_list} + tokenMetadata_x_y={tokenMetadata_x_y} + poolDetail={poolDetail} + tokenPriceList={tokenPriceList} + userLiquidity={list_liquidities[0]} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > + ) : null} +
+ ); +} +function UserLiquidityLineStyleGroupStyle2() { + if (is_mobile) { + return ( + + ); + } else { + return ( + + ); + } +} +function UserLiquidityLineStyleGroupStyle2Mobile() { + const { + tip_seed, + tokens, + fee, + intersectionRangeList, + ratedMapTokens, + isInRange, + accountAPR, + joined_seeds, + sort_joined_seeds, + your_liquidity, + tokenMetadata_x_y, + tokenFeeLeft, + tokenFeeRight, + poolDetail, + tokenFeeValue, + } = useContext(GroupData); + const [switch_off, set_switch_off] = useState(true); + function goPoolDetailPage() { + const params_str = get_pool_name(poolDetail.pool_id); + openUrl(`/poolV2/${params_str}`); + } + return ( + <> +
+
+
+
+
+ + +
+ + {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} + +
+ + {your_liquidity || '-'} + +
+
+
+ + {+fee / 10000}% + + { + goPoolDetailPage(); + }} + className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md px-1.5 cursor-pointer hover:text-white py-0.5 mr-1.5" + > + {' '} + + +
+
+
+ + + {tokenFeeValue} + +
+ { + set_switch_off(!switch_off); + }} + switch_off={switch_off} + > +
+
+
+ +
+
+
+
+ + + +
+ + + {isInRange ? ( + + ) : ( + + )} + +
+
+
+ {intersectionRangeList.map((range: string[], i: number) => { + return ( +
+ + {displayNumberToAppropriateDecimals(range[0])} + + - + + + {displayNumberToAppropriateDecimals(range[1])} + + {intersectionRangeList.length > 1 && + i < intersectionRangeList.length - 1 && ,} +
+ ); + })} + + {ratedMapTokens} + +
+
+
+ APR(24h) +
+ {accountAPR || '-'} + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { seed_apr, seed_status } = joined_seed_info; + if (seed_status == 'ended') return null; + if (length == 1) { + return ( +
+ + + + {seed_apr} +
+ ); + } else { + return ( +
+ + ({seed_status == 'run' ? 'new' : 'pre.'}) APR + + {seed_apr} +
+ ); + } + })} +
+ ) : tip_seed ? ( +
+ + + + 0% +
+ ) : null} +
+
+ {joined_seeds || tip_seed ? ( +
+ + + + {joined_seeds ? ( +
+ {Object.values(joined_seeds) + .sort(sort_joined_seeds) + .map((joined_seed_info: IUserJoinedSeedDetail) => { + const length = Object.values(joined_seeds).length; + const { + seed_status, + value_of_investment, + go_farm_url_link, + } = joined_seed_info; + if (length == 1) { + return ( + + ); + } else { + return ( + + ); + } + })} +
+ ) : tip_seed ? ( + -
+ ) : null} +
+ ) : null} + +
+ + + +
+ + + {tokenFeeLeft || '-'} + + + + {tokenFeeRight || '-'} +
- {showRemoveBox ? ( - { - setShowRemoveBox(false); - }} - listLiquidities={liquidities_list} - tokenMetadata_x_y={tokenMetadata_x_y} - poolDetail={poolDetail} - tokenPriceList={tokenPriceList} - userLiquidity={list_liquidities[0]} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > - ) : null} -
+ ); } -function UserLiquidityLineStyleGroupStyle2() { +function UserLiquidityLineStyleGroupStyle2Pc() { const { tip_seed, tokens, @@ -1699,7 +2415,7 @@ function UserLiquidityLineStyleGroupStyle2() {
{accountAPR || '-'} {joined_seeds ? ( -
+
{Object.values(joined_seeds) .sort(sort_joined_seeds) .map((joined_seed_info: IUserJoinedSeedDetail) => { @@ -1749,7 +2465,7 @@ function UserLiquidityLineStyleGroupStyle2() { {joined_seeds ? ( -
+
{Object.values(joined_seeds) .sort(sort_joined_seeds) .map((joined_seed_info: IUserJoinedSeedDetail) => { @@ -1845,339 +2561,6 @@ function UserLiquidityLineStyleGroupStyle2() { ); } - -function UserLiquidityLineStyle2Pc({ - switch_off, - set_switch_off, - getUsageDiv, -}: { - switch_off: boolean; - set_switch_off: any; - getUsageDiv: any; -}) { - const { - getLpt_id, - goYourLiquidityDetailPage, - goPoolDetailPage, - tokenMetadata_x_y, - fee, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, - your_liquidity, - getTokenFeeAmount, - } = useContext(LiquidityContext); - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - return ( -
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - - - {+fee / 10000}% - - { - goPoolDetailPage(); - }} - className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md px-1.5 cursor-pointer hover:text-white py-0.5 mr-1.5" - > - {' '} - - - { - goYourLiquidityDetailPage('new window'); - }} - className="flex items-center bg-portfolioRainbowColor rounded-md gotham_bold text-xs text-white cursor-pointer px-1.5 py-0.5" - > - NFT #{getLpt_id()} - -
-
-
- - ${your_liquidity || '-'} - -
- - - {getTokenFeeAmount('p')} - -
-
- { - set_switch_off(!switch_off); - }} - switch_off={switch_off} - > -
-
-
-
-
-
- - - - ${your_liquidity || '-'} -
-
- - - -
-
- - - {isInrange ? ( - - ) : ( - - )} - -
-
- - {getRate(rate_need_to_reverse_display ? 'right' : 'left')} -{' '} - {getRate(rate_need_to_reverse_display ? 'left' : 'right')} - - - {getRateMapTokens()} - -
-
-
-
- - - - {getUsageDiv()} -
-
- - - -
- - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - - - {getTokenFeeAmount('p')} - -
-
-
-
-
- ); -} -function UserLiquidityLineStyle2Mobile({ - switch_off, - set_switch_off, - getUsageDiv, -}: { - switch_off: boolean; - set_switch_off: any; - getUsageDiv: any; -}) { - const { - getLpt_id, - goYourLiquidityDetailPage, - goPoolDetailPage, - tokenMetadata_x_y, - fee, - getRate, - rate_need_to_reverse_display, - getRateMapTokens, - isInrange, - your_liquidity, - getTokenFeeAmount, - } = useContext(LiquidityContext); - const tokens = sort_tokens_by_base(tokenMetadata_x_y); - return ( -
-
-
-
-
- - -
- - {tokens[0]?.['symbol']}-{tokens[1]?.['symbol']} - -
- - ${your_liquidity || '-'} - -
-
-
- { - goYourLiquidityDetailPage('new window'); - }} - className="flex items-center bg-portfolioRainbowColor rounded-md gotham_bold text-xs text-white cursor-pointer px-1.5 py-0.5" - > - NFT #{getLpt_id()} - - { - goPoolDetailPage(); - }} - className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md cursor-pointer whitespace-nowrap py-0.5 px-1.5 ml-1.5" - > - DCL - -
-
- - - {getTokenFeeAmount('p')} - - { - set_switch_off(!switch_off); - }} - switch_off={switch_off} - > -
-
-
- -
-
- - - - - ${your_liquidity || '-'} - -
-
-
- - - -
- - - {isInrange ? ( - - ) : ( - - )} - -
-
-
- - {getRate(rate_need_to_reverse_display ? 'right' : 'left')} -{' '} - {getRate(rate_need_to_reverse_display ? 'left' : 'right')} - - - {getRateMapTokens()} - -
-
-
- - - - {getUsageDiv()} -
-
- - - -
- - - {getTokenFeeAmount('l') || '-'} - - - - {getTokenFeeAmount('r') || '-'} - -
-
-
-
- ); -} - interface IUserJoinedSeed { seed_id?: IUserJoinedSeedDetail; } diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 8cf7f2e51..1015d8616 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1862,7 +1862,7 @@ export default function AddYourLiquidityPageV3() { style={{ width: mobileDevice ? '' : '400px' }} className="flex-shrink-0 xs:w-full md:w-full" > -
+
{ + // if (mobileDevice) return; setHoverFeeBox(false); }} onMouseEnter={() => { + if (mobileDevice) return; setHoverFeeBox(true); }} + onClick={() => { + if (mobileDevice) { + setHoverFeeBox(!hoverFeeBox); + } + }} >
@@ -1974,7 +1981,7 @@ export default function AddYourLiquidityPageV3() { className="rounded-xl right-0 top-3 px-4 py-3 xs:px-2 md:px-2" style={{ border: '1.2px solid rgba(145, 162, 174, 0.2)', - width: '418px', + width: mobileDevice ? '300px' : '418px', background: 'linear-gradient(rgb(34, 47, 55) 0%, rgb(25, 34, 41) 100%)', }} @@ -1986,8 +1993,8 @@ export default function AddYourLiquidityPageV3() { />
{FEELIST.map((feeItem, index) => { @@ -2000,7 +2007,7 @@ export default function AddYourLiquidityPageV3() { switchSelectedFee(fee); }} key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + className={`relative xsm:w-full flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ tokenX && tokenY ? 'cursor-pointer' : '' } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ isNoPool @@ -2059,7 +2066,7 @@ export default function AddYourLiquidityPageV3() { >
-
+
{[SpotShape, CurveShape, BidAskShape].map( (Shape, index: number) => { let disabled = false; @@ -2071,7 +2078,7 @@ export default function AddYourLiquidityPageV3() { } return (
{ @@ -2123,8 +2130,8 @@ export default function AddYourLiquidityPageV3() { )}
{/* user chart part */} -
-
+
+
{/* chart area */} -
+
{ @@ -2522,6 +2533,7 @@ function PointsComponent() { radius={radius} config={{ radiusMode: priceRangeMode == 'by_radius', + svgWidth: is_mobile ? '330' : '', }} reverse={pair_is_reverse} > @@ -2534,7 +2546,10 @@ function PointsComponent() {
@@ -2542,7 +2557,7 @@ function PointsComponent() { )}
{/* set price range area */} -
+
{/* price range mode area */}
@@ -2591,7 +2606,7 @@ function PointsComponent() {
{/* content */} -
+
{/* target price input box */}
{/* chart area */}
-
+
{ setChartTab('liquidity'); @@ -3004,9 +3019,7 @@ function NoDataComponent() { defaultMessage="Set Price Range" /> - - {/* {getPair()} */} - +
@@ -3043,7 +3056,7 @@ function NoDataComponent() {
{/* content */} -
+
{/* target price input box */}
-
-
- - +
+
+
+ + +
+
+ {tokens[0]?.symbol}-{tokens[1]?.symbol} +
-
- - {tokens[0]?.symbol}-{tokens[1]?.symbol} - +
+ {tokens[0]?.symbol}-{tokens[1]?.symbol}
@@ -418,12 +421,6 @@ export default function PoolDetailV3() {
- -
@@ -442,7 +439,7 @@ export default function PoolDetailV3() { >
+(liquidity.part_farm_ratio || 0) == 0 ); - return ( - // - // - //
- //
- // Your Positions - // - // {user_liquidities_detail.length} - // - //
- //
- // - //
- //
- // {/* for Mobile */} - // {isMobile ? ( - //
- // {user_liquidities_detail.map( - // (liquidityDetail: UserLiquidityDetail, index: number) => { - // return ( - //
- // - // #{liquidityDetail.hashId} - // - //
- // Liquidity - // - // {displayLiqudityTvl(liquidityDetail)} - // - //
- //
- // Range - // - // {displayRange(liquidityDetail)} - // - //
- //
- // - // - // - // - // {displayLiqudityFee(liquidityDetail)} - // - //
- //
- // - // - // - // - // {displayFarmStatus(user_liquidities[index])} - // - //
- //
- // {is_in_farming(user_liquidities[index]) ? ( - // { - // e.stopPropagation(); - // go_farm(user_liquidities[index]); - // }} - // rounded="rounded-lg" - // px="px-0" - // py="py-1" - // style={{ minWidth: '5rem' }} - // className={`px-2 text-sm text-greenColor border-opacity-50 h-9 focus:outline-none`} - // > - //
- // - // - //
- //
- // ) : ( - // <> - // {operation == 'add' ? ( - // { - // e.stopPropagation(); - // hoverLine(liquidityDetail.hashId); - // setShowAddBox(true); - // }} - // color="#fff" - // borderRadius={'8px'} - // className={`px-2 h-9 text-center text-sm text-white focus:outline-none`} - // > - // - // - // ) : ( - // { - // e.stopPropagation(); - // hoverLine(liquidityDetail.hashId); - // setShowRemoveBox(true); - // }} - // color="#fff" - // className={`flex w-24 h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover`} - // > - // - // - // )} - // - // )} - //
- //
- // ); - // } - // )} - //
- // ) : ( - // // for Pc - //
- //
- // NFT ID - // - // - // - // - // - // - // - // - // - // {has_no_related_seed ? null : ( - // - // - // - // )} - // - //
- //
- // {user_liquidities_detail.map( - // (liquidityDetail: UserLiquidityDetail, index: number) => { - // return ( - //
{ - // hoverLine(liquidityDetail.hashId); - // }} - // // onMouseLeave={() => setHoverHashId('')} - // className={`grid grid-cols-${ - // has_no_related_seed ? 10 : 12 - // } gap-x-3 text-white text-base h-14 justify-center items-center px-6 ${ - // hoverHashId == liquidityDetail.hashId - // ? 'bg-chartBg bg-opacity-20' - // : '' - // }`} - // > - // - // #{liquidityDetail.hashId} - // - // - // {displayLiqudityTvl(liquidityDetail)} - // - // - // {displayRange(liquidityDetail)} - // - // - // {displayLiqudityFee(liquidityDetail)} - // - // {has_no_related_seed ? null : ( - // - // {displayFarmStatus(user_liquidities[index])} - // - // )} - //
- // {is_in_farming(user_liquidities[index]) ? ( - // { - // e.stopPropagation(); - // go_farm(user_liquidities[index]); - // }} - // rounded="rounded-lg" - // px="px-0" - // py="py-1" - // style={{ minWidth: '5rem' }} - // className={`w-full px-2 text-sm text-greenColor h-9 border-opacity-50 ${ - // hoverHashId == liquidityDetail.hashId - // ? '' - // : 'hidden' - // }`} - // > - //
- // - // - //
- //
- // ) : ( - // <> - // {operation == 'add' ? ( - // { - // e.stopPropagation(); - // setShowAddBox(true); - // }} - // color="#fff" - // borderRadius={'8px'} - // className={`px-2 h-9 text-center text-sm text-white focus:outline-none ${ - // hoverHashId == liquidityDetail.hashId - // ? '' - // : 'hidden' - // }`} - // > - // - // - // ) : ( - // { - // e.stopPropagation(); - // setShowRemoveBox(true); - // }} - // color="#fff" - // className={`flex h-9 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ - // hoverHashId == liquidityDetail.hashId - // ? '' - // : 'hidden' - // }`} - // > - // - // - // )} - // - // )} - //
- //
- // ); - // } - // )} - //
- //
- // )} - - // {operation == 'add' ? ( - //
- //
{ - // e.stopPropagation(); - // goAddLiqudityPage(); - // }} - // color="#fff" - // className={`flex items-center justify-center w-full h-10 mx-6 border border-dashed border-dclBorderColor rounded-lg text-sm text-primaryText cursor-pointer hover:bg-dclButtonBgColor hover:text-white focus:outline-none mt-7 xsm:mt-4`} - // > - // + - //
- //
- // ) : null} - // {operation == 'add' && showAddBox ? ( - // { - // setShowAddBox(false); - // }} - // tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} - // poolDetail={poolDetail} - // tokenPriceList={tokenPriceList} - // userLiquidity={getCurrentLiqudity(hoverHashId)} - // style={{ - // overlay: { - // backdropFilter: 'blur(15px)', - // WebkitBackdropFilter: 'blur(15px)', - // }, - // content: { - // outline: 'none', - // transform: 'translate(-50%, -50%)', - // }, - // }} - // > - // ) : null} - // {operation == 'remove' && showRemoveBox ? ( - // { - // setShowRemoveBox(false); - // }} - // listLiquidities={user_liquidities} - // tokenMetadata_x_y={[token_x_metadata, token_y_metadata]} - // poolDetail={poolDetail} - // tokenPriceList={tokenPriceList} - // userLiquidity={getCurrentLiqudity(hoverHashId)} - // style={{ - // overlay: { - // backdropFilter: 'blur(15px)', - // WebkitBackdropFilter: 'blur(15px)', - // }, - // content: { - // outline: 'none', - // transform: 'translate(-50%, -50%)', - // }, - // }} - // /> - // ) : null} - //
- //
- - operation == 'remove' && isOpen ? ( - - ) : null - ); + return operation == 'remove' && isOpen ? ( + + ) : null; } function Chart(props: any) { diff --git a/src/pages/poolsV3/YourLiquidityPageV3.tsx b/src/pages/poolsV3/YourLiquidityPageV3.tsx index 2a3fe0221..7040d85b6 100644 --- a/src/pages/poolsV3/YourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/YourLiquidityPageV3.tsx @@ -412,7 +412,7 @@ export default function YourLiquidityPageV3() {
) : null} {+v1LiquidityQuantity > 0 || showV1EmptyBar ? ( -
+
({v1LiquidityQuantity}) diff --git a/tailwind.config.js b/tailwind.config.js index 3ae0e1965..dd6e59ad4 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -408,7 +408,8 @@ module.exports = { chartHoverBoxBg: 'rgba(26, 39, 48, 1)', chartBorderColor: '#344451', proTabBgColor: '#324451', - dclTabBorderColor:'#3F4A52' + dclTabBorderColor:'#3F4A52', + dclYourLiquidityColor:'#283945', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From 66ddc1cbf601f67670214cf509fbd9d2cc238a4f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 17 Aug 2023 00:38:29 +0800 Subject: [PATCH 121/204] add mobile style --- src/components/farm/FarmsDclDetail.tsx | 14 +- src/components/icon/V3.tsx | 6 +- src/components/swap/SwapLimitOrderChart.tsx | 465 +++++++++------ src/pages/pools/DetailsPage.tsx | 96 +++- src/pages/poolsV3/PoolDetailV3.tsx | 608 ++++++++++++++------ tailwind.config.js | 1 + 6 files changed, 798 insertions(+), 392 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 9b6cb56a2..2ace54f71 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1452,8 +1452,8 @@ export default function FarmsDclDetail(props: {
-
-
+
+
{!isEnded && !canStake && ( -
+
{get_unavailable_text()}
)}
-
+
{!isEnded && ( @@ -1497,7 +1499,7 @@ export default function FarmsDclDetail(props: { minWidth="6rem" disabled={nft_unStake_loading ? true : false} onClick={batchUnStakeNFT} - className={`flex items-center justify-center h-8 px-4 ml-2 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ + className={`flex items-center xsm:flex-grow justify-center h-8 px-4 ml-2 text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ nft_unStake_loading ? 'opacity-40' : '' }`} > diff --git a/src/components/icon/V3.tsx b/src/components/icon/V3.tsx index bd7608fdd..366a02306 100644 --- a/src/components/icon/V3.tsx +++ b/src/components/icon/V3.tsx @@ -687,7 +687,7 @@ export const FarmBoardInDetailDCLPool = (props: any) => { { (); const [market_loading, set_market_loading] = useState(false); const [pair_is_reverse, set_pair_is_reverse] = useState(false); + const [show_view_all, set_show_view_all] = useState(false); const { dcl_pool_id } = useContext(SwapProContext); const GEARS = [15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1]; const [zoom, setZoom] = useState(GEARS[0]); @@ -48,6 +50,7 @@ export default function SwapLimitOrderChart() { const left_point = -800000; const right_point = 800000; const sellBoxRef = useRef(null); + const is_mobile = isMobile(); useEffect(() => { if (pool_id) { set_fetch_data_done(false); @@ -86,6 +89,14 @@ export default function SwapLimitOrderChart() { sellBoxRef.current.scrollTop = 10000; } }, [sellBoxRef, sell_list]); + useEffect(() => { + debugger; + if (show_view_all && is_mobile) { + document.documentElement.style.overflow = 'hidden'; + } else { + document.documentElement.style.overflow = 'auto'; + } + }, [show_view_all]); const [cur_pairs, cur_pairs_price_mode, cur_token_symbol, cur_pair_icons] = useMemo(() => { if (pool) { @@ -312,6 +323,29 @@ export default function SwapLimitOrderChart() { ); } } + function get_rate_element_mobile() { + if (pool) { + const { current_point, token_x_metadata, token_y_metadata } = pool; + const current_price_x = get_price_by_point(current_point); + const current_price_y = Big(current_price_x).gt(0) + ? Big(1).div(current_price_x).toFixed() + : '0'; + return ( +
+ + {switch_token == 'X' + ? formatPriceWithCommas(current_price_x) + : formatPriceWithCommas(current_price_y)} + + + {switch_token == 'X' + ? token_y_metadata.symbol + '/' + token_x_metadata.symbol + : token_x_metadata.symbol + '/' + token_y_metadata.symbol} + +
+ ); + } + } function marketRefresh() { set_market_loading(true); } @@ -346,7 +380,7 @@ export default function SwapLimitOrderChart() { GEARS, }} > -
+
{cur_pair_icons}
@@ -357,12 +391,140 @@ export default function SwapLimitOrderChart() {
{/* chart area */} -
+
{/* base data */} -
-
{get_rate_element()}
-
-
+
+
+ {get_rate_element()} +
+
+ {get_rate_element_mobile()} +
+
+
+
+
+ { + set_switch_token('X'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'X' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_x_metadata?.symbol} + + { + set_switch_token('Y'); + }} + className={`flex items-center justify-center cursor-pointer rounded-md px-1.5 py-0.5 text-xs ${ + switch_token == 'Y' + ? 'bg-proTabBgColor text-white' + : 'text-primaryText' + }`} + > + {pool?.token_y_metadata?.symbol} + +
+
+ {/* control button*/} +
+ {/*
+ +
*/} +
+ +
+
+ +
+ {/*
+ +
*/} +
+
+
{ + set_show_view_all(true); + }} + className="text-xs text-white px-2 py-1 border border-v3SwapGray border-opacity-20 rounded-md lg:hidden" + > + View All +
+
+
+ {/* chart */} + {is_empty ? ( +
+ + + Not enough data for the chart right now. + +
+ ) : ( + + )} +
+ {/* table area */} +
+ {is_mobile && show_view_all && ( +
{ + debugger; + set_show_view_all(false); + }} + >
+ )} +
+
+
+ Limit Orders +
+
- {/* control button*/} -
- {/*
- -
*/} -
+
+
+ Price + - -
-
+
+
+ Qty + - -
- {/*
+
+
+ + Total Qty + + - -
*/} + {cur_token_symbol} +
-
- {/* chart */} - {is_empty ? ( -
- - - Not enough data for the chart right now. - -
- ) : ( - - )} -
- {/* table area */} -
-
- Limit Orders -
-
-
- Price - - {cur_pairs} - -
-
- Qty - - {cur_token_symbol} - -
-
- - Total Qty - - - {cur_token_symbol} - -
-
- {is_empty ? ( -
- No order yet -
- ) : ( -
+ {is_empty ? (
- {sell_list?.map((item: IOrderPointItem, index) => { - return ( -
- - {formatPriceWithCommas(item.price)} - - - {formatNumber( - item.amount_x_readable || item.amount_y_readable - )} - - - {formatNumber( - item.accumulated_x_readable || - item.accumulated_y_readable - )} - -
- ); - })} + No order yet
-
- Market Pirce - -
-
- {buy_list?.map((item: IOrderPointItem, index) => { - return ( -
- - {formatPriceWithCommas(item.price)} - - - {formatNumber( - item.amount_x_readable || item.amount_y_readable - )} - - - {formatNumber( - item.accumulated_x_readable || - item.accumulated_y_readable - )} - -
- ); - })} + ) : ( +
+
+ {sell_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPriceWithCommas(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || + item.accumulated_y_readable + )} + +
+ ); + })} +
+
+ Market Pirce + +
+
+ {buy_list?.map((item: IOrderPointItem, index) => { + return ( +
+ + {formatPriceWithCommas(item.price)} + + + {formatNumber( + item.amount_x_readable || item.amount_y_readable + )} + + + {formatNumber( + item.accumulated_x_readable || + item.accumulated_y_readable + )} + +
+ ); + })} +
-
- )} + )} +
@@ -588,11 +701,12 @@ function OrderChart() { const [foucsOrderPoint, setFoucsOrderPoint] = useState(); const [side, setSide] = useState(); // CONST start - const svg_width = 600; + const svg_width = isMobile() ? 360 : 600; const svg_height = 400; const svg_padding = 40; const axisRightWidth = 60; const disFromHoverBoxToPointer = 20; + const is_mobile = isMobile(); // CONST end useEffect(() => { if (sell_list?.length || buy_list?.length) { @@ -1054,21 +1168,32 @@ function OrderChart() { if (offsetX > 380) { translate_x = offsetX - 235; } - d3.select('.hoverBox').attr( - 'style', - `visibility:visible;transform:translate(${translate_x}px, ${ - offsetY - disFromHoverBoxToPointer - }px)` - ); + if (is_mobile) { + d3.select('.hoverBox').attr('style', 'display:block'); + } else { + d3.select('.hoverBox').attr( + 'style', + `visibility:visible;transform:translate(${translate_x}px, ${ + offsetY - disFromHoverBoxToPointer + }px)` + ); + } } function hideCrossDot() { d3.select('.verticalDashLine').attr('opacity', '0'); d3.select('.horizontalDashLine').attr('opacity', '0'); d3.select('.dot').attr('opacity', '0'); - d3.select('.hoverBox').attr('style', `visibility:invisible`); + if (is_mobile) { + d3.select('.hoverBox').attr('style', `display:hidden`); + } else { + d3.select('.hoverBox').attr('style', `visibility:invisible`); + } } return ( -
+
{/* 横坐标 */} @@ -1134,7 +1259,7 @@ function OrderChart() { {/* hover上去的悬浮框 */} -
+
Side -
- @@ -1520,7 +1520,7 @@ export function RecentTransactions({ - + - - - + - - - + - - - - - + - - + + + + + + + ); + }); + + const renderLiquidityTransactions = liquidityTransactions.map((tx) => { + const swapIn = tokens[0]; + + const swapOut = tokens[1]; + + if (!swapIn || !swapOut) return null; + + const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_x); + const displayInAmount = + Number(AmountIn) < 0.01 && Number(AmountIn) > 0 + ? '<0.01' + : numberWithCommas(toPrecision(AmountIn, 6)); + + const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_y); + + const displayOutAmount = + Number(AmountOut) < 0.01 && Number(AmountOut) > 0 + ? '<0.01' + : numberWithCommas(toPrecision(AmountOut, 6)); + + const txLink = ( + + + + ); + + return ( + + + + + + + + ); + }); + + const renderLimitOrderTransactions = limitOrderTransactions.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.sell_token); + + const swapOut = tokens.find((t) => t.id !== tx.sell_token); + + if (!swapIn || !swapOut) return null; + let reverse = false; + const sort_tokens = sort_tokens_by_base([swapIn, swapOut]); + if (sort_tokens[0].id !== swapIn.id) { + reverse = true; + } + + const AmountIn = toReadableNumber(swapIn.decimals, tx.amount); + const displayInAmount = + Number(AmountIn) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountIn, 3)); + + const price = pointToPrice({ + tokenA: swapIn, + tokenB: swapOut, + point: + swapIn.id === pool_id.split('|')[0] + ? Number(tx.point) + : -Number(tx.point), + }); + const AmountOut = new Big(AmountIn).mul(price).toFixed(); + + const displayOutAmount = + Number(AmountOut) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(AmountOut, 3)); + + const txLink = ( + + + + ); + const display_price = reverse ? reverse_price(price) : price; + return ( + + + + + + + + + + + + ); + }); + + const renderTransactions = + tab === 'swap' + ? renderSwapTransactions + : tab === 'liquidity' + ? renderLiquidityTransactions + : renderLimitOrderTransactions; + return ( + <> +
+
+ +
+ +
+
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('swap'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('liquidity'); + }} + > + +
+ +
{ + e.preventDefault(); + e.stopPropagation(); + onChangeTab('limit_order'); + }} + > + +
+
+
+ +
+
- - {displayInAmount} - +
+ + {displayInAmount} + - - {toRealSymbol(swapIn.symbol)} - + + {toRealSymbol(swapIn.symbol)} + +
- - {displayOutAmount} - +
+ + {displayOutAmount} + - - {toRealSymbol(swapOut.symbol)} - + + {toRealSymbol(swapOut.symbol)} + +
- - {numberWithCommas(toPrecision(price, 4))} - +
+ + {numberWithCommas(toPrecision(price, 4))} + - - {reverse - ? `${toRealSymbol(swapOut.symbol)}/${toRealSymbol(swapIn.symbol)}` - : `${toRealSymbol(swapIn.symbol)}/${toRealSymbol( - swapOut.symbol - )}`} - + + {reverse + ? `${toRealSymbol(swapOut.symbol)}/${toRealSymbol( + swapIn.symbol + )}` + : `${toRealSymbol(swapIn.symbol)}/${toRealSymbol( + swapOut.symbol + )}`} + +
@@ -2713,13 +2740,17 @@ function Icon(props: { icon?: string; className?: string; style?: any }) { /> ); } +let timer: any; function LiquidityChart(props: any) { + const svgDefaultWidth = 750; const { data, chartDisplay, setChartDisplay } = props; const { poolDetail, depthData } = data; const [chartLoading, setChartLoading] = useState(true); const [noData, setNoData] = useState(true); const [rateDirection, setRateDirection] = useState(true); + const [svgWidth, setSvgWidth] = useState(svgDefaultWidth); const isMobile = isClientMobie(); + const refDom = useRef(null); useEffect(() => { if (poolDetail?.token_x_metadata) { if ( @@ -2771,12 +2802,25 @@ function LiquidityChart(props: any) { ); }, [poolDetail, rateDirection]); + useEffect(() => { + if (isMobile) return; + if (refDom.current) { + setSvgWidth(refDom.current.clientWidth || svgDefaultWidth); + window.onresize = () => { + clearTimeout(timer); + timer = setTimeout(() => { + setSvgWidth(refDom?.current?.clientWidth || svgDefaultWidth); + }, 50); + }; + } + }, [refDom.current]); function switchRate() { setRateDirection(!rateDirection); } return ( <>
diff --git a/src/services/indexer.ts b/src/services/indexer.ts index f7c10c211..fd967d88e 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -481,10 +481,10 @@ export interface DCLPoolFee { } export const getDCLTopBinFee = async (props: { - pool_id: string, - bin: number, - start_point: number, - end_point: number + pool_id: string; + bin: number; + start_point: number; + end_point: number; }): Promise => { const { pool_id, bin, start_point, end_point } = props; const result = await getDclPoolPoints(pool_id, bin, start_point, end_point); diff --git a/src/state/pool.ts b/src/state/pool.ts index 9237dabae..cd1f3a2e3 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -98,12 +98,16 @@ import { PoolInfo, get_pool } from '../services/swapV3'; import { useTokenPriceList } from './token'; import { isStablePool } from '../services/near'; import { getStablePoolDecimal } from '../pages/stable/StableSwapEntry'; -import { get_default_config_for_chart, get_custom_config_for_chart } from '../components/d3Chart/config'; -import { IChartItemConfig, IChartConfig } from '../components/d3Chart/interfaces'; -import { getPointByPrice, getPriceByPoint } from '../services/commonV3'; import { - formatPercentage -} from '../components/d3Chart/utils'; + get_default_config_for_chart, + get_custom_config_for_chart, +} from '../components/d3Chart/config'; +import { + IChartItemConfig, + IChartConfig, +} from '../components/d3Chart/interfaces'; +import { getPointByPrice, getPriceByPoint } from '../services/commonV3'; +import { formatPercentage } from '../components/d3Chart/utils'; const REF_FI_STABLE_POOL_INFO_KEY = `REF_FI_STABLE_Pool_INFO_VALUE_${ getConfig().STABLE_POOL_ID @@ -1426,24 +1430,25 @@ export const useDCLPoolTransaction = ({ }; }; -export const useDCLTopBinFee = ({ - pool, -}: { - pool:PoolInfo, -}) => { +export const useDCLTopBinFee = ({ pool }: { pool: PoolInfo }) => { const [topBinApr, setTopBinApr] = useState('-'); useEffect(() => { if (!pool) return; - const [bin, start_point, end_point ] = get_config_of_dcl_pool(pool); + const [bin, start_point, end_point] = get_config_of_dcl_pool(pool); getDCLTopBinFee({ pool_id: pool.pool_id, bin, start_point, - end_point + end_point, }).then((res) => { if (!res || ONLY_ZEROS.test(res.total_liquidity)) return; - const apr = - formatPercentage(new Big(res.total_fee).div(res.total_liquidity).mul(365).mul(100).toFixed()) + const apr = formatPercentage( + new Big(res.total_fee) + .div(res.total_liquidity) + .mul(365) + .mul(100) + .toFixed() + ); setTopBinApr(apr); }); }, [pool]); @@ -1451,21 +1456,24 @@ export const useDCLTopBinFee = ({ return topBinApr; }; -function get_config_of_dcl_pool(pool:PoolInfo) { +function get_config_of_dcl_pool(pool: PoolInfo) { const pool_id = pool.pool_id; - const { bin, rangeGear } = get_default_config_for_chart() as IChartItemConfig; + const { bin, rangeGear } = get_default_config_for_chart() as IChartItemConfig; const custom_config: IChartConfig = get_custom_config_for_chart(); const bin_final = custom_config[pool_id]?.bin || bin; const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; - const [price_l, price_r] = get_price_range_by_percent(rangeGear_final[0], pool); + const [price_l, price_r] = get_price_range_by_percent( + rangeGear_final[0], + pool + ); const point_l = get_point_by_price(price_l, pool); const point_r = get_point_by_price(price_r, pool); - return [bin_final, point_l, point_r] + return [bin_final, point_l, point_r]; } function get_price_range_by_percent( percent: number, - pool:PoolInfo, + pool: PoolInfo ): [string, string] { const { current_point } = pool; const p_l_r = percent / 100; @@ -1478,17 +1486,19 @@ function get_price_range_by_percent( return [price_l, price_r]; } -function get_price_by_point(point:number, pool:PoolInfo) { +function get_price_by_point(point: number, pool: PoolInfo) { const { token_x_metadata, token_y_metadata } = pool; - const decimalRate_point = Math.pow(10, token_x_metadata.decimals) / Math.pow(10, token_y_metadata.decimals); - const price = getPriceByPoint(point, decimalRate_point) + const decimalRate_point = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + const price = getPriceByPoint(point, decimalRate_point); return price; } -function get_point_by_price(price: string, pool:PoolInfo) { +function get_point_by_price(price: string, pool: PoolInfo) { const { point_delta, token_x_metadata, token_y_metadata } = pool; const decimalRate_point = Math.pow(10, token_y_metadata.decimals) / Math.pow(10, token_x_metadata.decimals); const point = getPointByPrice(point_delta, price, decimalRate_point); return point; -} \ No newline at end of file +} From d1c369f4f69a496c8e3239c9ae873dfd011985a3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 10 Aug 2023 11:50:23 +0800 Subject: [PATCH 110/204] update add liquidity ui --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 47 +++++++++++--------- 1 file changed, 26 insertions(+), 21 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 3dad2d09e..d062b4aff 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1921,7 +1921,7 @@ export default function AddYourLiquidityPageV3() { amount={tokenXAmount} changeAmount={changeTokenXAmount} currentSelectedPool={currentSelectedPool} - hidden={onlyAddYToken || invalidRange ? true : false} + disabled={onlyAddYToken || invalidRange ? true : false} > {token_amount_tip ? ( @@ -2163,17 +2163,6 @@ export default function AddYourLiquidityPageV3() { {currentSelectedPool && currentSelectedPool.pool_id && ( )} -
- - -
@@ -2200,6 +2189,11 @@ function PointsComponent() { isSignedIn, pair_is_reverse, + + onlyAddXToken, + onlyAddYToken, + invalidRange, + currentPoint, } = useContext(LiquidityProviderData); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' @@ -2704,11 +2698,17 @@ function PointsComponent() { {/* tip in foot */}
- *Only NEAR is needed in the price range you choose. + {onlyAddYToken && currentPoint != rightPoint - 1 + ? `*Only ${currentSelectedPool?.token_y_metadata?.symbol} is needed in the price range you choose.` + : ''} + {onlyAddXToken + ? `*Only ${currentSelectedPool?.token_x_metadata?.symbol} is needed in the price range you choose.` + : ''} + {invalidRange ? `The maket price is outside your price range.` : ''}
@@ -3185,7 +3185,7 @@ function InputAmount({ changeAmount, amount, currentSelectedPool, - hidden, + disabled, }: { token: TokenMetadata; balance: string; @@ -3193,7 +3193,7 @@ function InputAmount({ changeAmount: any; amount: string; currentSelectedPool: PoolInfo; - hidden?: Boolean; + disabled?: Boolean; }) { const [inputPrice, setInputPrice] = useState(''); const [showNearTip, setShowNearTip] = useState(false); @@ -3244,8 +3244,10 @@ function InputAmount({ return (
- {numberWithCommas(toPrecision(price, 4))} + {numberWithCommas(toPrecision(reverse_price(price), 4))} - {reverse + {toRealSymbol(swapIn.symbol)}/{toRealSymbol(swapOut.symbol)} + {/* {reverse ? `${toRealSymbol(swapOut.symbol)}/${toRealSymbol( swapIn.symbol )}` : `${toRealSymbol(swapIn.symbol)}/${toRealSymbol( swapOut.symbol - )}`} + )}`} */}
@@ -2365,18 +2367,12 @@ export function RecentTransactions({
- {numberWithCommas(toPrecision(reverse_price(price), 4))} + {numberWithCommas(toPrecision(display_price, 4))} - {toRealSymbol(swapIn.symbol)}/{toRealSymbol(swapOut.symbol)} - {/* {reverse - ? `${toRealSymbol(swapOut.symbol)}/${toRealSymbol( - swapIn.symbol - )}` - : `${toRealSymbol(swapIn.symbol)}/${toRealSymbol( - swapOut.symbol - )}`} */} + {toRealSymbol(sort_tokens?.[1]?.symbol)}/ + {toRealSymbol(sort_tokens?.[0]?.symbol)}
+ {displayInAmount} @@ -1455,7 +1455,7 @@ export function RecentTransactions({ + {displayOutAmount} @@ -1472,7 +1472,7 @@ export function RecentTransactions({ openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); }} > - + {tx.timestamp} {txLink} @@ -1505,7 +1505,7 @@ export function RecentTransactions({ return (
{renderTokens.map((renderToken, index) => { return ( @@ -1543,7 +1543,7 @@ export function RecentTransactions({ - + {tx.timestamp} {txLink} @@ -1566,15 +1566,15 @@ export function RecentTransactions({ return ( <> -
-
+
+
-
+
-
+
{tab === 'liquidity' && ( @@ -1661,7 +1661,47 @@ export function RecentTransactions({ maxHeight: '700px', }} > - {renderTx} + + + + + + + + + {renderTx} +
+ {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + +
@@ -1746,9 +1786,21 @@ export const MobileChartChangeButton = ({ return (
+ {showLiqudityButton ? ( + setChartDisplay('liquidity')} + className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 border-transparent px-2.5 ${ + chartDisplay === 'liquidity' + ? 'border-senderHot' + : 'border-transparent' + }`} + > + + + ) : null} setChartDisplay('tvl')} - className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 pr-2.5 ${ + className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 px-2.5 mx-3 ${ chartDisplay === 'tvl' ? 'border-senderHot' : 'border-transparent' }`} > @@ -1756,7 +1808,7 @@ export const MobileChartChangeButton = ({ setChartDisplay('volume')} - className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 px-2.5 ml-3 ${ + className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 px-2.5 ${ chartDisplay === 'volume' ? 'border-senderHot' : 'border-transparent' @@ -1764,18 +1816,6 @@ export const MobileChartChangeButton = ({ > - {showLiqudityButton ? ( - setChartDisplay('liquidity')} - className={`text-sm text-white text-opacity-60 pb-2.5 border-b-4 border-transparent px-2.5 ml-3 ${ - chartDisplay === 'liquidity' - ? 'border-senderHot' - : 'border-transparent' - }`} - > - - - ) : null}
diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 1463cff2f..6bf7047de 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -296,67 +296,69 @@ export default function PoolDetailV3() { }, ]} /> -
-
-
-
- - -
-
- {tokens[0]?.symbol}-{tokens[1]?.symbol} -
-
- -
-
-
- {tokens[0]?.symbol}-{tokens[1]?.symbol} +
+
+ {/* title for pc */} +
+
+
+
+ + +
-
- - :{' '} - - {poolDetail.fee / 10000}% - - - -
- - - - :{' '} - - {topBinApr} +
+
+ {tokens[0]?.symbol}-{tokens[1]?.symbol} +
+
+ + :{' '} + + {poolDetail.fee / 10000}% + - +
+
+ + + + :{' '} + + {topBinApr} + + +
- {isMobile && sole_seed && ( - 1} /> - )} + {isMobile && sole_seed && ( + 1} /> + )} +
- -
+
+ {sole_seed && ( + 1} /> + )} { e.preventDefault(); e.stopPropagation(); @@ -413,18 +415,8 @@ export default function PoolDetailV3() {
)} -
- {sole_seed && ( - 1} /> - )} -
-
-
- -
-
+ {/* title for mobile */} +
+
+
+
+ + +
+
+ {tokens[0]?.symbol}-{tokens[1]?.symbol} +
+
+ { + e.preventDefault(); + e.stopPropagation(); + showFullStart + ? handleRemoveFromWatchList() + : handleSaveWatchList(); + }} + style={{ + background: '#172534', + width: '30px', + height: '30px', + }} + > + {showFullStart ? ( +
+ + + +
+ ) : ( +
+ + +
+ )} +
+
+
+
+ + :{' '} + + {poolDetail.fee / 10000}% + + +
+
+ + + + :{' '} + + {topBinApr} + + +
+
+ {sole_seed && ( + 1} /> + )} +
+
@@ -1110,7 +1215,7 @@ function YourLiquidityBox(props: {
You have liquidity in farm, please unstake from{' '} -
- - - - - {getTotalLiquditiesFee()} - -
-
- {liquidities?.length > 1 ? ( - - {liquidities.length} NFTs + <> + {/* for pc */} +
+
+ + - ) : null} -
+ + {getTotalLiquditiesFee()} + +
+
+
+
+ + {display_amount_x} +
+
+ + {display_amount_y} +
+
-
-
-
+
+ } + /> +
+
+
+ {/* for mobile */} +
+
+ + + + + {getTotalLiquditiesFee()} + +
+
+
- {display_amount_x} + + {token_x_metadata.symbol} +
-
- - {display_amount_y} + {display_amount_x} +
+
+
+ + + {token_y_metadata.symbol} +
+ {display_amount_y}
-
-
+ ); } function get_unClaimed_fee_data( @@ -1654,7 +1805,7 @@ function Chart(props: any) { padding="px-7 py-5 xsm:px-4" bgcolor={'bg-transparent'} style={{ - height: isClientMobie() ? '390px' : '470px', + height: isClientMobie() ? 'auto' : '470px', }} > {chartDisplay === 'volume' ? ( @@ -1851,8 +2002,8 @@ export function RecentTransactions({ ); return ( -
+
{displayInAmount} @@ -1862,7 +2013,7 @@ export function RecentTransactions({ + {displayOutAmount} @@ -1872,9 +2023,9 @@ export function RecentTransactions({ + { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); }} @@ -1920,8 +2071,8 @@ export function RecentTransactions({ ); return ( -
+
{(tx.method_name.toLowerCase().indexOf('add') > -1 || tx.method_name.toLowerCase().indexOf('append') > -1) && @@ -1931,7 +2082,7 @@ export function RecentTransactions({ + {Big(AmountIn || 0).gt(0) ? ( <> @@ -1959,9 +2110,9 @@ export function RecentTransactions({ ) : null} + { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); }} @@ -2020,14 +2171,14 @@ export function RecentTransactions({ ); const display_price = reverse ? reverse_price(price) : price; return ( -
+
{tx.method_name.toLowerCase().indexOf('cancelled') > -1 && 'Cancel'} {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Place'} +
{displayInAmount} @@ -2039,7 +2190,7 @@ export function RecentTransactions({
+
{displayOutAmount} @@ -2051,7 +2202,7 @@ export function RecentTransactions({
+
{numberWithCommas(toPrecision(display_price, 4))} @@ -2064,9 +2215,9 @@ export function RecentTransactions({
+ { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); }} @@ -2089,15 +2240,15 @@ export function RecentTransactions({ : renderLimitOrderTransactions; return ( <> -
-
+
+
-
+
-
- {tab === 'liquidity' && ( - - )} + + + + {tab === 'swap' && ( + + )} + - {tab === 'limit_order' && ( - + )} - - {tab === 'limit_order' && ( - - )} + {tab === 'limit_order' && ( + + )} - {tab === 'limit_order' && ( - - )} - - - + +
+ {tab === 'liquidity' && ( + + )} - {tab === 'limit_order' && ( - - )} + {tab === 'limit_order' && ( + + )} - {tab === 'swap' && ( - - )} - - {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + - - + + + - -
- {renderTransactions} + + + + + + + {tab === 'limit_order' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + {renderTransactions} +
+ {tab === 'liquidity' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + + + +
@@ -2427,14 +2661,14 @@ function Icon(props: { icon?: string; className?: string; style?: any }) { } let timer: any; function LiquidityChart(props: any) { - const svgDefaultWidth = 750; const { data, chartDisplay, setChartDisplay } = props; const { poolDetail, depthData } = data; + const isMobile = isClientMobie(); + const svgDefaultWidth = isMobile ? '380' : 750; const [chartLoading, setChartLoading] = useState(true); const [noData, setNoData] = useState(true); const [rateDirection, setRateDirection] = useState(true); const [svgWidth, setSvgWidth] = useState(svgDefaultWidth); - const isMobile = isClientMobie(); const refDom = useRef(null); useEffect(() => { if (poolDetail?.token_x_metadata) { @@ -2546,7 +2780,11 @@ function LiquidityChart(props: any) {
diff --git a/tailwind.config.js b/tailwind.config.js index dd6e59ad4..da635bd15 100644 --- a/tailwind.config.js +++ b/tailwind.config.js @@ -410,6 +410,7 @@ module.exports = { proTabBgColor: '#324451', dclTabBorderColor:'#3F4A52', dclYourLiquidityColor:'#283945', + bottomBoxBorderColor:'#2F3A39', }, fontFamily: { sans: ['Poppins', ...defaultTheme.fontFamily.sans], From 59a03aced3947dde23c066e00106aa540a23e765 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 18 Sep 2023 22:39:52 +0800 Subject: [PATCH 122/204] fix bugs --- src/components/farm/FarmsDclDetail.tsx | 4 +- src/components/pool/RemovePoolV3.tsx | 7 ++- src/components/pool/YourLiquidityV2.tsx | 4 +- src/components/portfolio/Orders.tsx | 4 +- src/components/swap/SwapLimitOrderChart.tsx | 34 +++++++----- src/components/swap/swap.ts | 33 ++++++++++++ src/pages/Orderly/components/Common/Icons.tsx | 54 +++++++++++++++---- src/pages/SwapPage.tsx | 2 +- src/pages/pools/LiquidityPage.tsx | 2 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6 +-- src/pages/poolsV3/PoolDetailV3.tsx | 23 ++++---- src/services/commonV3.ts | 11 +++- src/state/swapV3.ts | 28 +++++++--- 13 files changed, 156 insertions(+), 56 deletions(-) create mode 100644 src/components/swap/swap.ts diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 2ace54f71..b368ab76c 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1527,7 +1527,7 @@ export default function FarmsDclDetail(props: { {/* unClaimed Rewards for Mobile */} -
-
+ */} {/* caculator */} {seedDclCalcVisible ? ( { batch_remove_liquidity, batch_update_liquidity, mint_liquidities, + }).then(() => { + sessionStorage.setItem('REMOVE_POOL_ID', pool_id); }); } function get_minimum_received_data() { @@ -966,10 +968,7 @@ export const RemovePoolV3 = (props: any) => { {min_received_x_amount} -
+
{ - openUrl('/myOrder'); + localStorage.setItem(SWAP_MODE_KEY, SWAP_MODE.LIMIT); + openUrl('/'); }} className="flex items-center justify-center text-xs text-v3SwapGray bg-selectTokenV3BgColor rounded-md px-1.5 cursor-pointer hover:text-white py-0.5" > diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index ebad30d02..cda702a83 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -90,7 +90,6 @@ export default function SwapLimitOrderChart() { } }, [sellBoxRef, sell_list]); useEffect(() => { - debugger; if (show_view_all && is_mobile) { document.documentElement.style.overflow = 'hidden'; } else { @@ -351,6 +350,7 @@ export default function SwapLimitOrderChart() { } // 缩小坐标轴区间范围 function zoomOut() { + if (is_empty) return; const targetPercent = GEARS.find((item) => item < zoom); if (targetPercent) { setZoom(targetPercent); @@ -359,6 +359,7 @@ export default function SwapLimitOrderChart() { } // 放大坐标轴区间范围 function zoomIn() { + if (is_empty) return; const GEARSCOPY: number[] = JSON.parse(JSON.stringify(GEARS)).reverse(); const targetPercent = GEARSCOPY.find((item) => item > zoom); if (targetPercent) { @@ -444,7 +445,7 @@ export default function SwapLimitOrderChart() {
*/}
{/* table area */} -
+
{is_mobile && show_view_all && (
{ - debugger; set_show_view_all(false); }} >
)}
- Market Pirce - + + Market Pirce + + +
+ + > + Refresh Market Price +
{ export function SpotShape() { return ( + + + + + + + + + + diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index 0e579690f..b792dee08 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -279,7 +279,7 @@ function SwapPage() { const [swapMode, setSwapMode] = useState( storageMode || SWAP_MODE.NORMAL ); - const dcl_pool_id = useDclPoolIdByUrl(); + const dcl_pool_id = useDclPoolIdByUrl('all'); useEffect(() => { if (swapMode === SWAP_MODE.LIMIT) { setLimitTokenTrigger(!limitTokenTrigger ? true : false); diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 8b56a4c70..e7d2d0488 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -3001,7 +3001,7 @@ function LiquidityPage_({
{allPoolsV2 - // .sort(poolv2ReSortingFunc) + .sort(poolv2ReSortingFunc) .filter(poolv2FilterFunc) .map((pool, i) => ( )} {index === 1 && ( )} {index === 2 && ( )} diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 6bf7047de..220f47499 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -2002,7 +2002,7 @@ export function RecentTransactions({ ); return ( -
{displayInAmount} @@ -2013,7 +2013,7 @@ export function RecentTransactions({ + {displayOutAmount} @@ -2023,7 +2023,7 @@ export function RecentTransactions({ + { @@ -2307,7 +2307,11 @@ export function RecentTransactions({
- + - - - - - - - ); - }); - - const renderLiquidityTransactions = liquidityTransactions.map((tx) => { - const swapIn = tokens[0]; - - const swapOut = tokens[1]; - - if (!swapIn || !swapOut) return null; - - const AmountIn = toReadableNumber(swapIn.decimals, tx.amount_x); - const displayInAmount = - Number(AmountIn) < 0.01 && Number(AmountIn) > 0 - ? '<0.01' - : numberWithCommas(toPrecision(AmountIn, 6)); - - const AmountOut = toReadableNumber(swapOut.decimals, tx.amount_y); - - const displayOutAmount = - Number(AmountOut) < 0.01 && Number(AmountOut) > 0 - ? '<0.01' - : numberWithCommas(toPrecision(AmountOut, 6)); - - const txLink = ( - - - - ); - - return ( - - - - - - - - ); - }); - - const renderLimitOrderTransactions = limitOrderTransactions.map((tx) => { - const swapIn = tokens.find((t) => t.id === tx.sell_token); - - const swapOut = tokens.find((t) => t.id !== tx.sell_token); - - if (!swapIn || !swapOut) return null; - let reverse = false; - const sort_tokens = sort_tokens_by_base([swapIn, swapOut]); - if (sort_tokens[0].id !== swapIn.id) { - reverse = true; - } - - const AmountIn = toReadableNumber(swapIn.decimals, tx.amount); - const displayInAmount = - Number(AmountIn) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(AmountIn, 3)); - - const price = pointToPrice({ - tokenA: swapIn, - tokenB: swapOut, - point: - swapIn.id === pool_id.split('|')[0] - ? Number(tx.point) - : -Number(tx.point), - }); - const AmountOut = new Big(AmountIn).mul(price).toFixed(); - - const displayOutAmount = - Number(AmountOut) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(AmountOut, 3)); - - const txLink = ( - - - - ); - const display_price = reverse ? reverse_price(price) : price; - return ( - - - - - - - - - - - - ); - }); - - const renderTransactions = - tab === 'swap' - ? renderSwapTransactions - : tab === 'liquidity' - ? renderLiquidityTransactions - : renderLimitOrderTransactions; - return ( - <> -
-
- -
- -
-
{ - e.preventDefault(); - e.stopPropagation(); - onChangeTab('swap'); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - onChangeTab('liquidity'); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - onChangeTab('limit_order'); - }} - > - -
-
-
- -
-
{tab === 'liquidity' && ( {tab === 'liquidity' && ( @@ -2377,7 +2383,9 @@ export function RecentTransactions({ )} - {chartLoading ? ( - - ) : null} {!chartLoading && noData ? ( ) : ( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 0d2f4967b..1a12d0189 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -205,7 +205,7 @@ export function useRemoveLiquidityUrlHandle() { useEffect(() => { if (txHash && isSignedIn) { checkTransaction(txHash).then((res: any) => { - const { transaction, receipts, receipts_outcome } = res; + const { transaction, receipts, status, receipts_outcome } = res; receipts_outcome?.[1]?.outcome?.status?.SuccessValue; const isNeth = transaction?.actions?.[0]?.FunctionCall?.method_name === 'execute'; @@ -220,7 +220,14 @@ export function useRemoveLiquidityUrlHandle() { methodName == 'batch_update_liquidity' || methodName == 'withdraw_asset' ) { - history.replace('/yourliquidity'); + const pool_id = sessionStorage.getItem('REMOVE_POOL_ID'); + sessionStorage.removeItem('REMOVE_POOL_ID'); + if (pool_id) { + const pool_name = get_pool_name(pool_id); + history.replace(`/poolV2/${pool_name}`); + } else { + history.replace('/yourliquidity'); + } } else { history.replace(`${location.pathname}`); } diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index d8bd4b4fa..3956caeca 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -15,7 +15,8 @@ import BigNumber from 'bignumber.js'; import { getDCLTopBinFee } from '../services/indexer'; import { list_pools } from '../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../services/wrap-near'; -import Big from 'big.js'; +import { getStorageTokenId } from '../components/swap/swap'; +import { wrapTokenId } from '../components/swap/LimitOrderCard'; export const useMyOrders = () => { const [activeOrder, setActiveOrder] = useState(); @@ -90,23 +91,34 @@ export const useAllPoolsV2 = () => { return allPools; }; -export const useDclPoolIdByUrl = () => { +export const useDclPoolIdByUrl = (source?: 'all' | 'url' | 'local') => { const [pool_id, set_pool_id] = useState(); const location = useLocation(); const hash = location.hash; useEffect(() => { - get_all_dcl_pools_by_url(); + get_dcl_pool_by_Ids(); }, [hash]); + const [in_id, out_id] = getStorageTokenId(); - async function get_all_dcl_pools_by_url() { + async function get_dcl_pool_by_Ids() { const dcl_pools: PoolInfo[] = await list_pools(); const [urlTokenIn, urlTokenOut] = decodeURIComponent( location.hash.slice(1) ).split('|'); - const url_token_in = - urlTokenIn == 'near' ? WRAP_NEAR_CONTRACT_ID : urlTokenIn; - const url_token_out = - urlTokenOut == 'near' ? WRAP_NEAR_CONTRACT_ID : urlTokenOut; + let url_token_in: string; + let url_token_out: string; + if (source == 'all') { + url_token_in = urlTokenIn || in_id; + url_token_out = urlTokenOut || out_id; + } else if (source == 'local') { + url_token_in = in_id; + url_token_out = out_id; + } else { + url_token_in = urlTokenIn; + url_token_out = urlTokenOut; + } + url_token_in = wrapTokenId(url_token_in); + url_token_out = wrapTokenId(url_token_out); const target: PoolInfo = dcl_pools.find((pool: PoolInfo) => { const { token_x, token_y } = pool; return ( From 0abc9a78e96c68dde405aea3f894c66726cde22c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 19 Sep 2023 10:34:17 +0800 Subject: [PATCH 123/204] update --- src/components/swap/SwapLimitOrderChart.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index cda702a83..c2c69ae09 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -116,7 +116,7 @@ export default function SwapLimitOrderChart() { return [ `${y_symbol}/${x_symbol}`, `${x_symbol}-${y_symbol}`, - `${y_symbol}`, + `${x_symbol}`, y_icons, ]; } else if (switch_token == 'Y') { @@ -132,7 +132,7 @@ export default function SwapLimitOrderChart() { return [ `${x_symbol}/${y_symbol}`, `${y_symbol}-${x_symbol}`, - `${x_symbol}`, + `${y_symbol}`, x_icons, ]; } From 07855ef0e4efc28e6872caa54c76207a41a4ea4d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 19 Sep 2023 21:50:46 +0800 Subject: [PATCH 124/204] split code --- src/components/pool/RemovePoolV3.tsx | 22 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 1261 +-------- src/pages/poolsV3/PoolDetailV3.tsx | 2424 +---------------- .../components/add/AddLiquidityButton.tsx | 228 ++ src/pages/poolsV3/components/add/Icon 6.tsx | 20 + .../poolsV3/components/add/InputAmount.tsx | 141 + .../components/add/IntegerInputComponent.tsx | 50 + .../components/add/NoDataComponent.tsx | 163 ++ .../components/add/PointInputComponent.tsx | 50 + .../components/add/PointsComponent.tsx | 595 ++++ .../poolsV3/components/detail/BaseData.tsx | 122 + src/pages/poolsV3/components/detail/Chart.tsx | 232 ++ .../poolsV3/components/detail/DetailFun.ts | 35 + src/pages/poolsV3/components/detail/Icon.tsx | 20 + .../components/detail/NoYourLiquditiesBox.tsx | 41 + .../components/detail/RelatedFarmsBox.tsx | 161 ++ .../components/detail/SelectLiquidityBox.tsx | 168 ++ .../poolsV3/components/detail/TablePool.tsx | 729 +++++ .../components/detail/UnclaimedFeesBox.tsx | 177 ++ .../poolsV3/components/detail/UserTabBox.tsx | 189 ++ .../components/detail/YourLiquidityBox.tsx | 635 +++++ src/pages/poolsV3/components/detail/type.ts | 16 + 22 files changed, 3846 insertions(+), 3633 deletions(-) create mode 100644 src/pages/poolsV3/components/add/AddLiquidityButton.tsx create mode 100644 src/pages/poolsV3/components/add/Icon 6.tsx create mode 100644 src/pages/poolsV3/components/add/InputAmount.tsx create mode 100644 src/pages/poolsV3/components/add/IntegerInputComponent.tsx create mode 100644 src/pages/poolsV3/components/add/NoDataComponent.tsx create mode 100644 src/pages/poolsV3/components/add/PointInputComponent.tsx create mode 100644 src/pages/poolsV3/components/add/PointsComponent.tsx create mode 100644 src/pages/poolsV3/components/detail/BaseData.tsx create mode 100644 src/pages/poolsV3/components/detail/Chart.tsx create mode 100644 src/pages/poolsV3/components/detail/DetailFun.ts create mode 100644 src/pages/poolsV3/components/detail/Icon.tsx create mode 100644 src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx create mode 100644 src/pages/poolsV3/components/detail/RelatedFarmsBox.tsx create mode 100644 src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx create mode 100644 src/pages/poolsV3/components/detail/TablePool.tsx create mode 100644 src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx create mode 100644 src/pages/poolsV3/components/detail/UserTabBox.tsx create mode 100644 src/pages/poolsV3/components/detail/YourLiquidityBox.tsx create mode 100644 src/pages/poolsV3/components/detail/type.ts diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index c8d31087c..78ccff059 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -701,12 +701,28 @@ export const RemovePoolV3 = (props: any) => { } const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; const is_mobile = isMobile(); - const cardWidth = is_mobile ? '95vw' : '550px'; + const cardWidth = is_mobile ? '100vw' : '550px'; return ( - + {/* Title */}
diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index a6ea696f6..7558a0599 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1,28 +1,8 @@ -import React, { - useState, - useContext, - useEffect, - useRef, - createContext, -} from 'react'; +import React, { useState, useContext, useEffect, createContext } from 'react'; -import { - ReturnIcon, - AddButton, - ReduceButton, - BoxDarkBg, - SideIcon, - InvalidIcon, - WarningIcon, - EmptyIcon, -} from '~components/icon/V3'; -import { FormattedMessage, useIntl } from 'react-intl'; +import { ReturnIcon, WarningIcon } from '~components/icon/V3'; +import { FormattedMessage } from 'react-intl'; import { useHistory } from 'react-router-dom'; -import { - GradientButton, - ButtonTextWrapper, - ConnectToNearBtn, -} from '~components/button/Button'; import { useTriTokens, useWhitelistTokens } from '../../state/token'; import { useTriTokenIdsOnRef } from '../../services/aurora/aurora'; import { @@ -31,40 +11,24 @@ import { ftGetTokenMetadata, } from '../../services/ft-contract'; import { getBoostTokenPrices } from '../../services/farm'; -import { useTokenBalances, useDepositableBalance } from '../../state/token'; +import { useDepositableBalance } from '../../state/token'; import Loading from '~components/layout/Loading'; -import { - list_pools, - add_liquidity, - create_pool, - PoolInfo, - batch_add_liquidity, -} from '../../services/swapV3'; +import { list_pools, PoolInfo } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; import { - getPriceByPoint, - getPointByPrice, CONSTANT_D, FEELIST, - POINTDELTAMAP, DEFAULTSELECTEDFEE, - POINTLEFTRANGE, - POINTRIGHTRANGE, useAddLiquidityUrlHandle, get_pool_id, get_pool_name, - openUrl, - getBinPointByPrice, getBinPointByPoint, get_l_amount_by_condition, UserLiquidityInfo, - reverse_price, sort_tokens_by_base, - getSlotPointByPoint, } from '../../services/commonV3'; import { formatWithCommas, - toPrecision, toReadableNumber, toNonDivisibleNumber, scientificNotationToString, @@ -74,10 +38,6 @@ import { import { WalletContext } from '../../utils/wallets-integration'; import _, { forEach, set } from 'lodash'; import BigNumber from 'bignumber.js'; -import { toRealSymbol } from '../../utils/token'; -import ReactTooltip from 'react-tooltip'; -import { getURLInfo } from '../../components/layout/transactionTipPopUp'; -import { BlueCircleLoading } from '../../components/layout/Loading'; import { isMobile } from '../../utils/device'; import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; import Big from 'big.js'; @@ -97,7 +57,6 @@ import { import { get_custom_config_for_chart, get_default_config_for_chart, - RADIUS_DEFAULT_NUMBER, max_nft_divisional_per_side, } from '../../components/d3Chart/config'; import { @@ -105,7 +64,12 @@ import { IChartConfig, } from '../../components/d3Chart/interfaces'; import { isInvalid } from '../../components/d3Chart/utils'; -const LiquidityProviderData = createContext(null); +import { PointsComponent } from './components/add/PointsComponent'; +import { AddLiquidityButton } from './components/add/AddLiquidityButton'; +import { NoDataComponent } from './components/add/NoDataComponent'; +import { InputAmount } from './components/add/InputAmount'; + +export const LiquidityProviderData = createContext(null); export default function AddYourLiquidityPageV3() { const [tokenX, setTokenX] = useState(null); const [tokenY, setTokenY] = useState(null); @@ -1749,7 +1713,6 @@ export default function AddYourLiquidityPageV3() { } } if (!refTokens || !triTokens || !triTokenIds) return ; - console.log('999999999999-pair_is_reverse--9999999999', pair_is_reverse); return ( ); } -function PointsComponent() { - const { - binNumber, - setBinNumber, - currentSelectedPool, - tokenX, - tokenY, - set_token_amount_tip, - - pointChange, - liquidityShape, - - SLOT_NUMBER, - BIN_WIDTH, - - switch_pool_loading, - - isSignedIn, - pair_is_reverse, - - onlyAddXToken, - onlyAddYToken, - invalidRange, - currentPoint, - } = useContext(LiquidityProviderData); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const [radius, setRadius] = useState(); - const [targetCustomPrice, setTargetCustomPrice] = useState(''); - const [leftCustomPrice, setLeftCustomPrice] = useState(''); - const [rightCustomPrice, setRightCustomPrice] = useState(''); - const [targetPoint, setTargetPoint] = useState(); - - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [targetInputStatus, setTargetInputStatus] = useState(false); - - const [leftPoint, setLeftPoint] = useState(); - const [rightPoint, setRightPoint] = useState(); - - const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - - const token_x_decimals = tokenX.decimals; - const token_y_decimals = tokenY.decimals; - - // init - useEffect(() => { - if (currentSelectedPool?.pool_id && !switch_pool_loading) { - const { current_point } = currentSelectedPool; - let left_point, right_point; - const targetPoint = get_slot_point_by_point(current_point); - if (pair_is_reverse) { - left_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; - } else { - right_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; - } - setTargetPoint(targetPoint); - setRadius(RADIUS_DEFAULT_NUMBER); - setLeftPoint(left_point); - setRightPoint(right_point); - setPriceRangeMode('by_range'); - setChartTab('liquidity'); - } - }, [currentSelectedPool, switch_pool_loading]); - - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - // effect bin - let diff; - if (pair_is_reverse) { - diff = leftPoint - rightPoint; - } else { - diff = rightPoint - leftPoint; - } - const bin_number_temp = diff / BIN_WIDTH; - setBinNumber(bin_number_temp); - // effect right area - if (pair_is_reverse) { - pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); - } else { - pointChange({ leftPoint, rightPoint }); - } - } - console.log( - '00000000--leftPoint, rightPoint--00000000', - leftPoint, - rightPoint - ); - }, [leftPoint, rightPoint, BIN_WIDTH]); - - useEffect(() => { - if ( - liquidityShape == 'Spot' && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) - ) { - if (pair_is_reverse) { - pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); - } else { - pointChange({ leftPoint, rightPoint }); - } - } - }, [liquidityShape]); - - // clean tip - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - set_token_amount_tip(null); - } - }, [liquidityShape, leftPoint, rightPoint]); - - // to change bin --> to get proper right point --->for proper bin - function changeBin(bin: number) { - let appropriate_right_point, appropriate_bin_number; - if (pair_is_reverse) { - appropriate_right_point = leftPoint - BIN_WIDTH * bin; - if (appropriate_right_point < POINTLEFTRANGE) { - appropriate_right_point = POINTLEFTRANGE; - } - appropriate_bin_number = - (leftPoint - appropriate_right_point) / BIN_WIDTH; - } else { - appropriate_right_point = leftPoint + BIN_WIDTH * bin; - if (appropriate_right_point > POINTRIGHTRANGE) { - appropriate_right_point = POINTRIGHTRANGE; - } - appropriate_bin_number = - (appropriate_right_point - leftPoint) / BIN_WIDTH; - } - setRightPoint(appropriate_right_point); - setBinNumber(appropriate_bin_number); - } - - // to change radius-->to get proper left point and right point --->for proper radius - function changeRadius(radius: number) { - let appropriate_left_point, appropriate_right_point, appropriate_radius; - if (pair_is_reverse) { - appropriate_left_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * radius - ); - appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - appropriate_radius = - (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); - } else { - appropriate_right_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * radius - ); - appropriate_left_point = get_bin_point_by_point( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - appropriate_radius = - (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); - } - - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - setRadius(appropriate_radius); - } - // to change targetPrice-->o get proper left point and right point--->for proper targetPrice - function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_left_point, - appropriate_right_point, - appropriate_target_point; - if (pair_is_reverse) { - appropriate_target_point = get_point_by_price(reverse_price(price)); - appropriate_left_point = get_bin_point_by_point( - appropriate_target_point + BIN_WIDTH * radius - ); - appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - // appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; - } else { - appropriate_target_point = get_point_by_price(price); - appropriate_right_point = get_bin_point_by_point( - appropriate_target_point + BIN_WIDTH * radius - ); - appropriate_left_point = get_bin_point_by_point( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - // appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; - } - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - return appropriate_target_point; - } - function getLeftPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(leftPoint) - ) { - let price; - if (pair_is_reverse) { - price = reverse_price(get_price_by_point(leftPoint)); - } else { - price = get_price_by_point(leftPoint); - } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getRightPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(rightPoint) - ) { - let price; - if (pair_is_reverse) { - price = reverse_price(get_price_by_point(rightPoint)); - } else { - price = get_price_by_point(rightPoint); - } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getTargetPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(targetPoint) - ) { - let price; - if (pair_is_reverse) { - price = reverse_price(get_price_by_point(targetPoint)); - } else { - price = get_price_by_point(targetPoint); - } - - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function get_point_by_price(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate_point = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const point = getPointByPrice(point_delta, price, decimalRate_point); - return point; - } - function get_price_by_point(point: number) { - const decimalRate_price = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - return getPriceByPoint(point, decimalRate_price); - } - function get_bin_point_by_price(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const appropriate_point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return appropriate_point; - } - function get_bin_point_by_point(point: number) { - const { point_delta } = currentSelectedPool; - return getBinPointByPoint(point_delta, SLOT_NUMBER, point); - } - function get_slot_point_by_point(point: number) { - const { point_delta } = currentSelectedPool; - return getSlotPointByPoint(point_delta, point); - } - function getPair() { - if (pair_is_reverse) { - return `(${tokenX.symbol}/${tokenY.symbol})`; - } else { - return `(${tokenY.symbol}/${tokenX.symbol})`; - } - } - const is_mobile = isMobile(); - return ( -
- {/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - if (isSignedIn) { - setChartTab('yours'); - } - }} - > - Yours - -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( - - )} -
- {isSignedIn && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- {/* set price range area */} -
- {/* price range mode area */} -
-
- - - - {getPair()} - -
- -
- { - setPriceRangeMode('by_range'); - }} - > - - - { - setPriceRangeMode('by_radius'); - changeRadius(radius); - }} - > - - -
-
- {/* content */} -
- {/* target price input box */} -
- - - - -
- - {/* radius input box */} -
- - - - -
- - {/* min price input box */} -
- - - - { - if (pair_is_reverse) { - return get_bin_point_by_price(reverse_price(price)); - } else { - return get_bin_point_by_price(price); - } - }} - disbaled={priceRangeMode === 'by_radius'} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - setPoint={setLeftPoint} - point={leftPoint} - > -
- - {/* max price input box */} -
- - - - { - if (pair_is_reverse) { - return get_bin_point_by_price(reverse_price(price)); - } else { - return get_bin_point_by_price(price); - } - }} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - setPoint={setRightPoint} - point={rightPoint} - disbaled={priceRangeMode === 'by_radius'} - > -
- - {/* bin number input box */} -
- - - - -
-
- {/* tip in foot */} -
- {onlyAddYToken && currentPoint != rightPoint - 1 - ? `*Only ${currentSelectedPool?.token_y_metadata?.symbol} is needed in the price range you choose.` - : ''} - {onlyAddXToken - ? `*Only ${currentSelectedPool?.token_x_metadata?.symbol} is needed in the price range you choose.` - : ''} - {invalidRange ? : ''} -
-
-
- ); -} -/** - * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 传递的时候只传实际使用值 - * @returns - */ -function AddLiquidityButton() { - const { - currentSelectedPool, - tokenX, - tokenY, - liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - getLiquiditySpot, - getLiquidityForCurveAndBidAskMode, - } = useContext(LiquidityProviderData); - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - - function addLiquiditySpot() { - setAddLiquidityButtonLoading(true); - const new_liquidity = getLiquiditySpot(); - add_liquidity(new_liquidity); - } - function addLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - nftList = getLiquidityForCurveAndBidAskMode(); - if (!nftList) { - setAddLiquidityButtonLoading(false); - return; - } - /** - * 计算出 nftList token x tokeny 的数量,这是需要的总数量 - * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 - * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 - */ - let last_total_needed_token_x_amount = Big(0); - let last_total_needed_token_y_amount = Big(0); - nftList.forEach((nft: IAddLiquidityInfo) => { - const { amount_x, amount_y } = nft; - last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( - amount_x || 0 - ); - last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( - amount_y || 0 - ); - }); - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: last_total_needed_token_x_amount.toFixed(), - amount_y: last_total_needed_token_y_amount.toFixed(), - }); - } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - } - function getButtonText() { - let txt: any = ( - - ); - if (!currentSelectedPool?.pool_id) { - txt = ; - } else if (invalidRange) { - txt = ( - - ); - } else if (onlyAddXToken && +tokenXAmount == 0) { - txt = ( - - ); - } else if (onlyAddYToken && +tokenYAmount == 0) { - txt = ( - - ); - } else if ( - !onlyAddXToken && - !onlyAddYToken && - (+tokenXAmount == 0 || +tokenYAmount == 0) - ) { - txt = ( - - ); - } else if ( - +tokenXAmount > 0 && - new BigNumber(tokenXAmount).isGreaterThan( - getMax(tokenX, tokenXBalanceFromNear) - ) - ) { - txt = ( - - ); - } else if ( - +tokenYAmount > 0 && - new BigNumber(tokenYAmount).isGreaterThan( - getMax(tokenY, tokenYBalanceFromNear) - ) - ) { - txt = ( - - ); - } - return txt; - } - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } else if (onlyAddYToken) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - return !(condition1 && condition2); - } - const isAddLiquidityDisabled = getButtonStatus(); - - const add_lp_func = - liquidityShape === 'Spot' - ? addLiquiditySpot - : addLiquidityForCurveAndBidAskMode; - - return ( -
- {isSignedIn ? ( - - <>{getButtonText()}} - /> - - ) : ( - - )} -
- ); -} - -function NoDataComponent() { - const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - return ( -
- {/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - if (isSignedIn) { - setChartTab('yours'); - } - }} - > - Yours - -
-
- Oops! The Pool doesn’t exist -
-
- {/* set price range area */} -
- {/* price range mode area */} -
-
- - - -
- -
- { - setPriceRangeMode('by_range'); - }} - > - - - { - setPriceRangeMode('by_radius'); - }} - > - - -
-
- {/* content */} -
- {/* target price input box */} -
- - - - - 0 - -
- - {/* radius input box */} -
- - - - - 0 - -
- - {/* min price input box */} -
- - - - - 0 - -
- - {/* max price input box */} -
- - - - - 0 - -
-
-
-
- ); -} -function PointInputComponent({ - handlePriceToAppropriatePoint, - customPrice, - setCustomPrice, - - getPrice, - point, - setPoint, - - inputStatus, - setInputStatus, - disbaled, -}: any) { - return ( -
- { - setInputStatus(false); - if (customPrice) { - const appropriate_point_temp = - handlePriceToAppropriatePoint(customPrice); - setPoint(appropriate_point_temp); - } else { - setPoint(point); - } - }} - disabled={disbaled} - value={inputStatus ? customPrice : getPrice()} - onChange={({ target }) => { - setInputStatus(true); - const inputPrice = target.value; - if (Big(target.value || 0).lt(0)) { - setCustomPrice('0'); - } else { - setCustomPrice(inputPrice); - } - }} - /> -
- ); -} -export function IntegerInputComponent({ - value, - setValue, - disabled, - triggerByValue, -}: any) { - const removeLeadingZeros = (s: string) => { - const oldLen = s.length; - s = s.replace(/^0+/, ''); - - if (s.length === 0 && oldLen > 0) { - s = '0'; - } - - return s; - }; - - const handleChange = (val: string) => { - val = val.replace(/[^\d]/g, ''); - val = removeLeadingZeros(val); - setValue(val); - if (val) { - triggerByValue(val); - } - }; - - return ( -
- { - if (!target.value) { - setValue(1); - triggerByValue(1); - } - }} - onChange={({ target }) => { - handleChange(target.value); - }} - /> -
- ); -} -function OneSide({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InvalidRange({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InputAmount({ - token, - balance, - tokenPriceList, - changeAmount, - amount, - currentSelectedPool, - disabled, -}: { - token: TokenMetadata; - balance: string; - tokenPriceList: Record; - changeAmount: any; - amount: string; - currentSelectedPool: PoolInfo; - disabled?: Boolean; -}) { - const [inputPrice, setInputPrice] = useState(''); - const [showNearTip, setShowNearTip] = useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - useEffect(() => { - const price = token ? tokenPriceList[token.id]?.price : ''; - if (price && amount) { - setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); - } else { - setInputPrice(''); - } - if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { - const difference = new BigNumber(maxBalance).minus(amount); - const b = difference.toFixed(); - const r = difference.isLessThan(0); - if (r) { - setShowNearTip(true); - } else { - setShowNearTip(false); - } - } else { - setShowNearTip(false); - } - }, [amount, token, tokenPriceList.length]); - function getBalance() { - let r = '0'; - if (token && balance) { - r = formatWithCommas(toPrecision(balance.toString(), 3)); - } - return isSignedIn ? r : '-'; - } - function showCurrentPrice() { - if (isNoPool) { - return '$-'; - } else if (inputPrice) { - return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); - } - return '$-'; - } - const maxBalance = - token?.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - const isNoPool = !currentSelectedPool?.pool_id; - return ( -
-
-
- { - changeAmount(target.value); - }} - /> - - {token ? toRealSymbol(token.symbol) : 'Selet Token'} - -
-
- {showCurrentPrice()} -
- - :{' '} - { - if (disabled) return; - changeAmount(maxBalance); - }} - > - {getBalance()} - - -
-
-
- {showNearTip && !isNoPool ? ( -
- - -
- ) : null} -
- ); -} diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 220f47499..9ee9a5616 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -1,53 +1,26 @@ import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; -import { useLocation, useParams } from 'react-router-dom'; -import { Card } from '~components/card/Card'; -import { Link } from 'react-router-dom'; -import { - toPrecision, - toReadableNumber, - toInternationalCurrencySystem, - formatWithCommas, -} from '../../utils/numbers'; -import { useClientMobile, isClientMobie } from '../../utils/device'; -import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; -import { isMobile } from '~utils/device'; -import { toRealSymbol } from '~utils/token'; +import { useParams } from 'react-router-dom'; +import { isClientMobie } from '../../utils/device'; import { useHistory } from 'react-router'; import { BigNumber } from 'bignumber.js'; import { FormattedMessage, useIntl } from 'react-intl'; -import { PoolTabV3 } from '../../components/pool/PoolTabV3'; import { BreadCrumb } from '~components/layout/BreadCrumb'; import { get_pool, PoolInfo, list_liquidities, get_liquidity, - get_pool_marketdepth, - claim_all_liquidity_fee, - get_metadata, - pointToPrice, } from '~services/swapV3'; import { UserLiquidityInfo, - getPriceByPoint, - drawChartData, - CONSTANT_D, - getXAmount_per_point_by_Lx, - getYAmount_per_point_by_Ly, TOKEN_LIST_FOR_RATE, allocation_rule_liquidities, get_matched_seeds_for_dcl_pool, get_all_seeds, - displayNumberToAppropriateDecimals, - getEffectiveFarmList, sort_tokens_by_base, get_pool_id, get_pool_name, - openUrl, - get_account_24_apr, - get_token_amount_in_user_liquidities, useRemoveLiquidityUrlHandle, - reverse_price, } from '~services/commonV3'; import { ftGetTokensMetadata } from '../../services/ft-contract'; import { @@ -55,84 +28,31 @@ import { WatchListStartFull, } from '../../components/icon/WatchListStar'; import Loading from '~components/layout/Loading'; -import { useTokenPriceList } from '../../state/token'; -import { - getBoostTokenPrices, - FarmBoost, - Seed, - get_seed, -} from '../../services/farm'; +import { getBoostTokenPrices, Seed, get_seed } from '../../services/farm'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { WalletContext } from '../../utils/wallets-integration'; import { - addLiquidityToPool, addPoolToWatchList, getWatchListFromDb, - Pool, - PoolDetails, removePoolFromWatchList, } from '~services/pool'; import ReactTooltip from 'react-tooltip'; -import { TokenLinks } from '~components/tokens/Token'; -import { FiArrowUpRight } from 'react-icons/fi'; -import { - VolumeChart, - TVLChart, - ChartType, - ChartChangeButton, - MobileChartChangeButton, -} from '../pools/DetailsPage'; -import { BlueCircleLoading } from '../../components/layout/Loading'; -import { ChartNoData } from '~components/icon/ChartNoData'; -import { - GradientButton, - OprationButton, - ButtonTextWrapper, - BorderButton, - SolidButton, -} from '~components/button/Button'; -import { RemovePoolV3 } from '~components/pool/RemovePoolV3'; -import { AddPoolV3 } from '~components/pool/AddPoolV3'; -import Modal from 'react-modal'; -import { ModalClose } from '~components/icon'; -import { - useV3VolumeChart, - useV3TvlChart, - useDCLPoolTransaction, - useDCLTopBinFee, -} from '~state/pool'; -import { getV3Pool24VolumeById } from '~services/indexer'; -import { - list_farmer_seeds, - list_seed_farms, - UserSeedInfo, -} from '../../services/farm'; +import { useDCLTopBinFee } from '~state/pool'; +import { list_farmer_seeds } from '../../services/farm'; import getConfig from '../../services/config'; -import { - SwitchButtonIcon, - NoLiquidityIcon, - FarmBoardInDetailPool, - Fire, - JumpLinkIcon, - FarmBoardInDetailDCLPool, -} from '../../components/icon/V3'; import _ from 'lodash'; -import { PoolRPCView } from '../../services/api'; -import { FarmStampNew, FarmStampNewDCL } from '../../components/icon/FarmStamp'; -import { numberWithCommas } from '~pages/Orderly/utiles'; -import { HiOutlineExternalLink } from 'react-icons/hi'; -import Big from 'big.js'; -import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; -import DclChart from '../../components/d3Chart/DclChart'; -import { IDCLAccountFee } from '../../components/d3Chart/interfaces'; -import { - formatPercentage, - formatWithCommas_usd, - formatNumber, -} from '../../components/d3Chart/utils'; -import { getDCLAccountFee } from '../../services/indexer'; - +import { FarmStampNewDCL } from '../../components/icon/FarmStamp'; +import { UserButtonBox } from './components/detail/UserTabBox'; +import { ParamTypes } from './components/detail/type'; +import { YourLiquidityBox } from './components/detail/YourLiquidityBox'; +import { UnclaimedFeesBox } from './components/detail/UnclaimedFeesBox'; +import { RelatedFarmsBox } from './components/detail/RelatedFarmsBox'; +import { Chart } from './components/detail/Chart'; +import { TablePool } from './components/detail/TablePool'; +import { BaseData } from './components/detail/BaseData'; +import { NoYourLiquditiesBox } from './components/detail/NoYourLiquditiesBox'; const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); + export default function PoolDetailV3() { const { id } = useParams(); let pool_id_from_url: string; @@ -285,7 +205,7 @@ export default function PoolDetailV3() { ]); return ( <> -
+
- {!isSignedIn || - (user_liquidities && user_liquidities.length == 0) ? ( - - ) : ( - <> - {isMobile ? ( - <> - - - ) : ( - <> - - - - )} - - )} + {!isMobile ? ( + !isSignedIn || + (user_liquidities && user_liquidities.length == 0) ? ( + + ) : ( + <> + + + + ) + ) : null} +
- - ); -} -function UserTabBox(props: { - poolDetail: PoolInfo; - liquidities: UserLiquidityInfo[]; - tokenPriceList: any; - matched_seeds: Seed[]; -}) { - const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; - const [tabActive, setTabActive] = useState(1); - function switchTab(tabIndex: number) { - setTabActive(tabIndex); - } - return ( -
-
-
{ - switchTab(1); - }} - > - - - - -
-
{ - switchTab(2); - }} - > - - - - -
-
- {tabActive == 1 ? ( - - ) : ( - - )} -
- ); -} -function YourLiquidityBox(props: { - poolDetail: PoolInfo; - liquidities: UserLiquidityInfo[]; - tokenPriceList: any; - matched_seeds: Seed[]; -}) { - const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; - const [user_liquidities_detail, set_user_liquidities_detail] = useState< - UserLiquidityDetail[] - >([]); - const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); - const [accountAPR, setAccountAPR] = useState(''); - const [earned_fee, set_earned_fee] = useState(''); - const [earned_fee_x_amount, set_earned_fee_x_amount] = useState(); - const [earned_fee_y_amount, set_earned_fee_y_amount] = useState(); - const [operationType, setOperationType] = useState('add'); - const [hover, setHover] = useState(false); - const [noReverseRange, setNoReverseRange] = useState(true); - const [removeButtonTip, setRemoveButtonTip] = useState(false); - const [is_in_farming, set_is_in_farming] = useState(false); - const [is_in_farming_done, set_is_in_farming_done] = useState(false); - const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; - const { accountId } = useWalletSelector(); - const history = useHistory(); - let pair_is_reverse = false; - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { - pair_is_reverse = true; - } - useEffect(() => { - if (liquidities) { - const temp_list: UserLiquidityDetail[] = []; - liquidities.forEach((liquidity: UserLiquidityInfo) => { - if (!liquidity) return; - const { - left_point, - right_point, - lpt_id, - amount, - unclaimed_fee_x, - unclaimed_fee_y, - } = liquidity; - const { amount_x, amount_y } = get_amount_x_y(liquidity); - const unclaimed_fee_x_amount = toReadableNumber( - token_x_metadata.decimals, - unclaimed_fee_x - ); - const unclaimed_fee_y_amount = toReadableNumber( - token_y_metadata.decimals, - unclaimed_fee_y - ); - const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; - const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_liqudities_price = - Number(amount_x) * Number(token_x_price) + - Number(amount_y) * Number(token_y_price); - const total_fees_price = - Number(unclaimed_fee_x_amount) * Number(token_x_price) + - Number(unclaimed_fee_y_amount) * Number(token_y_price); - const decimalRate = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - const l_price = getPriceByPoint(left_point, decimalRate); - const r_price = getPriceByPoint(right_point, decimalRate); - const temp: UserLiquidityDetail = { - total_liqudities_price: total_liqudities_price.toString(), - total_fees_price: total_fees_price.toString(), - amount_x, - amount_y, - unclaimed_fee_x_amount, - unclaimed_fee_y_amount, - - hashId: lpt_id.split('#')[1], - l_price, - r_price, - }; - temp_list.push(temp); - }); - set_user_liquidities_detail(temp_list); - } - }, [liquidities, Object.keys(tokenPriceList).length]); - useEffect(() => { - if ( - liquidities && - poolDetail && - tokenPriceList && - Object.keys(tokenPriceList).length - ) { - get_24_apr_and_fee(); - } - }, [poolDetail, tokenPriceList, liquidities]); - useEffect(() => { - if (liquidities) { - const target = liquidities.find((l: UserLiquidityInfo) => { - return Big(l.part_farm_ratio || 0).gt(0); - }); - if (target) { - set_is_in_farming(true); - } else { - set_is_in_farming(false); - } - set_is_in_farming_done(true); - } - }, [liquidities]); - async function get_24_apr_and_fee() { - let apr_24 = '0'; - let total_fee_earned = '0'; - let total_earned_fee_x; - let total_earned_fee_y; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ - pool_id, - account_id: accountId, - }); - if (dcl_fee_result) { - // 24h profit - apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); - // total unClaimed fee - const [ - unClaimed_tvl_fee, - unClaimed_amount_x_fee, - unClaimed_amount_y_fee, - ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); - // total earned fee - const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; - total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - total_earned_fee_x = Big(total_earned_fee_x) - .plus(unClaimed_amount_x_fee) - .toFixed(); - - total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - total_earned_fee_y = Big(total_earned_fee_y) - .plus(unClaimed_amount_y_fee) - .toFixed(); - - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value - .plus(total_earned_fee_y_value) - .toFixed(); - total_fee_earned = Big(total_fee_earned) - .plus(unClaimed_tvl_fee) - .toFixed(); - } - set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); - set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); - set_earned_fee(formatWithCommas_usd(total_fee_earned)); - setAccountAPR(formatPercentage(apr_24)); - } - function get_amount_x_y(liquidity: UserLiquidityInfo) { - const [tokenX, tokenY] = [token_x_metadata, token_y_metadata]; - const { left_point, right_point, amount: L } = liquidity; - const { current_point } = poolDetail; - let amount_x = '0'; - let amount_y = '0'; - // in range - if (current_point >= left_point && right_point > current_point) { - const tokenYAmount = getY( - left_point, - current_point, - current_point, - L, - tokenY - ); - const tokenXAmount = getX(current_point + 1, right_point, L, tokenX); - const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); - amount_x = new BigNumber(tokenXAmount).plus(amountx).toFixed(); - amount_y = new BigNumber(tokenYAmount).plus(amounty).toFixed(); - } - // only y token - if (current_point >= right_point) { - const tokenYAmount = getY( - left_point, - right_point, - current_point, - L, - tokenY - ); - amount_y = tokenYAmount; - } - // only x token - if (left_point > current_point) { - const tokenXAmount = getX(left_point, right_point, L, tokenX); - amount_x = tokenXAmount; - } - return { - amount_x, - amount_y, - }; - } - function getX( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const x = new BigNumber(L) - .multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ) - .toFixed(); - return toReadableNumber(token.decimals, toPrecision(x, 0)); - } - function getY( - leftPoint: number, - rightPoint: number, - currentPoint: number, - L: string, - token: TokenMetadata - ) { - const y = new BigNumber(L).multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - const y_result = y.toFixed(); - return toReadableNumber(token.decimals, toPrecision(y_result, 0)); - } - function get_X_Y_In_CurrentPoint( - tokenX: TokenMetadata, - tokenY: TokenMetadata, - L: string - ) { - const { liquidity, liquidity_x, current_point } = poolDetail; - const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); - let Ly = '0'; - let Lx = '0'; - // only remove y - if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { - Ly = L; - } else { - // have x and y - Ly = liquidity_y_big.toFixed(); - Lx = new BigNumber(L).minus(Ly).toFixed(); - } - const amountX = getXAmount_per_point_by_Lx(Lx, current_point); - const amountY = getYAmount_per_point_by_Ly(Ly, current_point); - const amountX_read = toReadableNumber( - tokenX.decimals, - toPrecision(amountX, 0) - ); - const amountY_read = toReadableNumber( - tokenY.decimals, - toPrecision(amountY, 0) - ); - return { amountx: amountX_read, amounty: amountY_read }; - } - function getTotalLiquditiesTvl() { - const [total_x, total_y] = get_tokens_amount_liquidities(); - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_x_value = Big(price_x).mul(total_x); - const total_y_value = Big(price_y).mul(total_y); - const total_value = total_x_value.plus(total_y_value).toFixed(); - const total_value_display = formatWithCommas_usd(total_value); - return total_value_display; - } - function get_tokens_amount_liquidities() { - const [total_x, total_y] = get_token_amount_in_user_liquidities({ - user_liquidities: liquidities, - pool: poolDetail, - token_x_metadata, - token_y_metadata, - }); - return [total_x, total_y]; - } - function getTotalTokenAmount() { - const [total_x, total_y] = get_tokens_amount_liquidities(); - let display_total_x = '0'; - let display_total_y = '0'; - if (+total_x == 0) { - display_total_x = '0'; - } else if (+total_x < 0.01) { - display_total_x = '<0.01'; - } else { - display_total_x = toInternationalCurrencySystem(total_x, 3); - } - if (+total_y == 0) { - display_total_y = '0'; - } else if (+total_y < 0.01) { - display_total_y = '<0.01'; - } else { - display_total_y = toInternationalCurrencySystem(total_y, 3); - } - return { - total_x: display_total_x, - total_y: display_total_y, - }; - } - function removeLiquidity() { - setOperationType('remove'); - setShowSelectLiquidityBox(true); - } - function getGroupLiquidities() { - const tokenMetadata_x_y = [token_x_metadata, token_y_metadata]; - - let rate_need_to_reverse_display: boolean; - - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { - rate_need_to_reverse_display = true; - } - - if (!noReverseRange) { - rate_need_to_reverse_display = !rate_need_to_reverse_display; - } - - function getRateMapTokens() { - if (tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - if (rate_need_to_reverse_display) { - return `${tokenX.symbol}/${tokenY.symbol}`; - } else { - return `${tokenY.symbol}/${tokenX.symbol}`; - } - } - } - - function getRate( - direction: string, - left_point: number, - right_point: number - ) { - let value = ''; - if (tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - if (direction == 'left') { - value = getPriceByPoint(left_point, decimalRate); - } else if (direction == 'right') { - value = getPriceByPoint(right_point, decimalRate); - } - if (rate_need_to_reverse_display && +value !== 0) { - value = new BigNumber(1).dividedBy(value).toFixed(); - } - } - return value; - } - - const priceRangeList = - liquidities?.map((l) => { - return [ - +getRate( - rate_need_to_reverse_display ? 'right' : 'left', - l.left_point, - l.right_point - ), - +getRate( - rate_need_to_reverse_display ? 'left' : 'right', - l.left_point, - l.right_point - ), - ]; - }) || []; - - const rangeList = findRangeIntersection(priceRangeList); - return { - rangeList, - rateMapTokens: getRateMapTokens(), - }; - } - - const groupedData = getGroupLiquidities(); - - function goAddliquidityV2() { - const url_pool_id = get_pool_name(poolDetail.pool_id); - history.push(`/addLiquidityV2#${url_pool_id}`); - } - - return ( -
-
- - - - - {getTotalLiquditiesTvl()} - -
- {/* chart area */} -
- -
-
-
- - - - - - {groupedData.rangeList.map((range: number[], i: number) => { - return ( -
- {displayNumberToAppropriateDecimals(range[0])} - - - {displayNumberToAppropriateDecimals(range[1])} - {groupedData.rangeList.length > 1 && - i < groupedData.rangeList.length - 1 && ( - , - )} -
- ); - })} - - { - setNoReverseRange(!noReverseRange); - }} - > - {groupedData.rateMapTokens} - -
-
- -
- - - - -
- {getTotalTokenAmount().total_x} - - {token_x_metadata.symbol} - - + - - {getTotalTokenAmount().total_y} - - {token_y_metadata.symbol} -
-
- -
- APR(24h) - -
- {accountAPR || '-'} -
-
- -
- - - - -
{ - setHover(true); - }} - onMouseLeave={() => { - setHover(false); - }} - > -
- {hover && ( -
-
- {toRealSymbol(token_x_metadata.symbol)} - {earned_fee_x_amount || '-'} -
- -
- {toRealSymbol(token_y_metadata.symbol)} - - {earned_fee_y_amount || '-'} -
-
- )} -
- {earned_fee || '-'} -
-
-
-
- { - e.stopPropagation(); - goAddliquidityV2(); - }} - color="#fff" - borderRadius={'8px'} - className={`flex-grow w-1 h-11 text-center text-sm text-white focus:outline-none mr-2.5`} - > - - -
{ - if (is_in_farming) { - setRemoveButtonTip(true); - } - }} - onMouseLeave={() => { - if (is_in_farming) { - setRemoveButtonTip(false); - } - }} - > - { - e.stopPropagation(); - removeLiquidity(); - }} - disabled={is_in_farming || !is_in_farming_done} - color="#fff" - className={`flex-grow w-1 h-11 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ - is_in_farming || !is_in_farming_done - ? 'opacity-30 pointer-events-none' - : '' - } }`} - > - - -
- You have liquidity in farm, please unstake from{' '} - { - localStorage.setItem('BOOST_FARM_TAB', 'yours'); - openUrl('/v2farms'); - }} - > - Your Farm - {' '} - first. -
-
-
- { - setShowSelectLiquidityBox(false); - }} - poolDetail={poolDetail} - user_liquidities_detail={user_liquidities_detail} - user_liquidities={liquidities} - operation={operationType} - tokenPriceList={tokenPriceList} - matched_seeds={matched_seeds} - style={{ - overlay: { - backdropFilter: 'blur(15px)', - WebkitBackdropFilter: 'blur(15px)', - }, - content: { - outline: 'none', - transform: 'translate(-50%, -50%)', - }, - }} - > -
- ); -} -function UnclaimedFeesBox(props: any) { - const { poolDetail, liquidities, tokenPriceList } = props; - const { token_x_metadata, token_y_metadata } = poolDetail; - const [user_liquidities_total, set_user_liquidities_total] = - useState>(); - const [cliam_loading, set_cliam_loading] = useState(false); - useEffect(() => { - if (liquidities) { - const [total_tvl_fee, total_amount_x_fee, total_amount_y_fee] = - get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); - set_user_liquidities_total({ - total_amount_x_fee, - total_amount_y_fee, - total_tvl_fee, - }); - } - }, [liquidities, Object.keys(tokenPriceList).length]); - function getTotalLiquditiesFee() { - const total_tvl = user_liquidities_total?.total_tvl_fee || 0; - if (total_tvl == 0) { - return '$0'; - } else if (total_tvl < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total_tvl.toString(), 2)); - } - } - function getTotalFeeAmount() { - const total_amount_x = user_liquidities_total?.total_amount_x_fee || 0; - const total_amount_y = user_liquidities_total?.total_amount_y_fee || 0; - let display_amount_x; - let display_amount_y; - const total_amount_x_y = total_amount_x + total_amount_y; - if (total_amount_x == 0) { - display_amount_x = '0'; - } else if (total_amount_x < 0.001) { - display_amount_x = '<0.001'; - } else { - display_amount_x = toPrecision(total_amount_x.toString(), 3); - } - if (total_amount_y == 0) { - display_amount_y = '0'; - } else if (total_amount_y < 0.001) { - display_amount_y = '<0.001'; - } else { - display_amount_y = toPrecision(total_amount_y.toString(), 3); - } - - return { - display_amount_x, - display_amount_y, - total_amount_x_y, - }; - } - function claimRewards() { - if (total_amount_x_y == 0) return; - set_cliam_loading(true); - const lpt_ids: string[] = []; - liquidities.forEach((liquidity: UserLiquidityInfo) => { - const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; - if (+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0) { - lpt_ids.push(liquidity.lpt_id); - } - }); - claim_all_liquidity_fee({ - token_x: token_x_metadata, - token_y: token_y_metadata, - lpt_ids, - }); - } - const { display_amount_x, display_amount_y, total_amount_x_y } = - getTotalFeeAmount(); - return ( - <> - {/* for pc */} -
-
- - - - - {getTotalLiquditiesFee()} - -
-
-
-
- - {display_amount_x} -
-
- - {display_amount_y} -
-
- -
- } - /> -
-
-
- {/* for mobile */} -
-
- - - - - {getTotalLiquditiesFee()} - -
-
-
- - - {token_x_metadata.symbol} - -
- {display_amount_x} -
-
-
- - - {token_y_metadata.symbol} - -
- {display_amount_y} -
-
- } - /> -
-
- - ); -} -function get_unClaimed_fee_data( - liquidities: UserLiquidityInfo[], - poolDetail: PoolInfo, - tokenPriceList: any -) { - // UnClaimed fee - let total_amount_x_fee = 0; - let total_amount_y_fee = 0; - let total_tvl_fee = 0; - const { token_x_metadata, token_y_metadata } = poolDetail; - liquidities.forEach((liquidity: UserLiquidityInfo) => { - const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; - const unclaimed_fee_x_amount = toReadableNumber( - token_x_metadata.decimals, - unclaimed_fee_x - ); - const unclaimed_fee_y_amount = toReadableNumber( - token_y_metadata.decimals, - unclaimed_fee_y - ); - const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; - const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_fees_price = - Number(unclaimed_fee_x_amount) * Number(token_x_price) + - Number(unclaimed_fee_y_amount) * Number(token_y_price); - total_amount_x_fee += Number(unclaimed_fee_x_amount); - total_amount_y_fee += Number(unclaimed_fee_y_amount); - total_tvl_fee += Number(total_fees_price); - }); - return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; -} -function RelatedFarmsBox(props: any) { - const { poolDetail, tokenPriceList, sole_seed } = props; - const [related_seed, set_related_seed] = useState(); - const [farm_loading, set_farm_loading] = useState(true); - useEffect(() => { - if (poolDetail && Object.keys(tokenPriceList).length > 0) { - get_farms_data(); - } - }, [poolDetail, tokenPriceList, sole_seed]); - async function get_farms_data() { - if (sole_seed) { - set_related_seed(sole_seed); - } - set_farm_loading(false); - } - function totalTvlPerWeekDisplay() { - const farms = related_seed.farmList; - const rewardTokenIconMap = {}; - let totalPrice = 0; - const effectiveFarms = getEffectiveFarmList(farms); - effectiveFarms.forEach((farm: FarmBoost) => { - const { id, decimals, icon } = farm.token_meta_data; - const { daily_reward } = farm.terms; - rewardTokenIconMap[id] = icon; - const tokenPrice = tokenPriceList[id]?.price; - if (tokenPrice && tokenPrice != 'N/A') { - const tokenAmount = toReadableNumber(decimals, daily_reward); - totalPrice += +new BigNumber(tokenAmount) - .multipliedBy(tokenPrice) - .toFixed(); - } - }); - totalPrice = +new BigNumber(totalPrice).multipliedBy(7).toFixed(); - const totalPriceDisplay = - totalPrice == 0 - ? '-' - : '$' + toInternationalCurrencySystem(totalPrice.toString(), 2); - return totalPriceDisplay; - } - function isPending(seed: Seed) { - let pending: boolean = true; - const farms = seed.farmList; - for (let i = 0; i < farms.length; i++) { - if (farms[i].status != 'Created' && farms[i].status != 'Pending') { - pending = false; - break; - } - } - return pending; - } - function getTotalAprForSeed() { - const farms = related_seed.farmList; - let apr = 0; - const allPendingFarms = isPending(related_seed); - farms.forEach(function (item: FarmBoost) { - const pendingFarm = item.status == 'Created' || item.status == 'Pending'; - if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { - apr = +new BigNumber(item.apr).plus(apr).toFixed(); - } - }); - if (apr == 0) { - return '-'; - } else { - apr = +new BigNumber(apr).multipliedBy(100).toFixed(); - return toPrecision(apr.toString(), 2) + '%'; - } - } - function getAllRewardsSymbols() { - const tempMap = {}; - related_seed.farmList.forEach((farm: FarmBoost) => { - const { token_meta_data } = farm; - const { icon, id } = token_meta_data; - tempMap[id] = icon; - }); - const arr = Object.entries(tempMap); - return arr.slice(0, 5); - } - function go_farm() { - const { seed_id } = related_seed; - const [contractId, temp_pool_id] = seed_id.split('@'); - const [fixRange, pool_id, left_point, right_point] = - temp_pool_id.split('&'); - const link_params = `${get_pool_name( - pool_id - )}[${left_point}-${right_point}]`; - openUrl(`/v2farms/${link_params}-r`); - } - if (farm_loading) return null; - if (!related_seed) return null; - return ( -
- -
- Farm APR -
- {getAllRewardsSymbols().map(([id, icon]: [string, string], index) => { - return ( - - ); - })} - {related_seed?.farmList.length > 5 ? ( -
- ... -
- ) : null} - - - {totalTvlPerWeekDisplay()}/week - -
-
-
-
- - {getTotalAprForSeed()} - - -
- - - -
-
- ); -} -function NoYourLiquditiesBox(props: any) { - const { poolDetail } = props; - const { token_x_metadata, pool_id } = poolDetail; - const history = useHistory(); - function goAddLiqudityPage() { - const [token_x, token_y, fee] = pool_id.split('|'); - let url_hash = pool_id; - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { - url_hash = `${token_y}|${token_x}|${fee}`; - } - const pool_name = get_pool_name(url_hash); - history.push(`/addLiquidityV2#${pool_name}`); - } - return ( -
- - - - -
- { - e.stopPropagation(); - goAddLiqudityPage(); - }} - color="#fff" - className={`w-full h-11 text-center text-base text-white focus:outline-none`} - > - - -
-
- ); -} -function SelectLiquidityBox(props: any) { - const { - isOpen, - onRequestClose, - style, - user_liquidities_detail, - poolDetail, - operation, - tokenPriceList, - user_liquidities, - matched_seeds, - } = props; - - const [hoverHashId, setHoverHashId] = useState(''); - const [showRemoveBox, setShowRemoveBox] = useState(false); - const [showAddBox, setShowAddBox] = useState(false); - const history = useHistory(); - const { token_x_metadata, token_y_metadata } = poolDetail; - function displayLiqudityTvl(liquidityDetail: UserLiquidityDetail) { - const total = +liquidityDetail.total_liqudities_price; - if (total == 0) { - return '$0'; - } else if (total < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total.toString(), 2)); - } - } - function displayLiqudityFee(liquidityDetail: UserLiquidityDetail) { - const total = +liquidityDetail.total_fees_price; - if (total == 0) { - return '$0'; - } else if (total < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total.toString(), 2)); - } - } - function displayRange(liquidityDetail: UserLiquidityDetail) { - const { l_price, r_price } = liquidityDetail; - let display_l; - let display_r; - if ( - TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1 && - +r_price !== 0 && - +l_price !== 0 - ) { - display_l = new BigNumber(1).dividedBy(r_price).toFixed(); - display_r = new BigNumber(1).dividedBy(l_price).toFixed(); - } else { - display_l = l_price; - display_r = r_price; - } - display_l = displayNumberToAppropriateDecimals(display_l); - display_r = displayNumberToAppropriateDecimals(display_r); - return `${display_l} - ${display_r}`; - } - function hoverLine(hashId: string) { - setHoverHashId(hashId); - } - function getCurrentLiqudity(hashId: string) { - const c_l = user_liquidities.find((liquidity: UserLiquidityInfo) => { - if (liquidity.lpt_id.split('#')[1] == hashId) return true; - }); - return c_l; - } - function goAddLiqudityPage() { - const pool_id = poolDetail.pool_id; - const [token_x, token_y, fee] = pool_id.split('|'); - let url_hash = pool_id; - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { - url_hash = `${token_y}|${token_x}|${fee}`; - } - history.push(`/addLiquidityV2#${url_hash}`); - } - function displayFarmStatus(liquidity: UserLiquidityInfo) { - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - if (is_in_farming) { - return ( - - ); - } else { - return ( - - ); - } - } - function go_farm(liquidity: UserLiquidityInfo) { - const { mft_id } = liquidity; - const [fixRange, pool_id, left_point, right_point] = mft_id.split('&'); - const link_params = `${get_pool_name( - pool_id - )}[${left_point}-${right_point}]`; - const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); - const temp_seeds = (matched_seeds || []).filter((seed: Seed) => { - return seed_id == seed.seed_id; - }); - let actives: FarmBoost[] = []; - temp_seeds.forEach((seed: Seed) => { - const { farmList } = seed; - const temp = farmList.filter((farm: FarmBoost) => { - return farm.status != 'Ended'; - }); - actives = actives.concat(temp); - }); - let url; - if (actives.length > 0) { - url = `/v2farms/${link_params}-r`; - } else { - url = `/v2farms/${link_params}-e`; - } - openUrl(url); - } - function is_in_farming(liquidity: UserLiquidityInfo) { - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - return is_in_farming; - } - const isMobile = isClientMobie(); - const has_no_related_seed = - matched_seeds?.length == 0 && - user_liquidities?.every( - (liquidity: UserLiquidityInfo) => +(liquidity.part_farm_ratio || 0) == 0 - ); - return operation == 'remove' && isOpen ? ( - - ) : null; -} - -function Chart(props: any) { - const { poolDetail, tokenPriceList } = props; - const [depthData, setDepthData] = useState(); - const [chartDisplay, setChartDisplay] = useState('liquidity'); - useEffect(() => { - getChartData(); - }, []); - async function getChartData() { - const depthData = await get_pool_marketdepth(poolDetail.pool_id); - setDepthData(depthData); - } - const monthVolume = useV3VolumeChart(poolDetail.pool_id); - const monthTVL = useV3TvlChart(poolDetail.pool_id); - return ( - - {chartDisplay === 'volume' ? ( - - ) : chartDisplay === 'tvl' ? ( - - ) : ( - - )} - - ); -} -function BaseData(props: any) { - const { poolDetail, tokenPriceList } = props; - const [volume24, setVolume24] = useState('0'); - const [user_liquidity_fee, set_user_liquidity_fee] = useState(); - useEffect(() => { - getV3Pool24VolumeById(poolDetail.pool_id) - .then((res) => { - setVolume24(res); - }) - .catch(() => {}); - get_metadata().then((res) => { - if (res) { - const { protocol_fee_rate } = res; - set_user_liquidity_fee((10000 - protocol_fee_rate) / 10000); - } - }); - }, []); - - function getTvl() { - const { - token_x_metadata, - token_y_metadata, - token_x, - token_y, - total_x, - total_y, - total_fee_x_charged, - total_fee_y_charged, - } = poolDetail; - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const totalX = new BigNumber(total_x).minus(total_fee_x_charged).toFixed(); - const totalY = new BigNumber(total_y).minus(total_fee_y_charged).toFixed(); - const tvlx = - Number(toReadableNumber(token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(token_y_metadata.decimals, totalY)) * - Number(pricey); - const tvl = tvlx + tvly; - if (tvl == 0) { - return '$0'; - } else if (tvl < 0.01) { - return '<$0.01'; - } else { - return '$' + toInternationalCurrencySystem(tvl.toString(), 2); - } - } - function get24Volume() { - if (+volume24 == 0) { - return '$0'; - } else if (+volume24 < 0.01) { - return '<$0.01'; - } else { - return '$' + toInternationalCurrencySystem(volume24.toString(), 2); - } - } - - function get24Fee() { - const fee = poolDetail.fee; - const f = new BigNumber(fee) - .dividedBy(1000000) - .multipliedBy(user_liquidity_fee || 1) - .multipliedBy(volume24) - .toFixed(); - if (+f == 0) { - return '$0'; - } else if (+f < 0.01) { - return '<$0.01'; - } else { - return '$' + toInternationalCurrencySystem(f.toString(), 2); - } - } - return ( -
- - } - value={getTvl()} - > - - } - value={get24Volume()} - > - - } - value={get24Fee()} - > -
- ); -} -function DataBox(props: any) { - const { title, value, className } = props; - return ( -
- {title} - {value} -
- ); -} - -type RencentTabKey = 'swap' | 'liquidity' | 'limit_order'; - -const REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL = - 'REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL'; - -export function RecentTransactions({ - pool_id, - tokens, -}: { - pool_id: string; - tokens: TokenMetadata[]; -}) { - const storedTab = sessionStorage.getItem( - REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL - ) as RencentTabKey; - - const { swapTransactions, liquidityTransactions, limitOrderTransactions } = - useDCLPoolTransaction({ pool_id }); - const [tab, setTab] = useState(storedTab || 'swap'); - - const onChangeTab = (tab: RencentTabKey) => { - sessionStorage.setItem(REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL, tab); - setTab(tab); - }; - - const renderSwapTransactions = swapTransactions.map((tx) => { - const swapIn = tokens.find((t) => t.id === tx.token_in); - - const swapOut = tokens.find((t) => t.id === tx.token_out); - - if (!swapIn || !swapOut) return null; - - const swapInAmount = toReadableNumber(swapIn.decimals, tx.amount_in); - const displayInAmount = - Number(swapInAmount) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(swapInAmount, 6)); - - const swapOutAmount = toReadableNumber(swapOut.decimals, tx.amount_out); - - const displayOutAmount = - Number(swapOutAmount) < 0.01 - ? '<0.01' - : numberWithCommas(toPrecision(swapOutAmount, 6)); - - const txLink = ( - - - - ); - - return ( -
- - {displayInAmount} - - - - {toRealSymbol(swapIn.symbol)} - - - - {displayOutAmount} - - - - {toRealSymbol(swapOut.symbol)} - - - { - openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); - }} - > - - {tx.timestamp} - - {txLink} - -
- - {(tx.method_name.toLowerCase().indexOf('add') > -1 || - tx.method_name.toLowerCase().indexOf('append') > -1) && - 'Add'} - - {tx.method_name.toLowerCase().indexOf('remove') > -1 && 'Remove'} - - - {Big(AmountIn || 0).gt(0) ? ( - <> - - {displayInAmount} - - - - {toRealSymbol(swapIn.symbol)} - - - ) : null} - {Big(AmountIn || 0).gt(0) && Big(AmountOut || 0).gt(0) ? ( - + - ) : null} - {Big(AmountOut || 0).gt(0) ? ( - <> - {' '} - - {displayOutAmount} - - - {toRealSymbol(swapOut.symbol)} - - - ) : null} - - { - openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); - }} - > - - {tx.timestamp} - - {txLink} - -
- {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && 'Cancel'} - - {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Place'} - -
- - {displayInAmount} - - - - {toRealSymbol(swapIn.symbol)} - -
-
-
- - {displayOutAmount} - - - - {toRealSymbol(swapOut.symbol)} - -
-
-
- - {numberWithCommas(toPrecision(display_price, 4))} - - - - {toRealSymbol(sort_tokens?.[1]?.symbol)}/ - {toRealSymbol(sort_tokens?.[0]?.symbol)} - -
-
- { - openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); - }} - > - - {tx.timestamp} - - {txLink} - -
- - - - - - {tab === 'limit_order' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - - -
- {tab === 'liquidity' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - {tab === 'swap' && ( - - )} - - {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - - - - - -
- -
- - - - - - - {tab === 'limit_order' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - - - {renderTransactions} -
- {tab === 'liquidity' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - {tab === 'swap' && ( - - )} - - {tab === 'liquidity' && ( - - )} - {tab === 'swap' && ( - - )} - - {tab === 'limit_order' && ( - - )} - - - - - - -
-
-
- - ); -} - -function TablePool(props: any) { - const { poolDetail, tokenPriceList } = props; - const [tokens, setTokens] = useState([]); - const intl = useIntl(); - useEffect(() => { - const { - token_x, - token_y, - total_x, - total_y, - token_x_metadata, - token_y_metadata, - total_fee_x_charged, - total_fee_y_charged, - } = poolDetail; - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const totalX = new BigNumber(total_x).minus(total_fee_x_charged).toFixed(); - const totalY = new BigNumber(total_y).minus(total_fee_y_charged).toFixed(); - const amountx = toReadableNumber(token_x_metadata.decimals, totalX); - const amounty = toReadableNumber(token_y_metadata.decimals, totalY); - const tvlx = Number(amountx) * Number(pricex); - const tvly = Number(amounty) * Number(pricey); - const temp_list = []; - const temp_tokenx = { - meta: token_x_metadata, - amount: amountx, - tvl: tvlx, - }; - const temp_tokeny = { - meta: token_y_metadata, - amount: amounty, - tvl: tvly, - }; - temp_list.push(temp_tokenx, temp_tokeny); - setTokens(temp_list); - }, [Object.keys(tokenPriceList).length]); - function valueOfNearTokenTip() { - const tip = intl.formatMessage({ id: 'awesomeNear_verified_token' }); - let result: string = `
${tip}
`; - return result; - } - function displayAmount(amount: string) { - if (+amount == 0) { - return '0'; - } else if (+amount < 0.01) { - return '< 0.01'; - } else { - return toInternationalCurrencySystem(amount.toString(), 2); - } - } - function displayTvl(token: any) { - const { tvl } = token; - if (+tvl == 0 && !tokenPriceList[token.meta.id]?.price) { - return '$ -'; - } else if (+tvl == 0) { - return '$0'; - } else if (+tvl < 0.01) { - return '< $0.01'; - } else { - return '$' + toInternationalCurrencySystem(tvl.toString(), 2); - } - } - return ( -
-
- -
-
-
-
- -
- -
- -
- -
- -
-
- {tokens.map((token: any, i: number) => ( -
-
- - -
-
- {displayAmount(token.amount)} -
-
- {displayTvl(token)} -
-
- ))} -
- - t.meta)} - pool_id={poolDetail.pool_id} - > -
- ); -} - -function Icon(props: { icon?: string; className?: string; style?: any }) { - const { icon, className, style } = props; - return icon ? ( - - ) : ( -
- ); -} -let timer: any; -function LiquidityChart(props: any) { - const { data, chartDisplay, setChartDisplay } = props; - const { poolDetail, depthData } = data; - const isMobile = isClientMobie(); - const svgDefaultWidth = isMobile ? '380' : 750; - const [chartLoading, setChartLoading] = useState(true); - const [noData, setNoData] = useState(true); - const [rateDirection, setRateDirection] = useState(true); - const [svgWidth, setSvgWidth] = useState(svgDefaultWidth); - const refDom = useRef(null); - useEffect(() => { - if (poolDetail?.token_x_metadata) { - if ( - TOKEN_LIST_FOR_RATE.indexOf(poolDetail?.token_x_metadata.symbol) > -1 - ) { - setRateDirection(false); - } else { - setRateDirection(true); - } - } - }, [poolDetail]); - useEffect(() => { - if (depthData) { - const { liquidities } = depthData; - const list = Object.values(liquidities); - if (list.length == 0) { - setNoData(true); - } else { - setNoData(false); - } - setChartLoading(false); - } else { - setChartLoading(true); - } - }, [depthData, rateDirection]); - const rateDOM = useMemo(() => { - const { current_point, token_x_metadata, token_y_metadata } = poolDetail; - const rate = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - let price = getPriceByPoint(current_point, rate); - if (!rateDirection) { - price = new BigNumber(1).dividedBy(price).toFixed(); - } - let displayRate; - if (new BigNumber(price).isLessThan('0.001')) { - displayRate = ' < 0.001'; - } else { - displayRate = ` = ${formatWithCommas(toPrecision(price.toString(), 3))}`; - } - return ( - - 1 {rateDirection ? token_x_metadata.symbol : token_y_metadata.symbol} -   - - {displayRate}{' '} - {rateDirection ? token_y_metadata.symbol : token_x_metadata.symbol} - - - ); - }, [poolDetail, rateDirection]); - useEffect(() => { - if (isMobile) return; - if (refDom.current) { - setSvgWidth(refDom.current.clientWidth || svgDefaultWidth); - window.onresize = () => { - clearTimeout(timer); - timer = setTimeout(() => { - setSvgWidth(refDom?.current?.clientWidth || svgDefaultWidth); - }, 50); - }; - } - }, [refDom.current]); - function switchRate() { - setRateDirection(!rateDirection); - } - return ( - <> -
-
-
- {rateDOM} - -
- - - -
- {isMobile ? ( - - ) : ( - - )} -
- {!chartLoading && noData ? ( - - ) : ( -
- -
- )} + ) : null} ); } - -function EmptyLiquidityChart() { - return ( -
-
-
- - - - -
-
-
-
-
- {[ - '24', - '31', - '07', - '14', - '21', - '28', - '04', - '11', - '18', - '25', - '02', - '09', - ].map((d, i) => { - return
{d}
; - })} -
-
-
- ); -} -interface ParamTypes { - id: string; -} -interface UserLiquidityDetail { - total_liqudities_price: string; - total_fees_price: string; - amount_x: string; - amount_y: string; - hashId: string; - l_price: string; - r_price: string; - unclaimed_fee_y_amount?: string; - unclaimed_fee_x_amount?: string; -} diff --git a/src/pages/poolsV3/components/add/AddLiquidityButton.tsx b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx new file mode 100644 index 000000000..5f8b4ecba --- /dev/null +++ b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx @@ -0,0 +1,228 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import Big from 'big.js'; +import { FormattedMessage } from 'react-intl'; +import BigNumber from 'bignumber.js'; +import { LiquidityProviderData } from '../../AddYourLiquidityPageV3'; +import { WalletContext } from '../../../../utils/wallets-integration'; +import { + add_liquidity, + batch_add_liquidity, +} from '../../../../services/swapV3'; +import { toNonDivisibleNumber } from '~utils/numbers'; +import { IAddLiquidityInfo } from '../../interfaces'; +import { TokenMetadata } from '../../../../services/ft-contract'; +import { WRAP_NEAR_CONTRACT_ID } from '../../../../services/wrap-near'; +import { + GradientButton, + ButtonTextWrapper, + ConnectToNearBtn, +} from '~components/button/Button'; + +/** + * 双边 最小token数量不满足 提示 + * 双边 一侧token 数量太多 传递的时候只传实际使用值 + * @returns + */ +export function AddLiquidityButton() { + const { + currentSelectedPool, + tokenX, + tokenY, + liquidityShape, + tokenXAmount, + tokenYAmount, + tokenXBalanceFromNear, + tokenYBalanceFromNear, + onlyAddXToken, + onlyAddYToken, + invalidRange, + getLiquiditySpot, + getLiquidityForCurveAndBidAskMode, + } = useContext(LiquidityProviderData); + const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = + useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + + function addLiquiditySpot() { + setAddLiquidityButtonLoading(true); + const new_liquidity = getLiquiditySpot(); + add_liquidity(new_liquidity); + } + function addLiquidityForCurveAndBidAskMode() { + /** + * 已知条件: + * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount + * 当前点位为point,以slot为单位 下一跳是 point + slot + * 当前点位为point,以bin为单位 下一跳是 point + bin * slots + * 最小的bin的高度就是等差的值 为dis + **/ + setAddLiquidityButtonLoading(true); + const tokenXAmount_nonDivisible = toNonDivisibleNumber( + tokenX.decimals, + tokenXAmount || '0' + ); + const tokenYAmount_nonDivisible = toNonDivisibleNumber( + tokenY.decimals, + tokenYAmount || '0' + ); + let nftList: IAddLiquidityInfo[] = []; + nftList = getLiquidityForCurveAndBidAskMode(); + if (!nftList) { + setAddLiquidityButtonLoading(false); + return; + } + /** + * 计算出 nftList token x tokeny 的数量,这是需要的总数量 + * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 + * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 + */ + let last_total_needed_token_x_amount = Big(0); + let last_total_needed_token_y_amount = Big(0); + nftList.forEach((nft: IAddLiquidityInfo) => { + const { amount_x, amount_y } = nft; + last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( + amount_x || 0 + ); + last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( + amount_y || 0 + ); + }); + batch_add_liquidity({ + liquidityInfos: nftList, + token_x: tokenX, + token_y: tokenY, + amount_x: last_total_needed_token_x_amount.toFixed(), + amount_y: last_total_needed_token_y_amount.toFixed(), + }); + } + function getMax(token: TokenMetadata, balance: string) { + return token.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + } + function getButtonText() { + let txt: any = ( + + ); + if (!currentSelectedPool?.pool_id) { + txt = ; + } else if (invalidRange) { + txt = ( + + ); + } else if (onlyAddXToken && +tokenXAmount == 0) { + txt = ( + + ); + } else if (onlyAddYToken && +tokenYAmount == 0) { + txt = ( + + ); + } else if ( + !onlyAddXToken && + !onlyAddYToken && + (+tokenXAmount == 0 || +tokenYAmount == 0) + ) { + txt = ( + + ); + } else if ( + +tokenXAmount > 0 && + new BigNumber(tokenXAmount).isGreaterThan( + getMax(tokenX, tokenXBalanceFromNear) + ) + ) { + txt = ( + + ); + } else if ( + +tokenYAmount > 0 && + new BigNumber(tokenYAmount).isGreaterThan( + getMax(tokenY, tokenYBalanceFromNear) + ) + ) { + txt = ( + + ); + } + return txt; + } + function getButtonStatus() { + const condition1 = currentSelectedPool?.pool_id; + let condition2; + if (onlyAddXToken) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount); + } else if (onlyAddYToken) { + condition2 = + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } else if (!invalidRange) { + condition2 = + +tokenXAmount > 0 && + new BigNumber( + getMax(tokenX, tokenXBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenXAmount) && + +tokenYAmount > 0 && + new BigNumber( + getMax(tokenY, tokenYBalanceFromNear) + ).isGreaterThanOrEqualTo(tokenYAmount); + } + return !(condition1 && condition2); + } + const isAddLiquidityDisabled = getButtonStatus(); + + const add_lp_func = + liquidityShape === 'Spot' + ? addLiquiditySpot + : addLiquidityForCurveAndBidAskMode; + + return ( +
+ {isSignedIn ? ( + + <>{getButtonText()}} + /> + + ) : ( + + )} +
+ ); +} diff --git a/src/pages/poolsV3/components/add/Icon 6.tsx b/src/pages/poolsV3/components/add/Icon 6.tsx new file mode 100644 index 000000000..a4a4852a2 --- /dev/null +++ b/src/pages/poolsV3/components/add/Icon 6.tsx @@ -0,0 +1,20 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +export function Icon(props: { + icon?: string; + className?: string; + style?: any; +}) { + const { icon, className, style } = props; + return icon ? ( + + ) : ( +
+ ); +} diff --git a/src/pages/poolsV3/components/add/InputAmount.tsx b/src/pages/poolsV3/components/add/InputAmount.tsx new file mode 100644 index 000000000..22a7be1d7 --- /dev/null +++ b/src/pages/poolsV3/components/add/InputAmount.tsx @@ -0,0 +1,141 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import { TokenMetadata } from '../../../../services/ft-contract'; +import { FormattedMessage } from 'react-intl'; +import BigNumber from 'bignumber.js'; +import { PoolInfo } from '../../../../services/swapV3'; +import { formatWithCommas, toPrecision } from '~utils/numbers'; +import { WalletContext } from '../../../../utils/wallets-integration'; +import { WRAP_NEAR_CONTRACT_ID } from '../../../../services/wrap-near'; +import { toRealSymbol } from '../../../../utils/token'; +import { WarningIcon } from '~components/icon/V3'; + +export function InputAmount({ + token, + balance, + tokenPriceList, + changeAmount, + amount, + currentSelectedPool, + disabled, +}: { + token: TokenMetadata; + balance: string; + tokenPriceList: Record; + changeAmount: any; + amount: string; + currentSelectedPool: PoolInfo; + disabled?: Boolean; +}) { + const [inputPrice, setInputPrice] = useState(''); + const [showNearTip, setShowNearTip] = useState(false); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + const price = token ? tokenPriceList[token.id]?.price : ''; + if (price && amount) { + setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); + } else { + setInputPrice(''); + } + if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { + const difference = new BigNumber(maxBalance).minus(amount); + const b = difference.toFixed(); + const r = difference.isLessThan(0); + if (r) { + setShowNearTip(true); + } else { + setShowNearTip(false); + } + } else { + setShowNearTip(false); + } + }, [amount, token, tokenPriceList.length]); + function getBalance() { + let r = '0'; + if (token && balance) { + r = formatWithCommas(toPrecision(balance.toString(), 3)); + } + return isSignedIn ? r : '-'; + } + function showCurrentPrice() { + if (isNoPool) { + return '$-'; + } else if (inputPrice) { + return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); + } + return '$-'; + } + const maxBalance = + token?.id !== WRAP_NEAR_CONTRACT_ID + ? balance + : Number(balance) <= 0.5 + ? '0' + : String(Number(balance) - 0.5); + const isNoPool = !currentSelectedPool?.pool_id; + return ( +
+
+
+ { + changeAmount(target.value); + }} + /> + + {token ? toRealSymbol(token.symbol) : 'Selet Token'} + +
+
+ {showCurrentPrice()} +
+ + :{' '} + { + if (disabled) return; + changeAmount(maxBalance); + }} + > + {getBalance()} + + +
+
+
+ {showNearTip && !isNoPool ? ( +
+ + +
+ ) : null} +
+ ); +} diff --git a/src/pages/poolsV3/components/add/IntegerInputComponent.tsx b/src/pages/poolsV3/components/add/IntegerInputComponent.tsx new file mode 100644 index 000000000..2189322f1 --- /dev/null +++ b/src/pages/poolsV3/components/add/IntegerInputComponent.tsx @@ -0,0 +1,50 @@ +import React from 'react'; + +export function IntegerInputComponent({ + value, + setValue, + disabled, + triggerByValue, +}: any) { + const removeLeadingZeros = (s: string) => { + const oldLen = s.length; + s = s.replace(/^0+/, ''); + + if (s.length === 0 && oldLen > 0) { + s = '0'; + } + + return s; + }; + + const handleChange = (val: string) => { + val = val.replace(/[^\d]/g, ''); + val = removeLeadingZeros(val); + setValue(val); + if (val) { + triggerByValue(val); + } + }; + + return ( +
+ { + if (!target.value) { + setValue(1); + triggerByValue(1); + } + }} + onChange={({ target }) => { + handleChange(target.value); + }} + /> +
+ ); +} diff --git a/src/pages/poolsV3/components/add/NoDataComponent.tsx b/src/pages/poolsV3/components/add/NoDataComponent.tsx new file mode 100644 index 000000000..d168b334e --- /dev/null +++ b/src/pages/poolsV3/components/add/NoDataComponent.tsx @@ -0,0 +1,163 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { WalletContext } from '../../../../utils/wallets-integration'; + +export function NoDataComponent() { + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + return ( +
+ {/* chart area */} +
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + if (isSignedIn) { + setChartTab('yours'); + } + }} + > + Yours + +
+
+ Oops! The Pool doesn’t exist +
+
+ {/* set price range area */} +
+ {/* price range mode area */} +
+
+ + + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + }} + > + + +
+
+ {/* content */} +
+ {/* target price input box */} +
+ + + + + 0 + +
+ + {/* radius input box */} +
+ + + + + 0 + +
+ + {/* min price input box */} +
+ + + + + 0 + +
+ + {/* max price input box */} +
+ + + + + 0 + +
+
+
+
+ ); +} diff --git a/src/pages/poolsV3/components/add/PointInputComponent.tsx b/src/pages/poolsV3/components/add/PointInputComponent.tsx new file mode 100644 index 000000000..097bda282 --- /dev/null +++ b/src/pages/poolsV3/components/add/PointInputComponent.tsx @@ -0,0 +1,50 @@ +import React from 'react'; +import Big from 'big.js'; + +export function PointInputComponent({ + handlePriceToAppropriatePoint, + customPrice, + setCustomPrice, + + getPrice, + point, + setPoint, + + inputStatus, + setInputStatus, + disbaled, +}: any) { + return ( +
+ { + setInputStatus(false); + if (customPrice) { + const appropriate_point_temp = + handlePriceToAppropriatePoint(customPrice); + setPoint(appropriate_point_temp); + } else { + setPoint(point); + } + }} + disabled={disbaled} + value={inputStatus ? customPrice : getPrice()} + onChange={({ target }) => { + setInputStatus(true); + const inputPrice = target.value; + if (Big(target.value || 0).lt(0)) { + setCustomPrice('0'); + } else { + setCustomPrice(inputPrice); + } + }} + /> +
+ ); +} diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx new file mode 100644 index 000000000..a64ae3305 --- /dev/null +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -0,0 +1,595 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import { LiquidityProviderData } from '../../AddYourLiquidityPageV3'; +import { RADIUS_DEFAULT_NUMBER } from '../../../../components/d3Chart/config'; +import { isInvalid } from '../../../../components/d3Chart/utils'; +import { + getPriceByPoint, + getPointByPrice, + POINTLEFTRANGE, + POINTRIGHTRANGE, + getBinPointByPrice, + getBinPointByPoint, + reverse_price, + getSlotPointByPoint, +} from '../../../../services/commonV3'; +import BigNumber from 'bignumber.js'; +import { toPrecision } from '~utils/numbers'; +import { isMobile } from '../../../../utils/device'; +import { FormattedMessage } from 'react-intl'; +import DclChart from '../../../../components/d3Chart/DclChart'; +import { PointInputComponent } from './PointInputComponent'; +import { IntegerInputComponent } from './IntegerInputComponent'; + +export function PointsComponent() { + const { + binNumber, + setBinNumber, + currentSelectedPool, + tokenX, + tokenY, + set_token_amount_tip, + + pointChange, + liquidityShape, + + SLOT_NUMBER, + BIN_WIDTH, + + switch_pool_loading, + + isSignedIn, + pair_is_reverse, + + onlyAddXToken, + onlyAddYToken, + invalidRange, + currentPoint, + } = useContext(LiquidityProviderData); + const [priceRangeMode, setPriceRangeMode] = useState< + 'by_range' | 'by_radius' + >('by_range'); + const [radius, setRadius] = useState(); + const [targetCustomPrice, setTargetCustomPrice] = useState(''); + const [leftCustomPrice, setLeftCustomPrice] = useState(''); + const [rightCustomPrice, setRightCustomPrice] = useState(''); + const [targetPoint, setTargetPoint] = useState(); + + const [leftInputStatus, setLeftInputStatus] = useState(false); + const [rightInputStatus, setRightInputStatus] = useState(false); + const [targetInputStatus, setTargetInputStatus] = useState(false); + + const [leftPoint, setLeftPoint] = useState(); + const [rightPoint, setRightPoint] = useState(); + + const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); + + const token_x_decimals = tokenX.decimals; + const token_y_decimals = tokenY.decimals; + + // init + useEffect(() => { + if (currentSelectedPool?.pool_id && !switch_pool_loading) { + const { current_point } = currentSelectedPool; + let left_point, right_point; + const targetPoint = get_slot_point_by_point(current_point); + if (pair_is_reverse) { + left_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + } else { + right_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * RADIUS_DEFAULT_NUMBER + ); + left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; + } + setTargetPoint(targetPoint); + setRadius(RADIUS_DEFAULT_NUMBER); + setLeftPoint(left_point); + setRightPoint(right_point); + setPriceRangeMode('by_range'); + setChartTab('liquidity'); + } + }, [currentSelectedPool, switch_pool_loading]); + + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + // effect bin + let diff; + if (pair_is_reverse) { + diff = leftPoint - rightPoint; + } else { + diff = rightPoint - leftPoint; + } + const bin_number_temp = diff / BIN_WIDTH; + setBinNumber(bin_number_temp); + // effect right area + if (pair_is_reverse) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } else { + pointChange({ leftPoint, rightPoint }); + } + } + console.log( + '00000000--leftPoint, rightPoint--00000000', + leftPoint, + rightPoint + ); + }, [leftPoint, rightPoint, BIN_WIDTH]); + + useEffect(() => { + if ( + liquidityShape == 'Spot' && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) + ) { + if (pair_is_reverse) { + pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); + } else { + pointChange({ leftPoint, rightPoint }); + } + } + }, [liquidityShape]); + + // clean tip + useEffect(() => { + if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { + set_token_amount_tip(null); + } + }, [liquidityShape, leftPoint, rightPoint]); + + // to change bin --> to get proper right point --->for proper bin + function changeBin(bin: number) { + let appropriate_right_point, appropriate_bin_number; + if (pair_is_reverse) { + appropriate_right_point = leftPoint - BIN_WIDTH * bin; + if (appropriate_right_point < POINTLEFTRANGE) { + appropriate_right_point = POINTLEFTRANGE; + } + appropriate_bin_number = + (leftPoint - appropriate_right_point) / BIN_WIDTH; + } else { + appropriate_right_point = leftPoint + BIN_WIDTH * bin; + if (appropriate_right_point > POINTRIGHTRANGE) { + appropriate_right_point = POINTRIGHTRANGE; + } + appropriate_bin_number = + (appropriate_right_point - leftPoint) / BIN_WIDTH; + } + setRightPoint(appropriate_right_point); + setBinNumber(appropriate_bin_number); + } + + // to change radius-->to get proper left point and right point --->for proper radius + function changeRadius(radius: number) { + let appropriate_left_point, appropriate_right_point, appropriate_radius; + if (pair_is_reverse) { + appropriate_left_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * radius + ); + appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + appropriate_radius = + (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); + } else { + appropriate_right_point = get_bin_point_by_point( + targetPoint + BIN_WIDTH * radius + ); + appropriate_left_point = get_bin_point_by_point( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + appropriate_radius = + (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); + } + + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + setRadius(appropriate_radius); + } + // to change targetPrice-->o get proper left point and right point--->for proper targetPrice + function handleTargetPriceToAppropriatePoint(price: string) { + let appropriate_left_point, + appropriate_right_point, + appropriate_target_point; + if (pair_is_reverse) { + appropriate_target_point = get_point_by_price(reverse_price(price)); + appropriate_left_point = get_bin_point_by_point( + appropriate_target_point + BIN_WIDTH * radius + ); + appropriate_right_point = get_bin_point_by_point( + appropriate_left_point - BIN_WIDTH * radius * 2 + ); + // appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; + } else { + appropriate_target_point = get_point_by_price(price); + appropriate_right_point = get_bin_point_by_point( + appropriate_target_point + BIN_WIDTH * radius + ); + appropriate_left_point = get_bin_point_by_point( + appropriate_right_point - BIN_WIDTH * radius * 2 + ); + // appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; + } + setLeftPoint(appropriate_left_point); + setRightPoint(appropriate_right_point); + return appropriate_target_point; + } + function getLeftPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(leftPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(leftPoint)); + } else { + price = get_price_by_point(leftPoint); + } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getRightPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(rightPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(rightPoint)); + } else { + price = get_price_by_point(rightPoint); + } + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function getTargetPrice() { + if ( + currentSelectedPool && + currentSelectedPool.pool_id && + !isInvalid(targetPoint) + ) { + let price; + if (pair_is_reverse) { + price = reverse_price(get_price_by_point(targetPoint)); + } else { + price = get_price_by_point(targetPoint); + } + + if (new BigNumber(price).isLessThan('0.00000001')) { + return price; + } else { + return toPrecision(price.toString(), 8); + } + } else { + return ''; + } + } + function get_point_by_price(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate_point = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const point = getPointByPrice(point_delta, price, decimalRate_point); + return point; + } + function get_price_by_point(point: number) { + const decimalRate_price = + Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); + return getPriceByPoint(point, decimalRate_price); + } + function get_bin_point_by_price(price: string) { + const { point_delta } = currentSelectedPool; + const decimalRate = + Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); + const appropriate_point = getBinPointByPrice( + point_delta, + price, + decimalRate, + SLOT_NUMBER + ); + return appropriate_point; + } + function get_bin_point_by_point(point: number) { + const { point_delta } = currentSelectedPool; + return getBinPointByPoint(point_delta, SLOT_NUMBER, point); + } + function get_slot_point_by_point(point: number) { + const { point_delta } = currentSelectedPool; + return getSlotPointByPoint(point_delta, point); + } + function getPair() { + if (pair_is_reverse) { + return `(${tokenX.symbol}/${tokenY.symbol})`; + } else { + return `(${tokenY.symbol}/${tokenX.symbol})`; + } + } + const is_mobile = isMobile(); + return ( +
+ {/* chart area */} +
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + if (isSignedIn) { + setChartTab('yours'); + } + }} + > + Yours + +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )} +
+ {isSignedIn && + !isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( +
+ +
+ )} +
+ {/* set price range area */} +
+ {/* price range mode area */} +
+
+ + + + {getPair()} + +
+ +
+ { + setPriceRangeMode('by_range'); + }} + > + + + { + setPriceRangeMode('by_radius'); + changeRadius(radius); + }} + > + + +
+
+ {/* content */} +
+ {/* target price input box */} +
+ + + + +
+ + {/* radius input box */} +
+ + + + +
+ + {/* min price input box */} +
+ + + + { + if (pair_is_reverse) { + return get_bin_point_by_price(reverse_price(price)); + } else { + return get_bin_point_by_price(price); + } + }} + disbaled={priceRangeMode === 'by_radius'} + customPrice={leftCustomPrice} + getPrice={getLeftPrice} + setCustomPrice={setLeftCustomPrice} + inputStatus={leftInputStatus} + setInputStatus={setLeftInputStatus} + setPoint={setLeftPoint} + point={leftPoint} + > +
+ + {/* max price input box */} +
+ + + + { + if (pair_is_reverse) { + return get_bin_point_by_price(reverse_price(price)); + } else { + return get_bin_point_by_price(price); + } + }} + customPrice={rightCustomPrice} + getPrice={getRightPrice} + setCustomPrice={setRightCustomPrice} + inputStatus={rightInputStatus} + setInputStatus={setRightInputStatus} + setPoint={setRightPoint} + point={rightPoint} + disbaled={priceRangeMode === 'by_radius'} + > +
+ + {/* bin number input box */} +
+ + + + +
+
+ {/* tip in foot */} +
+ {onlyAddYToken && currentPoint != rightPoint - 1 + ? `*Only ${currentSelectedPool?.token_y_metadata?.symbol} is needed in the price range you choose.` + : ''} + {onlyAddXToken + ? `*Only ${currentSelectedPool?.token_x_metadata?.symbol} is needed in the price range you choose.` + : ''} + {invalidRange ? : ''} +
+
+
+ ); +} diff --git a/src/pages/poolsV3/components/detail/BaseData.tsx b/src/pages/poolsV3/components/detail/BaseData.tsx new file mode 100644 index 000000000..b09a6527c --- /dev/null +++ b/src/pages/poolsV3/components/detail/BaseData.tsx @@ -0,0 +1,122 @@ +import React, { useEffect, useState } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { BigNumber } from 'bignumber.js'; +import { + toReadableNumber, + toInternationalCurrencySystem, +} from '../../../../utils/numbers'; +import { getV3Pool24VolumeById } from '~services/indexer'; +import { get_metadata } from '~services/swapV3'; +export function BaseData(props: any) { + const { poolDetail, tokenPriceList } = props; + const [volume24, setVolume24] = useState('0'); + const [user_liquidity_fee, set_user_liquidity_fee] = useState(); + useEffect(() => { + getV3Pool24VolumeById(poolDetail.pool_id) + .then((res) => { + setVolume24(res); + }) + .catch(() => {}); + get_metadata().then((res) => { + if (res) { + const { protocol_fee_rate } = res; + set_user_liquidity_fee((10000 - protocol_fee_rate) / 10000); + } + }); + }, []); + + function getTvl() { + const { + token_x_metadata, + token_y_metadata, + token_x, + token_y, + total_x, + total_y, + total_fee_x_charged, + total_fee_y_charged, + } = poolDetail; + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const totalX = new BigNumber(total_x).minus(total_fee_x_charged).toFixed(); + const totalY = new BigNumber(total_y).minus(total_fee_y_charged).toFixed(); + const tvlx = + Number(toReadableNumber(token_x_metadata.decimals, totalX)) * + Number(pricex); + const tvly = + Number(toReadableNumber(token_y_metadata.decimals, totalY)) * + Number(pricey); + const tvl = tvlx + tvly; + if (tvl == 0) { + return '$0'; + } else if (tvl < 0.01) { + return '<$0.01'; + } else { + return '$' + toInternationalCurrencySystem(tvl.toString(), 2); + } + } + function get24Volume() { + if (+volume24 == 0) { + return '$0'; + } else if (+volume24 < 0.01) { + return '<$0.01'; + } else { + return '$' + toInternationalCurrencySystem(volume24.toString(), 2); + } + } + + function get24Fee() { + const fee = poolDetail.fee; + const f = new BigNumber(fee) + .dividedBy(1000000) + .multipliedBy(user_liquidity_fee || 1) + .multipliedBy(volume24) + .toFixed(); + if (+f == 0) { + return '$0'; + } else if (+f < 0.01) { + return '<$0.01'; + } else { + return '$' + toInternationalCurrencySystem(f.toString(), 2); + } + } + return ( +
+ + } + value={getTvl()} + > + + } + value={get24Volume()} + > + + } + value={get24Fee()} + > +
+ ); +} +function DataBox(props: any) { + const { title, value, className } = props; + return ( +
+ {title} + {value} +
+ ); +} diff --git a/src/pages/poolsV3/components/detail/Chart.tsx b/src/pages/poolsV3/components/detail/Chart.tsx new file mode 100644 index 000000000..e76c738ab --- /dev/null +++ b/src/pages/poolsV3/components/detail/Chart.tsx @@ -0,0 +1,232 @@ +import React, { useEffect, useState, useMemo, useRef } from 'react'; +import { + VolumeChart, + TVLChart, + ChartType, + ChartChangeButton, + MobileChartChangeButton, +} from '../../../pools/DetailsPage'; +import { get_pool_marketdepth } from '~services/swapV3'; +import { useV3VolumeChart, useV3TvlChart } from '~state/pool'; +import { Card } from '~components/card/Card'; +import { isClientMobie } from '../../../../utils/device'; +import { ChartNoData } from '~components/icon/ChartNoData'; +import { getPriceByPoint, TOKEN_LIST_FOR_RATE } from '~services/commonV3'; +import { BigNumber } from 'bignumber.js'; +import { toPrecision, formatWithCommas } from '../../../../utils/numbers'; +import { SwitchButtonIcon } from '../../../../components/icon/V3'; +import { FormattedMessage, useIntl } from 'react-intl'; +import DclChart from '../../../../components/d3Chart/DclChart'; + +export function Chart(props: any) { + const { poolDetail, tokenPriceList } = props; + const [depthData, setDepthData] = useState(); + const [chartDisplay, setChartDisplay] = useState('liquidity'); + useEffect(() => { + getChartData(); + }, []); + async function getChartData() { + const depthData = await get_pool_marketdepth(poolDetail.pool_id); + setDepthData(depthData); + } + const monthVolume = useV3VolumeChart(poolDetail.pool_id); + const monthTVL = useV3TvlChart(poolDetail.pool_id); + return ( + + {chartDisplay === 'volume' ? ( + + ) : chartDisplay === 'tvl' ? ( + + ) : ( + + )} + + ); +} +let timer: any; +function LiquidityChart(props: any) { + const { data, chartDisplay, setChartDisplay } = props; + const { poolDetail, depthData } = data; + const isMobile = isClientMobie(); + const svgDefaultWidth = isMobile ? '380' : 750; + const [chartLoading, setChartLoading] = useState(true); + const [noData, setNoData] = useState(true); + const [rateDirection, setRateDirection] = useState(true); + const [svgWidth, setSvgWidth] = useState(svgDefaultWidth); + const refDom = useRef(null); + useEffect(() => { + if (poolDetail?.token_x_metadata) { + if ( + TOKEN_LIST_FOR_RATE.indexOf(poolDetail?.token_x_metadata.symbol) > -1 + ) { + setRateDirection(false); + } else { + setRateDirection(true); + } + } + }, [poolDetail]); + useEffect(() => { + if (depthData) { + const { liquidities } = depthData; + const list = Object.values(liquidities); + if (list.length == 0) { + setNoData(true); + } else { + setNoData(false); + } + setChartLoading(false); + } else { + setChartLoading(true); + } + }, [depthData, rateDirection]); + const rateDOM = useMemo(() => { + const { current_point, token_x_metadata, token_y_metadata } = poolDetail; + const rate = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + let price = getPriceByPoint(current_point, rate); + if (!rateDirection) { + price = new BigNumber(1).dividedBy(price).toFixed(); + } + let displayRate; + if (new BigNumber(price).isLessThan('0.001')) { + displayRate = ' < 0.001'; + } else { + displayRate = ` = ${formatWithCommas(toPrecision(price.toString(), 3))}`; + } + return ( + + 1 {rateDirection ? token_x_metadata.symbol : token_y_metadata.symbol} +   + + {displayRate}{' '} + {rateDirection ? token_y_metadata.symbol : token_x_metadata.symbol} + + + ); + }, [poolDetail, rateDirection]); + useEffect(() => { + if (isMobile) return; + if (refDom.current) { + setSvgWidth(refDom.current.clientWidth || svgDefaultWidth); + window.onresize = () => { + clearTimeout(timer); + timer = setTimeout(() => { + setSvgWidth(refDom?.current?.clientWidth || svgDefaultWidth); + }, 50); + }; + } + }, [refDom.current]); + function switchRate() { + setRateDirection(!rateDirection); + } + return ( + <> +
+
+
+ {rateDOM} + +
+ + + +
+ {isMobile ? ( + + ) : ( + + )} +
+ {!chartLoading && noData ? ( + + ) : ( +
+ +
+ )} + + ); +} +function EmptyLiquidityChart() { + return ( +
+
+
+ + + + +
+
+
+
+
+ {[ + '24', + '31', + '07', + '14', + '21', + '28', + '04', + '11', + '18', + '25', + '02', + '09', + ].map((d, i) => { + return
{d}
; + })} +
+
+
+ ); +} diff --git a/src/pages/poolsV3/components/detail/DetailFun.ts b/src/pages/poolsV3/components/detail/DetailFun.ts new file mode 100644 index 000000000..0ddef46f5 --- /dev/null +++ b/src/pages/poolsV3/components/detail/DetailFun.ts @@ -0,0 +1,35 @@ +import { UserLiquidityInfo } from '~services/commonV3'; +import { PoolInfo } from '~services/swapV3'; +import { toReadableNumber } from '../../../../utils/numbers'; + +export function get_unClaimed_fee_data( + liquidities: UserLiquidityInfo[], + poolDetail: PoolInfo, + tokenPriceList: any +) { + // UnClaimed fee + let total_amount_x_fee = 0; + let total_amount_y_fee = 0; + let total_tvl_fee = 0; + const { token_x_metadata, token_y_metadata } = poolDetail; + liquidities.forEach((liquidity: UserLiquidityInfo) => { + const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; + const unclaimed_fee_x_amount = toReadableNumber( + token_x_metadata.decimals, + unclaimed_fee_x + ); + const unclaimed_fee_y_amount = toReadableNumber( + token_y_metadata.decimals, + unclaimed_fee_y + ); + const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; + const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_fees_price = + Number(unclaimed_fee_x_amount) * Number(token_x_price) + + Number(unclaimed_fee_y_amount) * Number(token_y_price); + total_amount_x_fee += Number(unclaimed_fee_x_amount); + total_amount_y_fee += Number(unclaimed_fee_y_amount); + total_tvl_fee += Number(total_fees_price); + }); + return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; +} diff --git a/src/pages/poolsV3/components/detail/Icon.tsx b/src/pages/poolsV3/components/detail/Icon.tsx new file mode 100644 index 000000000..a4a4852a2 --- /dev/null +++ b/src/pages/poolsV3/components/detail/Icon.tsx @@ -0,0 +1,20 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +export function Icon(props: { + icon?: string; + className?: string; + style?: any; +}) { + const { icon, className, style } = props; + return icon ? ( + + ) : ( +
+ ); +} diff --git a/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx b/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx new file mode 100644 index 000000000..84037fc26 --- /dev/null +++ b/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx @@ -0,0 +1,41 @@ +import React from 'react'; +import { useHistory } from 'react-router'; +import { TOKEN_LIST_FOR_RATE, get_pool_name } from '~services/commonV3'; +import { GradientButton } from '~components/button/Button'; +import { FormattedMessage, useIntl } from 'react-intl'; +import { NoLiquidityIcon } from '../../../../components/icon/V3'; + +export function NoYourLiquditiesBox(props: any) { + const { poolDetail } = props; + const { token_x_metadata, pool_id } = poolDetail; + const history = useHistory(); + function goAddLiqudityPage() { + const [token_x, token_y, fee] = pool_id.split('|'); + let url_hash = pool_id; + if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { + url_hash = `${token_y}|${token_x}|${fee}`; + } + const pool_name = get_pool_name(url_hash); + history.push(`/addLiquidityV2#${pool_name}`); + } + return ( +
+ + + + +
+ { + e.stopPropagation(); + goAddLiqudityPage(); + }} + color="#fff" + className={`w-full h-11 text-center text-base text-white focus:outline-none`} + > + + +
+
+ ); +} diff --git a/src/pages/poolsV3/components/detail/RelatedFarmsBox.tsx b/src/pages/poolsV3/components/detail/RelatedFarmsBox.tsx new file mode 100644 index 000000000..3a88b43e2 --- /dev/null +++ b/src/pages/poolsV3/components/detail/RelatedFarmsBox.tsx @@ -0,0 +1,161 @@ +import React, { useEffect, useState } from 'react'; +import { + getEffectiveFarmList, + get_pool_name, + openUrl, +} from '~services/commonV3'; +import { FarmBoost, Seed } from '../../../../services/farm'; +import { + toPrecision, + toReadableNumber, + toInternationalCurrencySystem, +} from '../../../../utils/numbers'; +import { BigNumber } from 'bignumber.js'; +import { Fire, FarmBoardInDetailDCLPool } from '../../../../components/icon/V3'; +import { SolidButton } from '~components/button/Button'; +import { FormattedMessage, useIntl } from 'react-intl'; + +export function RelatedFarmsBox(props: any) { + const { poolDetail, tokenPriceList, sole_seed } = props; + const [related_seed, set_related_seed] = useState(); + const [farm_loading, set_farm_loading] = useState(true); + useEffect(() => { + if (poolDetail && Object.keys(tokenPriceList).length > 0) { + get_farms_data(); + } + }, [poolDetail, tokenPriceList, sole_seed]); + async function get_farms_data() { + if (sole_seed) { + set_related_seed(sole_seed); + } + set_farm_loading(false); + } + function totalTvlPerWeekDisplay() { + const farms = related_seed.farmList; + const rewardTokenIconMap = {}; + let totalPrice = 0; + const effectiveFarms = getEffectiveFarmList(farms); + effectiveFarms.forEach((farm: FarmBoost) => { + const { id, decimals, icon } = farm.token_meta_data; + const { daily_reward } = farm.terms; + rewardTokenIconMap[id] = icon; + const tokenPrice = tokenPriceList[id]?.price; + if (tokenPrice && tokenPrice != 'N/A') { + const tokenAmount = toReadableNumber(decimals, daily_reward); + totalPrice += +new BigNumber(tokenAmount) + .multipliedBy(tokenPrice) + .toFixed(); + } + }); + totalPrice = +new BigNumber(totalPrice).multipliedBy(7).toFixed(); + const totalPriceDisplay = + totalPrice == 0 + ? '-' + : '$' + toInternationalCurrencySystem(totalPrice.toString(), 2); + return totalPriceDisplay; + } + function isPending(seed: Seed) { + let pending: boolean = true; + const farms = seed.farmList; + for (let i = 0; i < farms.length; i++) { + if (farms[i].status != 'Created' && farms[i].status != 'Pending') { + pending = false; + break; + } + } + return pending; + } + function getTotalAprForSeed() { + const farms = related_seed.farmList; + let apr = 0; + const allPendingFarms = isPending(related_seed); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { + apr = +new BigNumber(item.apr).plus(apr).toFixed(); + } + }); + if (apr == 0) { + return '-'; + } else { + apr = +new BigNumber(apr).multipliedBy(100).toFixed(); + return toPrecision(apr.toString(), 2) + '%'; + } + } + function getAllRewardsSymbols() { + const tempMap = {}; + related_seed.farmList.forEach((farm: FarmBoost) => { + const { token_meta_data } = farm; + const { icon, id } = token_meta_data; + tempMap[id] = icon; + }); + const arr = Object.entries(tempMap); + return arr.slice(0, 5); + } + function go_farm() { + const { seed_id } = related_seed; + const [contractId, temp_pool_id] = seed_id.split('@'); + const [fixRange, pool_id, left_point, right_point] = + temp_pool_id.split('&'); + const link_params = `${get_pool_name( + pool_id + )}[${left_point}-${right_point}]`; + openUrl(`/v2farms/${link_params}-r`); + } + if (farm_loading) return null; + if (!related_seed) return null; + return ( +
+ +
+ Farm APR +
+ {getAllRewardsSymbols().map(([id, icon]: [string, string], index) => { + return ( + + ); + })} + {related_seed?.farmList.length > 5 ? ( +
+ ... +
+ ) : null} + + + {totalTvlPerWeekDisplay()}/week + +
+
+
+
+ + {getTotalAprForSeed()} + + +
+ + + +
+
+ ); +} diff --git a/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx b/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx new file mode 100644 index 000000000..d4ba195f2 --- /dev/null +++ b/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx @@ -0,0 +1,168 @@ +import React, { useEffect, useState } from 'react'; +import { useHistory } from 'react-router'; +import { UserLiquidityDetail } from './type'; +import { BigNumber } from 'bignumber.js'; +import { toPrecision, formatWithCommas } from '../../../../utils/numbers'; +import { isClientMobie } from '../../../../utils/device'; +import { + UserLiquidityInfo, + TOKEN_LIST_FOR_RATE, + displayNumberToAppropriateDecimals, + get_pool_name, + openUrl, +} from '~services/commonV3'; +import { FormattedMessage } from 'react-intl'; +import getConfig from '../../../../services/config'; +import { FarmBoost, Seed } from '../../../../services/farm'; +import { RemovePoolV3 } from '~components/pool/RemovePoolV3'; +const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); + +export function SelectLiquidityBox(props: any) { + const { + isOpen, + onRequestClose, + poolDetail, + operation, + tokenPriceList, + user_liquidities, + matched_seeds, + } = props; + + const [hoverHashId, setHoverHashId] = useState(''); + const [showRemoveBox, setShowRemoveBox] = useState(false); + const [showAddBox, setShowAddBox] = useState(false); + const history = useHistory(); + const { token_x_metadata, token_y_metadata } = poolDetail; + function displayLiqudityTvl(liquidityDetail: UserLiquidityDetail) { + const total = +liquidityDetail.total_liqudities_price; + if (total == 0) { + return '$0'; + } else if (total < 0.01) { + return '<$0.01'; + } else { + return '$' + formatWithCommas(toPrecision(total.toString(), 2)); + } + } + function displayLiqudityFee(liquidityDetail: UserLiquidityDetail) { + const total = +liquidityDetail.total_fees_price; + if (total == 0) { + return '$0'; + } else if (total < 0.01) { + return '<$0.01'; + } else { + return '$' + formatWithCommas(toPrecision(total.toString(), 2)); + } + } + function displayRange(liquidityDetail: UserLiquidityDetail) { + const { l_price, r_price } = liquidityDetail; + let display_l; + let display_r; + if ( + TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1 && + +r_price !== 0 && + +l_price !== 0 + ) { + display_l = new BigNumber(1).dividedBy(r_price).toFixed(); + display_r = new BigNumber(1).dividedBy(l_price).toFixed(); + } else { + display_l = l_price; + display_r = r_price; + } + display_l = displayNumberToAppropriateDecimals(display_l); + display_r = displayNumberToAppropriateDecimals(display_r); + return `${display_l} - ${display_r}`; + } + function hoverLine(hashId: string) { + setHoverHashId(hashId); + } + function getCurrentLiqudity(hashId: string) { + const c_l = user_liquidities.find((liquidity: UserLiquidityInfo) => { + if (liquidity.lpt_id.split('#')[1] == hashId) return true; + }); + return c_l; + } + function goAddLiqudityPage() { + const pool_id = poolDetail.pool_id; + const [token_x, token_y, fee] = pool_id.split('|'); + let url_hash = pool_id; + if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { + url_hash = `${token_y}|${token_x}|${fee}`; + } + history.push(`/addLiquidityV2#${url_hash}`); + } + function displayFarmStatus(liquidity: UserLiquidityInfo) { + const is_in_farming = + liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; + if (is_in_farming) { + return ( + + ); + } else { + return ( + + ); + } + } + function go_farm(liquidity: UserLiquidityInfo) { + const { mft_id } = liquidity; + const [fixRange, pool_id, left_point, right_point] = mft_id.split('&'); + const link_params = `${get_pool_name( + pool_id + )}[${left_point}-${right_point}]`; + const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); + const temp_seeds = (matched_seeds || []).filter((seed: Seed) => { + return seed_id == seed.seed_id; + }); + let actives: FarmBoost[] = []; + temp_seeds.forEach((seed: Seed) => { + const { farmList } = seed; + const temp = farmList.filter((farm: FarmBoost) => { + return farm.status != 'Ended'; + }); + actives = actives.concat(temp); + }); + let url; + if (actives.length > 0) { + url = `/v2farms/${link_params}-r`; + } else { + url = `/v2farms/${link_params}-e`; + } + openUrl(url); + } + function is_in_farming(liquidity: UserLiquidityInfo) { + const is_in_farming = + liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; + return is_in_farming; + } + const isMobile = isClientMobie(); + const has_no_related_seed = + matched_seeds?.length == 0 && + user_liquidities?.every( + (liquidity: UserLiquidityInfo) => +(liquidity.part_farm_ratio || 0) == 0 + ); + return operation == 'remove' && isOpen ? ( + + ) : null; +} diff --git a/src/pages/poolsV3/components/detail/TablePool.tsx b/src/pages/poolsV3/components/detail/TablePool.tsx new file mode 100644 index 000000000..81397d025 --- /dev/null +++ b/src/pages/poolsV3/components/detail/TablePool.tsx @@ -0,0 +1,729 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import { FormattedMessage, useIntl } from 'react-intl'; +import ReactTooltip from 'react-tooltip'; +import { BigNumber } from 'bignumber.js'; +import { + toPrecision, + toReadableNumber, + toInternationalCurrencySystem, +} from '../../../../utils/numbers'; +import { Icon } from './Icon'; +import { toRealSymbol } from '~utils/token'; +import { TokenLinks } from '~components/tokens/Token'; +import { FiArrowUpRight } from 'react-icons/fi'; +import { TokenMetadata } from '~services/ft-contract'; +import { RencentTabKey } from './type'; +import { useDCLPoolTransaction } from '~state/pool'; +import { numberWithCommas } from '~pages/Orderly/utiles'; +import { HiOutlineExternalLink } from 'react-icons/hi'; +import getConfig from '../../../../services/config'; +import Big from 'big.js'; +import { + sort_tokens_by_base, + openUrl, + reverse_price, +} from '~services/commonV3'; +import { pointToPrice } from '~services/swapV3'; + +export function TablePool(props: any) { + const { poolDetail, tokenPriceList } = props; + const [tokens, setTokens] = useState([]); + const intl = useIntl(); + useEffect(() => { + const { + token_x, + token_y, + total_x, + total_y, + token_x_metadata, + token_y_metadata, + total_fee_x_charged, + total_fee_y_charged, + } = poolDetail; + const pricex = tokenPriceList[token_x]?.price || 0; + const pricey = tokenPriceList[token_y]?.price || 0; + const totalX = new BigNumber(total_x).minus(total_fee_x_charged).toFixed(); + const totalY = new BigNumber(total_y).minus(total_fee_y_charged).toFixed(); + const amountx = toReadableNumber(token_x_metadata.decimals, totalX); + const amounty = toReadableNumber(token_y_metadata.decimals, totalY); + const tvlx = Number(amountx) * Number(pricex); + const tvly = Number(amounty) * Number(pricey); + const temp_list = []; + const temp_tokenx = { + meta: token_x_metadata, + amount: amountx, + tvl: tvlx, + }; + const temp_tokeny = { + meta: token_y_metadata, + amount: amounty, + tvl: tvly, + }; + temp_list.push(temp_tokenx, temp_tokeny); + setTokens(temp_list); + }, [Object.keys(tokenPriceList).length]); + function valueOfNearTokenTip() { + const tip = intl.formatMessage({ id: 'awesomeNear_verified_token' }); + let result: string = `
${tip}
`; + return result; + } + function displayAmount(amount: string) { + if (+amount == 0) { + return '0'; + } else if (+amount < 0.01) { + return '< 0.01'; + } else { + return toInternationalCurrencySystem(amount.toString(), 2); + } + } + function displayTvl(token: any) { + const { tvl } = token; + if (+tvl == 0 && !tokenPriceList[token.meta.id]?.price) { + return '$ -'; + } else if (+tvl == 0) { + return '$0'; + } else if (+tvl < 0.01) { + return '< $0.01'; + } else { + return '$' + toInternationalCurrencySystem(tvl.toString(), 2); + } + } + return ( +
+
+ +
+
+
+
+ +
+ +
+ +
+ +
+ +
+
+ {tokens.map((token: any, i: number) => ( +
+
+ + +
+
+ {displayAmount(token.amount)} +
+
+ {displayTvl(token)} +
+
+ ))} +
+ + t.meta)} + pool_id={poolDetail.pool_id} + > +
+ ); +} +const REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL = + 'REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL'; + +function RecentTransactions({ + pool_id, + tokens, +}: { + pool_id: string; + tokens: TokenMetadata[]; +}) { + const storedTab = sessionStorage.getItem( + REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL + ) as RencentTabKey; + + const { swapTransactions, liquidityTransactions, limitOrderTransactions } = + useDCLPoolTransaction({ pool_id }); + const [tab, setTab] = useState(storedTab || 'swap'); + + const onChangeTab = (tab: RencentTabKey) => { + sessionStorage.setItem(REF_FI_RECENT_TRANSACTION_TAB_KEY_DCL, tab); + setTab(tab); + }; + + const renderSwapTransactions = swapTransactions.map((tx) => { + const swapIn = tokens.find((t) => t.id === tx.token_in); + + const swapOut = tokens.find((t) => t.id === tx.token_out); + + if (!swapIn || !swapOut) return null; + + const swapInAmount = toReadableNumber(swapIn.decimals, tx.amount_in); + const displayInAmount = + Number(swapInAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapInAmount, 6)); + + const swapOutAmount = toReadableNumber(swapOut.decimals, tx.amount_out); + + const displayOutAmount = + Number(swapOutAmount) < 0.01 + ? '<0.01' + : numberWithCommas(toPrecision(swapOutAmount, 6)); + + const txLink = ( + + + + ); + + return ( +
+ + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + + + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + +
+ + {(tx.method_name.toLowerCase().indexOf('add') > -1 || + tx.method_name.toLowerCase().indexOf('append') > -1) && + 'Add'} + + {tx.method_name.toLowerCase().indexOf('remove') > -1 && 'Remove'} + + + {Big(AmountIn || 0).gt(0) ? ( + <> + + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + + + ) : null} + {Big(AmountIn || 0).gt(0) && Big(AmountOut || 0).gt(0) ? ( + + + ) : null} + {Big(AmountOut || 0).gt(0) ? ( + <> + {' '} + + {displayOutAmount} + + + {toRealSymbol(swapOut.symbol)} + + + ) : null} + + { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + +
+ {tx.method_name.toLowerCase().indexOf('cancelled') > -1 && 'Cancel'} + + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Place'} + +
+ + {displayInAmount} + + + + {toRealSymbol(swapIn.symbol)} + +
+
+
+ + {displayOutAmount} + + + + {toRealSymbol(swapOut.symbol)} + +
+
+
+ + {numberWithCommas(toPrecision(display_price, 4))} + + + + {toRealSymbol(sort_tokens?.[1]?.symbol)}/ + {toRealSymbol(sort_tokens?.[0]?.symbol)} + +
+
+ { + openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); + }} + > + + {tx.timestamp} + + {txLink} + +
+ + + + + + {tab === 'limit_order' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + +
+ {tab === 'liquidity' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + + + +
+ +
+ + + + + + + {tab === 'limit_order' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + {renderTransactions} +
+ {tab === 'liquidity' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + {tab === 'swap' && ( + + )} + + {tab === 'liquidity' && ( + + )} + {tab === 'swap' && ( + + )} + + {tab === 'limit_order' && ( + + )} + + + + + + +
+
+
+ + ); +} diff --git a/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx b/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx new file mode 100644 index 000000000..82e5a0b15 --- /dev/null +++ b/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx @@ -0,0 +1,177 @@ +import React, { useEffect, useState } from 'react'; +import { toPrecision, formatWithCommas } from '../../../../utils/numbers'; +import { FormattedMessage } from 'react-intl'; +import { claim_all_liquidity_fee } from '~services/swapV3'; +import { UserLiquidityInfo } from '~services/commonV3'; +import { ButtonTextWrapper } from '~components/button/Button'; +import _ from 'lodash'; +import { Icon } from './Icon'; +import { get_unClaimed_fee_data } from './DetailFun'; +export function UnclaimedFeesBox(props: any) { + const { poolDetail, liquidities, tokenPriceList } = props; + const { token_x_metadata, token_y_metadata } = poolDetail; + const [user_liquidities_total, set_user_liquidities_total] = + useState>(); + const [cliam_loading, set_cliam_loading] = useState(false); + useEffect(() => { + if (liquidities) { + const [total_tvl_fee, total_amount_x_fee, total_amount_y_fee] = + get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); + set_user_liquidities_total({ + total_amount_x_fee, + total_amount_y_fee, + total_tvl_fee, + }); + } + }, [liquidities, Object.keys(tokenPriceList).length]); + function getTotalLiquditiesFee() { + const total_tvl = user_liquidities_total?.total_tvl_fee || 0; + if (total_tvl == 0) { + return '$0'; + } else if (total_tvl < 0.01) { + return '<$0.01'; + } else { + return '$' + formatWithCommas(toPrecision(total_tvl.toString(), 2)); + } + } + function getTotalFeeAmount() { + const total_amount_x = user_liquidities_total?.total_amount_x_fee || 0; + const total_amount_y = user_liquidities_total?.total_amount_y_fee || 0; + let display_amount_x; + let display_amount_y; + const total_amount_x_y = total_amount_x + total_amount_y; + if (total_amount_x == 0) { + display_amount_x = '0'; + } else if (total_amount_x < 0.001) { + display_amount_x = '<0.001'; + } else { + display_amount_x = toPrecision(total_amount_x.toString(), 3); + } + if (total_amount_y == 0) { + display_amount_y = '0'; + } else if (total_amount_y < 0.001) { + display_amount_y = '<0.001'; + } else { + display_amount_y = toPrecision(total_amount_y.toString(), 3); + } + + return { + display_amount_x, + display_amount_y, + total_amount_x_y, + }; + } + function claimRewards() { + if (total_amount_x_y == 0) return; + set_cliam_loading(true); + const lpt_ids: string[] = []; + liquidities.forEach((liquidity: UserLiquidityInfo) => { + const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; + if (+unclaimed_fee_x > 0 || +unclaimed_fee_y > 0) { + lpt_ids.push(liquidity.lpt_id); + } + }); + claim_all_liquidity_fee({ + token_x: token_x_metadata, + token_y: token_y_metadata, + lpt_ids, + }); + } + const { display_amount_x, display_amount_y, total_amount_x_y } = + getTotalFeeAmount(); + return ( + <> + {/* for pc */} +
+
+ + + + + {getTotalLiquditiesFee()} + +
+
+
+
+ + {display_amount_x} +
+
+ + {display_amount_y} +
+
+ +
+ } + /> +
+
+
+ {/* for mobile */} +
+
+ + + + + {getTotalLiquditiesFee()} + +
+
+
+ + + {token_x_metadata.symbol} + +
+ {display_amount_x} +
+
+
+ + + {token_y_metadata.symbol} + +
+ {display_amount_y} +
+
+ } + /> +
+
+ + ); +} diff --git a/src/pages/poolsV3/components/detail/UserTabBox.tsx b/src/pages/poolsV3/components/detail/UserTabBox.tsx new file mode 100644 index 000000000..d0a6eed54 --- /dev/null +++ b/src/pages/poolsV3/components/detail/UserTabBox.tsx @@ -0,0 +1,189 @@ +import React, { useEffect, useState, useContext } from 'react'; +import Modal from 'react-modal'; +import { FormattedMessage } from 'react-intl'; +import { PoolInfo } from '~services/swapV3'; +import { UserLiquidityInfo } from '~services/commonV3'; +import { Seed } from '../../../../services/farm'; +import _ from 'lodash'; +import { YourLiquidityBox } from './YourLiquidityBox'; +import { UnclaimedFeesBox } from './UnclaimedFeesBox'; +import { NoYourLiquditiesBox } from './NoYourLiquditiesBox'; +import { GradientButton } from '~components/button/Button'; +import { WalletContext } from '../../../../utils/wallets-integration'; + +export function UserButtonBox(props: { + poolDetail: PoolInfo; + liquidities: UserLiquidityInfo[]; + tokenPriceList: any; + matched_seeds: Seed[]; +}) { + const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; + const [show, setShow] = useState(false); + const [tab, setTab] = useState(); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + if (!isSignedIn) return null; + return ( + <> + +
+ { + setShow(true); + setTab(1); + }} + color="#fff" + borderRadius={'8px'} + className={`flex-grow w-1 h-10 text-center text-sm text-white focus:outline-none mr-2.5`} + > + You Liquidity + +
{ + setShow(true); + setTab(2); + }} + > + Unclaimed Fees +
+
+ + ); +} +function UserTabBox(props: { + poolDetail: PoolInfo; + liquidities: UserLiquidityInfo[]; + tokenPriceList: any; + matched_seeds: Seed[]; + show: boolean; + setShow: Function; + tab: number; +}) { + const { + poolDetail, + liquidities, + tokenPriceList, + matched_seeds, + show, + setShow, + tab, + } = props; + const [tabActive, setTabActive] = useState(1); + const { globalState } = useContext(WalletContext); + const isSignedIn = globalState.isSignedIn; + useEffect(() => { + setTabActive(tab); + }, [tab]); + function switchTab(tabIndex: number) { + setTabActive(tabIndex); + } + function onRequestClose() { + setShow(false); + } + const no_data = !isSignedIn || (liquidities && liquidities.length == 0); + + return ( + +
+
+
{ + switchTab(1); + }} + > + + + + +
+
{ + switchTab(2); + }} + > + + + + +
+
+ {no_data ? ( + + ) : ( + <> + {tabActive == 1 ? ( + + ) : ( + + )} + + )} +
+
+ ); +} diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx new file mode 100644 index 000000000..26a4f6256 --- /dev/null +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -0,0 +1,635 @@ +import React, { useEffect, useState } from 'react'; +import _ from 'lodash'; +import Big from 'big.js'; +import { + toPrecision, + toReadableNumber, + toInternationalCurrencySystem, +} from '../../../../utils/numbers'; +import { TokenMetadata } from '~services/ft-contract'; +import { toRealSymbol } from '~utils/token'; +import { useHistory } from 'react-router'; +import { BigNumber } from 'bignumber.js'; +import { FormattedMessage } from 'react-intl'; +import { PoolInfo } from '~services/swapV3'; +import { + UserLiquidityInfo, + getPriceByPoint, + CONSTANT_D, + getXAmount_per_point_by_Lx, + getYAmount_per_point_by_Ly, + TOKEN_LIST_FOR_RATE, + displayNumberToAppropriateDecimals, + get_pool_name, + openUrl, + get_account_24_apr, + get_token_amount_in_user_liquidities, +} from '~services/commonV3'; +import { Seed } from '../../../../services/farm'; +import { useWalletSelector } from '../../../../context/WalletSelectorContext'; +import { GradientButton, OprationButton } from '~components/button/Button'; +import { findRangeIntersection } from '~components/pool/YourLiquidityV2'; +import DclChart from '../../../../components/d3Chart/DclChart'; +import { IDCLAccountFee } from '../../../../components/d3Chart/interfaces'; +import { + formatPercentage, + formatWithCommas_usd, + formatNumber, +} from '../../../../components/d3Chart/utils'; +import { getDCLAccountFee } from '../../../../services/indexer'; +import { UserLiquidityDetail } from './type'; +import { get_unClaimed_fee_data } from './DetailFun'; +import { SelectLiquidityBox } from './SelectLiquidityBox'; +export function YourLiquidityBox(props: { + poolDetail: PoolInfo; + liquidities: UserLiquidityInfo[]; + tokenPriceList: any; + matched_seeds: Seed[]; +}) { + const { poolDetail, liquidities, tokenPriceList, matched_seeds } = props; + const [user_liquidities_detail, set_user_liquidities_detail] = useState< + UserLiquidityDetail[] + >([]); + const [showSelectLiquidityBox, setShowSelectLiquidityBox] = useState(false); + const [accountAPR, setAccountAPR] = useState(''); + const [earned_fee, set_earned_fee] = useState(''); + const [earned_fee_x_amount, set_earned_fee_x_amount] = useState(); + const [earned_fee_y_amount, set_earned_fee_y_amount] = useState(); + const [operationType, setOperationType] = useState('add'); + const [hover, setHover] = useState(false); + const [noReverseRange, setNoReverseRange] = useState(true); + const [removeButtonTip, setRemoveButtonTip] = useState(false); + const [is_in_farming, set_is_in_farming] = useState(false); + const [is_in_farming_done, set_is_in_farming_done] = useState(false); + const { token_x_metadata, token_y_metadata, pool_id } = poolDetail; + const { accountId } = useWalletSelector(); + const history = useHistory(); + let pair_is_reverse = false; + if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { + pair_is_reverse = true; + } + useEffect(() => { + if (liquidities) { + const temp_list: UserLiquidityDetail[] = []; + liquidities.forEach((liquidity: UserLiquidityInfo) => { + if (!liquidity) return; + const { + left_point, + right_point, + lpt_id, + amount, + unclaimed_fee_x, + unclaimed_fee_y, + } = liquidity; + const { amount_x, amount_y } = get_amount_x_y(liquidity); + const unclaimed_fee_x_amount = toReadableNumber( + token_x_metadata.decimals, + unclaimed_fee_x + ); + const unclaimed_fee_y_amount = toReadableNumber( + token_y_metadata.decimals, + unclaimed_fee_y + ); + const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; + const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_liqudities_price = + Number(amount_x) * Number(token_x_price) + + Number(amount_y) * Number(token_y_price); + const total_fees_price = + Number(unclaimed_fee_x_amount) * Number(token_x_price) + + Number(unclaimed_fee_y_amount) * Number(token_y_price); + const decimalRate = + Math.pow(10, token_x_metadata.decimals) / + Math.pow(10, token_y_metadata.decimals); + const l_price = getPriceByPoint(left_point, decimalRate); + const r_price = getPriceByPoint(right_point, decimalRate); + const temp: UserLiquidityDetail = { + total_liqudities_price: total_liqudities_price.toString(), + total_fees_price: total_fees_price.toString(), + amount_x, + amount_y, + unclaimed_fee_x_amount, + unclaimed_fee_y_amount, + + hashId: lpt_id.split('#')[1], + l_price, + r_price, + }; + temp_list.push(temp); + }); + set_user_liquidities_detail(temp_list); + } + }, [liquidities, Object.keys(tokenPriceList).length]); + useEffect(() => { + if ( + liquidities && + poolDetail && + tokenPriceList && + Object.keys(tokenPriceList).length + ) { + get_24_apr_and_fee(); + } + }, [poolDetail, tokenPriceList, liquidities]); + useEffect(() => { + if (liquidities) { + const target = liquidities.find((l: UserLiquidityInfo) => { + return Big(l.part_farm_ratio || 0).gt(0); + }); + if (target) { + set_is_in_farming(true); + } else { + set_is_in_farming(false); + } + set_is_in_farming_done(true); + } + }, [liquidities]); + async function get_24_apr_and_fee() { + let apr_24 = '0'; + let total_fee_earned = '0'; + let total_earned_fee_x; + let total_earned_fee_y; + const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ + pool_id, + account_id: accountId, + }); + if (dcl_fee_result) { + // 24h profit + apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + // total unClaimed fee + const [ + unClaimed_tvl_fee, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); + // total earned fee + const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; + total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + Big(total_fee_x || 0).toFixed() + ); + total_earned_fee_x = Big(total_earned_fee_x) + .plus(unClaimed_amount_x_fee) + .toFixed(); + + total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + Big(total_fee_y || 0).toFixed() + ); + total_earned_fee_y = Big(total_earned_fee_y) + .plus(unClaimed_amount_y_fee) + .toFixed(); + + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value + .plus(total_earned_fee_y_value) + .toFixed(); + total_fee_earned = Big(total_fee_earned) + .plus(unClaimed_tvl_fee) + .toFixed(); + } + set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); + set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); + set_earned_fee(formatWithCommas_usd(total_fee_earned)); + setAccountAPR(formatPercentage(apr_24)); + } + function get_amount_x_y(liquidity: UserLiquidityInfo) { + const [tokenX, tokenY] = [token_x_metadata, token_y_metadata]; + const { left_point, right_point, amount: L } = liquidity; + const { current_point } = poolDetail; + let amount_x = '0'; + let amount_y = '0'; + // in range + if (current_point >= left_point && right_point > current_point) { + const tokenYAmount = getY( + left_point, + current_point, + current_point, + L, + tokenY + ); + const tokenXAmount = getX(current_point + 1, right_point, L, tokenX); + const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); + amount_x = new BigNumber(tokenXAmount).plus(amountx).toFixed(); + amount_y = new BigNumber(tokenYAmount).plus(amounty).toFixed(); + } + // only y token + if (current_point >= right_point) { + const tokenYAmount = getY( + left_point, + right_point, + current_point, + L, + tokenY + ); + amount_y = tokenYAmount; + } + // only x token + if (left_point > current_point) { + const tokenXAmount = getX(left_point, right_point, L, tokenX); + amount_x = tokenXAmount; + } + return { + amount_x, + amount_y, + }; + } + function getX( + leftPoint: number, + rightPoint: number, + L: string, + token: TokenMetadata + ) { + const x = new BigNumber(L) + .multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) + ) + .toFixed(); + return toReadableNumber(token.decimals, toPrecision(x, 0)); + } + function getY( + leftPoint: number, + rightPoint: number, + currentPoint: number, + L: string, + token: TokenMetadata + ) { + const y = new BigNumber(L).multipliedBy( + (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - + Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / + (Math.sqrt(CONSTANT_D) - 1) + ); + const y_result = y.toFixed(); + return toReadableNumber(token.decimals, toPrecision(y_result, 0)); + } + function get_X_Y_In_CurrentPoint( + tokenX: TokenMetadata, + tokenY: TokenMetadata, + L: string + ) { + const { liquidity, liquidity_x, current_point } = poolDetail; + const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); + let Ly = '0'; + let Lx = '0'; + // only remove y + if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { + Ly = L; + } else { + // have x and y + Ly = liquidity_y_big.toFixed(); + Lx = new BigNumber(L).minus(Ly).toFixed(); + } + const amountX = getXAmount_per_point_by_Lx(Lx, current_point); + const amountY = getYAmount_per_point_by_Ly(Ly, current_point); + const amountX_read = toReadableNumber( + tokenX.decimals, + toPrecision(amountX, 0) + ); + const amountY_read = toReadableNumber( + tokenY.decimals, + toPrecision(amountY, 0) + ); + return { amountx: amountX_read, amounty: amountY_read }; + } + function getTotalLiquditiesTvl() { + const [total_x, total_y] = get_tokens_amount_liquidities(); + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_x_value = Big(price_x).mul(total_x); + const total_y_value = Big(price_y).mul(total_y); + const total_value = total_x_value.plus(total_y_value).toFixed(); + const total_value_display = formatWithCommas_usd(total_value); + return total_value_display; + } + function get_tokens_amount_liquidities() { + const [total_x, total_y] = get_token_amount_in_user_liquidities({ + user_liquidities: liquidities, + pool: poolDetail, + token_x_metadata, + token_y_metadata, + }); + return [total_x, total_y]; + } + function getTotalTokenAmount() { + const [total_x, total_y] = get_tokens_amount_liquidities(); + let display_total_x = '0'; + let display_total_y = '0'; + if (+total_x == 0) { + display_total_x = '0'; + } else if (+total_x < 0.01) { + display_total_x = '<0.01'; + } else { + display_total_x = toInternationalCurrencySystem(total_x, 3); + } + if (+total_y == 0) { + display_total_y = '0'; + } else if (+total_y < 0.01) { + display_total_y = '<0.01'; + } else { + display_total_y = toInternationalCurrencySystem(total_y, 3); + } + return { + total_x: display_total_x, + total_y: display_total_y, + }; + } + function removeLiquidity() { + setOperationType('remove'); + setShowSelectLiquidityBox(true); + } + function getGroupLiquidities() { + const tokenMetadata_x_y = [token_x_metadata, token_y_metadata]; + + let rate_need_to_reverse_display: boolean; + + if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata.symbol) > -1) { + rate_need_to_reverse_display = true; + } + + if (!noReverseRange) { + rate_need_to_reverse_display = !rate_need_to_reverse_display; + } + + function getRateMapTokens() { + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + if (rate_need_to_reverse_display) { + return `${tokenX.symbol}/${tokenY.symbol}`; + } else { + return `${tokenY.symbol}/${tokenX.symbol}`; + } + } + } + + function getRate( + direction: string, + left_point: number, + right_point: number + ) { + let value = ''; + if (tokenMetadata_x_y) { + const [tokenX, tokenY] = tokenMetadata_x_y; + const decimalRate = + Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); + if (direction == 'left') { + value = getPriceByPoint(left_point, decimalRate); + } else if (direction == 'right') { + value = getPriceByPoint(right_point, decimalRate); + } + if (rate_need_to_reverse_display && +value !== 0) { + value = new BigNumber(1).dividedBy(value).toFixed(); + } + } + return value; + } + + const priceRangeList = + liquidities?.map((l) => { + return [ + +getRate( + rate_need_to_reverse_display ? 'right' : 'left', + l.left_point, + l.right_point + ), + +getRate( + rate_need_to_reverse_display ? 'left' : 'right', + l.left_point, + l.right_point + ), + ]; + }) || []; + + const rangeList = findRangeIntersection(priceRangeList); + return { + rangeList, + rateMapTokens: getRateMapTokens(), + }; + } + + const groupedData = getGroupLiquidities(); + + function goAddliquidityV2() { + const url_pool_id = get_pool_name(poolDetail.pool_id); + history.push(`/addLiquidityV2#${url_pool_id}`); + } + + return ( +
+
+ + + + + {getTotalLiquditiesTvl()} + +
+ {/* chart area */} +
+ +
+
+
+ + + + + + {groupedData.rangeList.map((range: number[], i: number) => { + return ( +
+ {displayNumberToAppropriateDecimals(range[0])} + - + {displayNumberToAppropriateDecimals(range[1])} + {groupedData.rangeList.length > 1 && + i < groupedData.rangeList.length - 1 && ( + , + )} +
+ ); + })} + + { + setNoReverseRange(!noReverseRange); + }} + > + {groupedData.rateMapTokens} + +
+
+ +
+ + + + +
+ {getTotalTokenAmount().total_x} + + {token_x_metadata.symbol} + + + + + {getTotalTokenAmount().total_y} + + {token_y_metadata.symbol} +
+
+ +
+ APR(24h) + +
+ {accountAPR || '-'} +
+
+ +
+ + + + +
{ + setHover(true); + }} + onMouseLeave={() => { + setHover(false); + }} + > +
+ {hover && ( +
+
+ {toRealSymbol(token_x_metadata.symbol)} + {earned_fee_x_amount || '-'} +
+ +
+ {toRealSymbol(token_y_metadata.symbol)} + + {earned_fee_y_amount || '-'} +
+
+ )} +
+ {earned_fee || '-'} +
+
+
+
+ { + e.stopPropagation(); + goAddliquidityV2(); + }} + color="#fff" + borderRadius={'8px'} + className={`flex-grow w-1 h-11 text-center text-sm text-white focus:outline-none mr-2.5`} + > + + +
{ + if (is_in_farming) { + setRemoveButtonTip(true); + } + }} + onMouseLeave={() => { + if (is_in_farming) { + setRemoveButtonTip(false); + } + }} + > + { + e.stopPropagation(); + removeLiquidity(); + }} + disabled={is_in_farming || !is_in_farming_done} + color="#fff" + className={`flex-grow w-1 h-11 items-center justify-center text-center text-sm text-white focus:outline-none font-semibold bg-bgGreyDefault hover:bg-bgGreyHover ${ + is_in_farming || !is_in_farming_done + ? 'opacity-30 pointer-events-none' + : '' + } }`} + > + + +
+ You have liquidity in farm, please unstake from{' '} + { + localStorage.setItem('BOOST_FARM_TAB', 'yours'); + openUrl('/v2farms'); + }} + > + Your Farm + {' '} + first. +
+
+
+ { + setShowSelectLiquidityBox(false); + }} + poolDetail={poolDetail} + user_liquidities_detail={user_liquidities_detail} + user_liquidities={liquidities} + operation={operationType} + tokenPriceList={tokenPriceList} + matched_seeds={matched_seeds} + style={{ + overlay: { + backdropFilter: 'blur(15px)', + WebkitBackdropFilter: 'blur(15px)', + }, + content: { + outline: 'none', + transform: 'translate(-50%, -50%)', + }, + }} + > +
+ ); +} diff --git a/src/pages/poolsV3/components/detail/type.ts b/src/pages/poolsV3/components/detail/type.ts new file mode 100644 index 000000000..b83e973cf --- /dev/null +++ b/src/pages/poolsV3/components/detail/type.ts @@ -0,0 +1,16 @@ +export interface ParamTypes { + id: string; +} +export interface UserLiquidityDetail { + total_liqudities_price: string; + total_fees_price: string; + amount_x: string; + amount_y: string; + hashId: string; + l_price: string; + r_price: string; + unclaimed_fee_y_amount?: string; + unclaimed_fee_x_amount?: string; +} + +export type RencentTabKey = 'swap' | 'liquidity' | 'limit_order'; From 40c57d36d2b6c39308d93ec1f107843c77e7a81e Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 19 Sep 2023 23:02:03 +0800 Subject: [PATCH 125/204] update --- src/components/d3Chart/utils.ts | 4 +- src/components/pool/RemovePoolV3.tsx | 4 +- src/components/pool/RemovePoolV3Copy.tsx | 1844 ----------------- src/components/portfolio/Farms.tsx | 43 +- .../poolsV3/components/detail/UserTabBox.tsx | 2 +- .../components/detail/YourLiquidityBox.tsx | 6 +- src/services/indexer.ts | 2 +- 7 files changed, 13 insertions(+), 1892 deletions(-) delete mode 100644 src/components/pool/RemovePoolV3Copy.tsx diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index c0fe8e242..a5c37850c 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -28,7 +28,7 @@ export const formatPercentage = (v: string | number) => { return big.toFixed(2, 1) + '%'; } }; -export const formatNumber = (v: string | number) => { +export const formatNumber = (v: string | number, type?: 'down' | 'both') => { if (isInvalid(v)) return '-'; const big = Big(v); if (big.eq(0)) { @@ -36,7 +36,7 @@ export const formatNumber = (v: string | number) => { } else if (big.lt(0.01)) { return '<0.01'; } else { - return big.toFixed(2, 1); + return type == 'down' ? big.toFixed(2, 0) : big.toFixed(2, 1); } }; export const formatWithCommas_number = (v: string | number) => { diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 78ccff059..78960460a 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -127,8 +127,8 @@ export const RemovePoolV3 = (props: any) => { const { total_token_x_amount, total_token_y_amount, total_value } = get_minimum_received_data(); return [ - formatNumber(total_token_x_amount), - formatNumber(total_token_y_amount), + formatNumber(total_token_x_amount, 'down'), + formatNumber(total_token_y_amount, 'down'), formatWithCommas_usd(total_value), ]; } diff --git a/src/components/pool/RemovePoolV3Copy.tsx b/src/components/pool/RemovePoolV3Copy.tsx deleted file mode 100644 index 3dc0567d2..000000000 --- a/src/components/pool/RemovePoolV3Copy.tsx +++ /dev/null @@ -1,1844 +0,0 @@ -import React, { useEffect, useMemo, useState, useContext, useRef } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { WalletContext } from '../../utils/wallets-integration'; -import { Card } from '~components/card/Card'; -import { ModalClose } from '~components/icon'; -import { TokenMetadata } from '../../services/ft-contract'; -import ReactSlider from 'react-slider'; -import { - GradientButton, - ButtonTextWrapper, - ConnectToNearBtn, -} from '../../components/button/Button'; -import { PoolSlippageSelectorV3 } from '~components/forms/SlippageSelector'; -import Modal from 'react-modal'; -import BigNumber from 'bignumber.js'; -import { - toPrecision, - toReadableNumber, - toNonDivisibleNumber, -} from '~utils/numbers'; -import { - getPriceByPoint, - CONSTANT_D, - UserLiquidityInfo, - getXAmount_per_point_by_Lx, - getYAmount_per_point_by_Ly, - sort_tokens_by_base, - getBinPointByPrice, - getBinPointByPoint, -} from '../../services/commonV3'; -import { - PoolInfo, - batch_remove_liquidity_contract, -} from '../../services/swapV3'; -import { REF_POOL_NAV_TAB_KEY } from './PoolTabV3'; -import Big from 'big.js'; -import { - get_custom_config_for_chart, - get_default_config_for_chart, -} from '../../components/d3Chart/config'; -import { - IChartItemConfig, - IChartConfig, -} from '../../components/d3Chart/interfaces'; -import { - formatNumber, - formatWithCommas_usd, -} from '../../components/d3Chart/utils'; -import { - IAddLiquidityInfo, - IRemoveLiquidityInfo, - IBatchUpdateiquidityInfo, -} from '~pages/poolsV3/interfaces'; -import DclChart from '../../components/d3Chart/DclChart'; - -export type RemoveType = 'left' | 'right' | 'all'; -/** - * 中文 - * @param props - * @returns - */ -export const RemovePoolV3 = (props: any) => { - // return - return ; -}; -/** - * - * @param props 正向操作 - * @returns - */ -export const RemovePoolV3Forward = ({ props }: any) => { - const { - tokenMetadata_x_y, - poolDetail, - tokenPriceList, - isLegacy, - listLiquidities, - ...restProps - }: { - tokenMetadata_x_y: TokenMetadata[]; - poolDetail: PoolInfo; - tokenPriceList: any; - isLegacy?: boolean; - restProps: any; - listLiquidities: UserLiquidityInfo[]; - } = props; - const SLOT_NUMBER = get_slot_number_in_a_bin(); - const [slippageTolerance, setSlippageTolerance] = useState(0.5); - - // const tokens = sort_tokens_by_base(tokenMetadata_x_y);// todo - const tokens = tokenMetadata_x_y; - const { decimals: token_x_decimals } = tokens[0]; - const { decimals: token_y_decimals } = tokens[1]; - const [removeLoading, setRemoveLoading] = useState(false); - const [removeType, setRemoveType] = useState('all'); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - - // new - const [minPoint, setMinPoint] = useState(); - const [maxPoint, setMaxPoint] = useState(); - const [minPrice, setMinPrice] = useState(''); - const [maxPrice, setMaxPrice] = useState(''); - const [maxBinAmount, setMaxBinAmount] = useState(); - - const [minBoxPrice, setMinBoxPrice] = useState(''); - const [maxBoxPrice, setMaxBoxPrice] = useState(''); - const [minBoxPoint, setMinBoxPoint] = useState(); - const [maxBoxPoint, setMaxBoxPoint] = useState(); - const [binBoxAmount, setBinBoxAmount] = useState(''); - - useEffect(() => { - // init - if (tokens && poolDetail && listLiquidities) { - get_user_points_range(); - } - }, [tokens, poolDetail, listLiquidities]); - useEffect(() => { - if (minBoxPoint && maxBoxPoint) { - const bin_amount = get_bin_amount_by_points(minBoxPoint, maxBoxPoint); - setBinBoxAmount(bin_amount); - } - }, [minBoxPoint, maxBoxPoint]); - useEffect(() => { - if (binBoxAmount !== '') { - handleBinAmountToAppropriateAmount(+binBoxAmount); - } - }, [binBoxAmount]); - const [ - min_received_x_amount, - min_received_y_amount, - min_received_total_value, - ] = useMemo(() => { - if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { - const { total_token_x_amount, total_token_y_amount, total_value } = - get_minimum_received_data(); - return [ - formatNumber(total_token_x_amount), - formatNumber(total_token_y_amount), - formatWithCommas_usd(total_value), - ]; - } - return ['0', '0', '$0']; - }, [ - tokenPriceList, - tokenMetadata_x_y, - minBoxPoint, - maxBoxPoint, - slippageTolerance, - ]); - /** - * NOTE 删除一个点的场景暂时不考虑 - * @returns - */ - function get_will_deleted_nfts() { - let whole_deleted_nfts: UserLiquidityInfo[] = []; - let broken_deleted_nfts: UserLiquidityInfo[] = []; - if (removeType == 'all') { - whole_deleted_nfts = [].concat(listLiquidities); - } else if (removeType == 'left') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (left_point < maxBoxPoint) { - // 之前:left_point <= maxBoxPoint - if (right_point <= maxBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } else if (removeType == 'right') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (right_point > minBoxPoint) { - if (left_point >= minBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } - return { - whole_deleted_nfts, - broken_deleted_nfts, - }; - } - function get_slot_number_in_a_bin() { - const pool_id = poolDetail?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - function get_user_points_range() { - const user_points: number[] = []; - listLiquidities.forEach((l: UserLiquidityInfo) => { - user_points.push(l.left_point, l.right_point); - }); - user_points.sort((b, a) => b - a); - const min_point = get_bin_point_by_point(user_points[0], 'floor'); - const max_point = get_bin_point_by_point( - user_points[user_points.length - 1], - 'ceil' - ); - const min_price = get_bin_price_by_point(min_point); - const max_price = get_bin_price_by_point(max_point); - - const max_bin_amount = get_bin_amount_by_points(min_point, max_point); - setMinPoint(min_point); - setMaxPoint(max_point); - setMinPrice(min_price); - setMaxPrice(max_price); - setMaxBinAmount(max_bin_amount); - - setMinBoxPrice(min_price); - setMaxBoxPrice(max_price); - setMinBoxPoint(min_point); - setMaxBoxPoint(max_point); - setBinBoxAmount(max_bin_amount); - } - function get_bin_amount_by_points(left_point: number, right_point: number) { - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - const bin_amount = Big(right_point - left_point) - .div(binWidth) - .toFixed(); - return bin_amount; - } - function get_bin_price_by_point(point: number) { - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(point, decimalRate); - return price; - } - function get_bin_point_by_price(price: string) { - const point_delta = poolDetail.point_delta; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return point; - } - function get_bin_point_by_point( - point: number, - type: 'round' | 'floor' | 'ceil' - ) { - const point_delta = poolDetail.point_delta; - const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); - return bin_point; - } - function handleMinBoxPriceToAppropriatePoint() { - /** - * min price <= price < max box price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(minBoxPrice || 0); - if (big_price.lt(minPrice)) { - appropriate_price = minPrice; - appropriate_point = minPoint; - } else if (big_price.gt(maxBoxPrice)) { - appropriate_price = maxBoxPrice; - appropriate_point = maxBoxPoint; - } else { - appropriate_point = get_bin_point_by_price(minBoxPrice); - appropriate_price = get_bin_price_by_point(appropriate_point); - } - setMinBoxPrice(appropriate_price); - setMinBoxPoint(appropriate_point); - } - function handleMaxBoxPriceToAppropriatePoint() { - /** - * min box price <= price <= max price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(maxBoxPrice || 0); - if (big_price.lt(minBoxPrice)) { - appropriate_price = minBoxPrice; - appropriate_point = minBoxPoint; - } else if (big_price.gt(maxPrice)) { - appropriate_price = maxPrice; - appropriate_point = maxPoint; - } else { - appropriate_point = get_bin_point_by_price(maxBoxPrice); - appropriate_price = get_bin_price_by_point(appropriate_point); - } - setMaxBoxPrice(appropriate_price); - setMaxBoxPoint(appropriate_point); - } - /** - * 左右点位改变会触发bin amount随之更改 - * bin amount 修改会改变可以修改的点位 - * 0 <= bin amount < max bin amount - */ - function handleBinAmountToAppropriateAmount(point: number) { - const amount_int = point || +binBoxAmount; - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - let appropriate_amount = amount_int; - if (amount_int > +maxBinAmount) { - appropriate_amount = +maxBinAmount; - } - if (removeType == 'left') { - const right_box_point = minPoint + binWidth * appropriate_amount; - const right_box_price = get_bin_price_by_point(right_box_point); - setMaxBoxPoint(right_box_point); - setMaxBoxPrice(right_box_price); - } else if (removeType == 'right') { - const left_box_point = maxPoint - binWidth * appropriate_amount; - const left_box_price = get_bin_price_by_point(left_box_point); - setMinBoxPoint(left_box_point); - setMinBoxPrice(left_box_price); - } - setBinBoxAmount(appropriate_amount.toString()); - } - function getY( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const y = new BigNumber(L).multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - const y_result = y.toFixed(); - return toReadableNumber(token.decimals, toPrecision(y_result, 0)); - } - function getX( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const x = new BigNumber(L) - .multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ) - .toFixed(); - return toReadableNumber(token.decimals, toPrecision(x, 0)); - } - function get_X_Y_In_CurrentPoint( - tokenX: TokenMetadata, - tokenY: TokenMetadata, - L: string - ) { - const { liquidity, liquidity_x, current_point } = poolDetail; - const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); - let Ly = '0'; - let Lx = '0'; - // only remove y - if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { - Ly = L; - } else { - // have x and y - Ly = liquidity_y_big.toFixed(); - Lx = new BigNumber(L).minus(Ly).toFixed(); - } - const amountX = getXAmount_per_point_by_Lx(Lx, current_point); - const amountY = getYAmount_per_point_by_Ly(Ly, current_point); - const amountX_read = toReadableNumber( - tokenX.decimals, - toPrecision(amountX, 0) - ); - const amountY_read = toReadableNumber( - tokenY.decimals, - toPrecision(amountY, 0) - ); - return { amountx: amountX_read, amounty: amountY_read }; - } - function batch_remove_nfts() { - setRemoveLoading(true); - const [tokenX, tokenY] = tokenMetadata_x_y; - sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); - let batch_remove_liquidity: IRemoveLiquidityInfo[]; - let batch_update_liquidity: IBatchUpdateiquidityInfo; - let mint_liquidities: UserLiquidityInfo[] = []; - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - const { pool_id } = poolDetail; - /** - * step1 找到被截断的nft的 未截断的区间 - * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 - * step3 未截断的区间 和 token 数量作为 添加nft的参数 - */ - let min_withdraw_token_x_amount = Big(0); - let min_withdraw_token_y_amount = Big(0); - if (broken_deleted_nfts.length) { - const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - // const [whole_token_x_amount, whole_token_y_amount] = - // get_x_y_amount_of_liquidity({ left_point, right_point, amount }); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - // if (Big(min_token_x_amount).gt(new_token_x_amount)) { - // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - // Big(min_token_x_amount).minus(new_token_x_amount) - // ); - // } - // if (Big(min_token_y_amount).gt(new_token_y_amount)) { - // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - // Big(min_token_y_amount).minus(new_token_y_amount) - // ); - // } - - // const remove_part_token_x_amount = new Big( - // whole_token_x_amount || 0 - // ).minus(new_token_x_amount || 0); - // const remove_part_token_y_amount = new Big( - // whole_token_y_amount || 0 - // ).minus(whole_token_y_amount || 0); - // const rate = (100 - slippageTolerance) / 100; - // min_withdraw_token_x_amount = min_withdraw_token_x_amount.plus( - // remove_part_token_x_amount.mul(rate) - // ); - // min_withdraw_token_y_amount = min_withdraw_token_y_amount.plus( - // remove_part_token_y_amount.mul(rate) - // ); - - addLiquidityInfoList.push({ - pool_id, - left_point: new_left_point, - right_point: new_right_point, - amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), - amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), - min_amount_x: '0', - min_amount_y: '0', - }); - removeLiquidityInfos.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - - batch_update_liquidity = { - remove_liquidity_infos: removeLiquidityInfos, - add_liquidity_infos: addLiquidityInfoList, - }; - } - if (whole_deleted_nfts.length) { - const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - batchRemoveLiquidity.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - batch_remove_liquidity = batchRemoveLiquidity; - } - // const widthdraw_infos = { - // min_withdraw_token_x_amount: toNonDivisibleNumber( - // tokenX.decimals, - // min_withdraw_token_x_amount.toFixed() - // ), - // min_withdraw_token_y_amount: toNonDivisibleNumber( - // tokenY.decimals, - // min_withdraw_token_y_amount.toFixed() - // ), - // }; - batch_remove_liquidity_contract({ - token_x: tokenX, - token_y: tokenY, - batch_remove_liquidity, - batch_update_liquidity, - mint_liquidities, - // widthdraw_infos, - }); - } - function get_minimum_received_data() { - /** - * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 - * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 - * step3 把上述step1, step2 得到的x,y 累加起来即可 - */ - let total_token_x_amount = Big(0); - let total_token_y_amount = Big(0); - let minimum_total_value = Big(0); - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - if (whole_deleted_nfts.length) { - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - total_token_x_amount = total_token_x_amount.plus( - min_token_x_amount || 0 - ); - total_token_y_amount = total_token_y_amount.plus( - min_token_y_amount || 0 - ); - }); - } - if (broken_deleted_nfts.length) { - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - const broken_min_token_x_amount = - Big(min_token_x_amount).minus(new_token_x_amount); - const broken_min_token_y_amount = - Big(min_token_y_amount).minus(new_token_y_amount); - if (broken_min_token_x_amount.gt(0)) { - total_token_x_amount = total_token_x_amount.plus( - broken_min_token_x_amount || 0 - ); - } - if (broken_min_token_y_amount.gt(0)) { - total_token_y_amount = total_token_y_amount.plus( - broken_min_token_y_amount || 0 - ); - } - }); - } - if (tokenPriceList && tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const priceX = tokenPriceList[tokenX.id]?.price || 0; - const priceY = tokenPriceList[tokenY.id]?.price || 0; - const token_x_value = total_token_x_amount.mul(priceX); - const token_y_value = total_token_y_amount.mul(priceY); - minimum_total_value = token_x_value.plus(token_y_value); - } - const rate = (100 - slippageTolerance) / 100; - return { - total_token_x_amount: total_token_x_amount.toFixed(), - total_token_y_amount: total_token_y_amount.toFixed(), - minimum_total_value: minimum_total_value.toFixed(), - total_value: minimum_total_value.div(rate).toFixed(), - }; - } - function get_un_deleted_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - // intersection part - const intersection_l = Math.max(left_point, minBoxPoint); - const intersection_r = Math.min(right_point, maxBoxPoint); - // intersection part - let un_intersection_l; - let un_intersection_r; - if (removeType == 'left') { - un_intersection_l = intersection_r; - un_intersection_r = right_point; - } else if (removeType == 'right') { - un_intersection_l = left_point; - un_intersection_r = intersection_l; - } - return [un_intersection_l, un_intersection_r]; - } - function get_deleted_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - // intersection part - const intersection_l = Math.max(left_point, minBoxPoint); - const intersection_r = Math.min(right_point, maxBoxPoint); - return [intersection_l, intersection_r]; - } - function get_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const { left_point, right_point, amount: L } = liquidity; - const { current_point } = poolDetail; - let curTokenXAmount = '0'; - let curTokenYAmount = '0'; - // in range - if (current_point >= left_point && right_point > current_point) { - curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); - curTokenYAmount = getY(left_point, current_point, L, tokenY); - const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); - curTokenXAmount = Big(amountx || '0') - .plus(curTokenXAmount || '0') - .toFixed(); - curTokenYAmount = Big(amounty || '0') - .plus(curTokenYAmount || '0') - .toFixed(); - } - // only y token - if (current_point >= right_point) { - curTokenYAmount = getY(left_point, right_point, L, tokenY); - } - // only x token - if (left_point > current_point) { - curTokenXAmount = getX(left_point, right_point, L, tokenX); - } - return [curTokenXAmount, curTokenYAmount]; - } - function get_min_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const rate = (100 - slippageTolerance) / 100; - const [token_x_amount, token_y_amount] = - get_x_y_amount_of_liquidity(liquidity); - const min_token_x_amount = Big(token_x_amount || 0) - .mul(rate) - .toFixed(); - const min_token_y_amount = Big(token_y_amount || 0) - .mul(rate) - .toFixed(); - return [min_token_x_amount, min_token_y_amount]; - } - const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; - return ( - - - {/* Title */} -
- - - -
- -
-
- {/* Symbol pairs */} -
-
-
- - -
- - {tokens[0]?.symbol}/{tokens[1]?.symbol} - -
- - {min_received_total_value} - -
-
- {maxPoint && ( - - )} -
- {/* Removing way */} -
-
- -
-
-
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('left'); - setMinBoxPrice(minPrice); - setMinBoxPoint(minPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('right'); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(maxPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('all'); - setMinBoxPrice(minPrice); - setMinBoxPoint(minPoint); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(maxPoint); - }} - > - -
-
-
- {/* remove slider todo */} - {/* binBoxAmount 控制 */} - { - setBinBoxAmount(v.toString()); - }} - value={+binBoxAmount} - min={0} - max={+maxBinAmount} - step={1} - > - {/* Set points */} -
- {/* min price */} -
- - - - - { - const value = e.target.value; - setMinBoxPrice(value); - }} - inputMode="decimal" - onBlur={() => { - handleMinBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'right'} - > -
- {/* max price */} -
- - - - { - const value = e.target.value; - setMaxBoxPrice(value); - }} - value={maxBoxPrice} - inputMode="decimal" - onBlur={() => { - handleMaxBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'left'} - > -
- {/* bin amount */} -
- - - - - -
-
- {/* Slippage */} -
- -
- {/* Minimum received */} -
-
- - - - -
-
- - - - {min_received_x_amount} - -
-
- - - - {min_received_y_amount} - -
-
-
- {/* Button */} - {isSignedIn ? ( - - } - /> - - ) : ( -
- -
- )} -
-
- ); -}; - -/** - * - * @param props 逆向的操作思路是:展示层价格 是反的,数据层,点位是不变的。 - * @returns - */ -export const RemovePoolV3Reverse = ({ props }: any) => { - const { - tokenMetadata_x_y, - poolDetail, - tokenPriceList, - isLegacy, - listLiquidities, - ...restProps - }: { - tokenMetadata_x_y: TokenMetadata[]; - poolDetail: PoolInfo; - tokenPriceList: any; - isLegacy?: boolean; - restProps: any; - listLiquidities: UserLiquidityInfo[]; - } = props; - const SLOT_NUMBER = get_slot_number_in_a_bin(); - const [slippageTolerance, setSlippageTolerance] = useState(0.5); - - const tokens = tokenMetadata_x_y; - const { decimals: token_x_decimals } = tokens[0]; - const { decimals: token_y_decimals } = tokens[1]; - const [removeLoading, setRemoveLoading] = useState(false); - const [removeType, setRemoveType] = useState('all'); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - - // new - const [minPoint, setMinPoint] = useState(); - const [maxPoint, setMaxPoint] = useState(); - const [minPrice, setMinPrice] = useState(''); - const [maxPrice, setMaxPrice] = useState(''); - const [maxBinAmount, setMaxBinAmount] = useState(); - - const [minBoxPrice, setMinBoxPrice] = useState(''); - const [maxBoxPrice, setMaxBoxPrice] = useState(''); - const [minBoxPoint, setMinBoxPoint] = useState(); - const [maxBoxPoint, setMaxBoxPoint] = useState(); - const [binBoxAmount, setBinBoxAmount] = useState(''); - - useEffect(() => { - // =======》ok - // init - if (tokens && poolDetail && listLiquidities) { - get_user_points_range(); - } - }, [tokens, poolDetail, listLiquidities]); - useEffect(() => { - // =======》ok - if (minBoxPoint && maxBoxPoint) { - const bin_amount = get_bin_amount_by_points(maxBoxPoint, minBoxPoint); - setBinBoxAmount(bin_amount); - } - }, [minBoxPoint, maxBoxPoint]); - useEffect(() => { - if (binBoxAmount !== '') { - // =======》ok - handleBinAmountToAppropriateAmount(+binBoxAmount); - } - }, [binBoxAmount]); - const [ - min_received_x_amount, - min_received_y_amount, - min_received_total_value, - ] = useMemo(() => { - // ========> ok - if (tokenMetadata_x_y && minBoxPoint && maxBoxPoint) { - const { total_token_x_amount, total_token_y_amount, total_value } = - get_minimum_received_data(); - return [ - formatNumber(total_token_x_amount), - formatNumber(total_token_y_amount), - formatWithCommas_usd(total_value), - ]; - } - return ['0', '0', '$0']; - }, [ - tokenPriceList, - tokenMetadata_x_y, - minBoxPoint, - maxBoxPoint, - slippageTolerance, - ]); - /** - * NOTE 删除一个点的场景暂时不考虑 - * @returns - */ - function get_will_deleted_nfts() { - let whole_deleted_nfts: UserLiquidityInfo[] = []; - let broken_deleted_nfts: UserLiquidityInfo[] = []; - if (removeType == 'all') { - whole_deleted_nfts = [].concat(listLiquidities); - } else if (removeType == 'left') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (right_point > maxBoxPoint) { - if (left_point >= maxBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } else if (removeType == 'right') { - listLiquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - if (left_point < minBoxPoint) { - if (right_point <= minBoxPoint) { - whole_deleted_nfts.push(l); - } else { - broken_deleted_nfts.push(l); - } - } - }); - } - return { - whole_deleted_nfts, - broken_deleted_nfts, - }; - } - function get_slot_number_in_a_bin() { - const pool_id = poolDetail?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - function get_user_points_range() { - const user_points: number[] = []; - listLiquidities.forEach((l: UserLiquidityInfo) => { - user_points.push(l.left_point, l.right_point); - }); - user_points.sort((b, a) => b - a); - const min_point = get_bin_point_by_point(user_points[0], 'floor'); - const max_point = get_bin_point_by_point( - user_points[user_points.length - 1], - 'ceil' - ); - const min_price = reverse_price(get_bin_price_by_point(max_point)); - const max_price = reverse_price(get_bin_price_by_point(min_point)); - const max_bin_amount = get_bin_amount_by_points(min_point, max_point); - setMinPoint(min_point); - setMaxPoint(max_point); - setMinPrice(min_price); - setMaxPrice(max_price); - setMaxBinAmount(max_bin_amount); - - setMinBoxPrice(min_price); - setMaxBoxPrice(max_price); - setMinBoxPoint(max_point); - setMaxBoxPoint(min_point); - setBinBoxAmount(max_bin_amount); - } - function reverse_price(price: string) { - if (Big(price).eq(0)) return '0'; - return Big(1).div(price).toFixed(); - } - function get_bin_amount_by_points(left_point: number, right_point: number) { - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - const bin_amount = Big(right_point - left_point) - .div(binWidth) - .toFixed(); - return bin_amount; - } - function get_bin_price_by_point(point: number) { - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - const price = getPriceByPoint(point, decimalRate); - return price; - } - function get_bin_point_by_price(price: string) { - const point_delta = poolDetail.point_delta; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return point; - } - function get_bin_point_by_point( - point: number, - type: 'round' | 'floor' | 'ceil' - ) { - const point_delta = poolDetail.point_delta; - const bin_point = getBinPointByPoint(point_delta, SLOT_NUMBER, point, type); - return bin_point; - } - function handleMinBoxPriceToAppropriatePoint() { - /** - * min price <= price < max box price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(minBoxPrice || 0); - if (big_price.lt(minPrice)) { - appropriate_price = minPrice; - appropriate_point = maxPoint; - } else if (big_price.gt(maxBoxPrice)) { - appropriate_price = maxBoxPrice; - appropriate_point = maxBoxPoint; - } else { - appropriate_point = get_bin_point_by_price(reverse_price(minBoxPrice)); - appropriate_price = reverse_price( - get_bin_price_by_point(appropriate_point) - ); - } - setMinBoxPrice(appropriate_price); - setMinBoxPoint(appropriate_point); - } - function handleMaxBoxPriceToAppropriatePoint() { - /** - * min box price <= price <= max price - */ - let appropriate_price; - let appropriate_point; - const big_price = Big(maxBoxPrice || 0); - if (big_price.lt(minBoxPrice)) { - appropriate_price = minBoxPrice; - appropriate_point = minBoxPoint; - } else if (big_price.gt(maxPrice)) { - appropriate_price = maxPrice; - appropriate_point = minPoint; - } else { - appropriate_point = get_bin_point_by_price(reverse_price(maxBoxPrice)); - appropriate_price = reverse_price( - get_bin_price_by_point(appropriate_point) - ); - } - setMaxBoxPrice(appropriate_price); - setMaxBoxPoint(appropriate_point); - } - /** - * 左右点位改变会触发bin amount随之更改 - * bin amount 修改会改变可以修改的点位 - * 0 <= bin amount < max bin amount - */ - function handleBinAmountToAppropriateAmount(binAmount: number) { - const amount_int = binAmount || +binBoxAmount; - const { point_delta } = poolDetail; - const binWidth = SLOT_NUMBER * point_delta; - let appropriate_amount = amount_int; - if (amount_int > +maxBinAmount) { - appropriate_amount = +maxBinAmount; - } - if (removeType == 'left') { - const right_box_point = maxPoint - binWidth * appropriate_amount; - const right_box_price = reverse_price( - get_bin_price_by_point(right_box_point) - ); - setMaxBoxPoint(right_box_point); - setMaxBoxPrice(right_box_price); - } else if (removeType == 'right') { - const left_box_point = minPoint + binWidth * appropriate_amount; - const left_box_price = reverse_price( - get_bin_price_by_point(left_box_point) - ); - setMinBoxPoint(left_box_point); - setMinBoxPrice(left_box_price); - } - setBinBoxAmount(appropriate_amount.toString()); - } - function getY( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const y = new BigNumber(L).multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - const y_result = y.toFixed(); - return toReadableNumber(token.decimals, toPrecision(y_result, 0)); - } - function getX( - leftPoint: number, - rightPoint: number, - L: string, - token: TokenMetadata - ) { - const x = new BigNumber(L) - .multipliedBy( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ) - .toFixed(); - return toReadableNumber(token.decimals, toPrecision(x, 0)); - } - function get_X_Y_In_CurrentPoint( - tokenX: TokenMetadata, - tokenY: TokenMetadata, - L: string - ) { - const { liquidity, liquidity_x, current_point } = poolDetail; - const liquidity_y_big = new BigNumber(liquidity).minus(liquidity_x); - let Ly = '0'; - let Lx = '0'; - // only remove y - if (liquidity_y_big.isGreaterThanOrEqualTo(L)) { - Ly = L; - } else { - // have x and y - Ly = liquidity_y_big.toFixed(); - Lx = new BigNumber(L).minus(Ly).toFixed(); - } - const amountX = getXAmount_per_point_by_Lx(Lx, current_point); - const amountY = getYAmount_per_point_by_Ly(Ly, current_point); - const amountX_read = toReadableNumber( - tokenX.decimals, - toPrecision(amountX, 0) - ); - const amountY_read = toReadableNumber( - tokenY.decimals, - toPrecision(amountY, 0) - ); - return { amountx: amountX_read, amounty: amountY_read }; - } - function batch_remove_nfts() { - setRemoveLoading(true); - const [tokenX, tokenY] = tokenMetadata_x_y; - sessionStorage.setItem(REF_POOL_NAV_TAB_KEY, '/yourliquidity'); - let batch_remove_liquidity: IRemoveLiquidityInfo[]; - let batch_update_liquidity: IBatchUpdateiquidityInfo; - let mint_liquidities: UserLiquidityInfo[] = []; - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - const { pool_id } = poolDetail; - /** - * step1 找到被截断的nft的 未截断的区间 - * step2 找到区间,也知道高度==>推导出这个区间的 tokenx的数量和tokeny的数量 - * step3 未截断的区间 和 token 数量作为 添加nft的参数 - */ - if (broken_deleted_nfts.length) { - const removeLiquidityInfos: IRemoveLiquidityInfo[] = []; - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - addLiquidityInfoList.push({ - pool_id, - left_point: new_left_point, - right_point: new_right_point, - amount_x: toNonDivisibleNumber(tokenX.decimals, new_token_x_amount), - amount_y: toNonDivisibleNumber(tokenY.decimals, new_token_y_amount), - min_amount_x: '0', - min_amount_y: '0', - }); - removeLiquidityInfos.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - - batch_update_liquidity = { - remove_liquidity_infos: removeLiquidityInfos, - add_liquidity_infos: addLiquidityInfoList, - }; - } - if (whole_deleted_nfts.length) { - const batchRemoveLiquidity: IRemoveLiquidityInfo[] = []; - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, lpt_id, left_point, right_point, mft_id } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - batchRemoveLiquidity.push({ - lpt_id, - amount, - min_amount_x: toNonDivisibleNumber( - tokenX.decimals, - min_token_x_amount - ), - min_amount_y: toNonDivisibleNumber( - tokenY.decimals, - min_token_y_amount - ), - }); - if (mft_id) { - mint_liquidities.push(l); - } - }); - batch_remove_liquidity = batchRemoveLiquidity; - } - batch_remove_liquidity_contract({ - token_x: tokenX, - token_y: tokenY, - batch_remove_liquidity, - batch_update_liquidity, - mint_liquidities, - // widthdraw_infos, - }); - } - function get_minimum_received_data() { - /** - * step1 完整删除的nfts,求出每个nft 对应的最小 x,y 的数量 - * step2 截段的nfts,求出每个nft被删除那一段流动性 对应的最小 x,y的数量 - * step3 把上述step1, step2 得到的x,y 累加起来即可 - */ - let total_token_x_amount = Big(0); - let total_token_y_amount = Big(0); - let minimum_total_value = Big(0); - const { whole_deleted_nfts, broken_deleted_nfts } = get_will_deleted_nfts(); - if (whole_deleted_nfts.length) { - whole_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - total_token_x_amount = total_token_x_amount.plus( - min_token_x_amount || 0 - ); - total_token_y_amount = total_token_y_amount.plus( - min_token_y_amount || 0 - ); - }); - } - if (broken_deleted_nfts.length) { - broken_deleted_nfts.forEach((l: UserLiquidityInfo) => { - const { amount, left_point, right_point } = l; - const [new_left_point, new_right_point] = get_un_deleted_range(l); - const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ - left_point: new_left_point, - right_point: new_right_point, - amount, - }); - const [min_token_x_amount, min_token_y_amount] = - get_min_x_y_amount_of_liquidity({ - left_point: left_point, - right_point: right_point, - amount, - }); - const broken_min_token_x_amount = - Big(min_token_x_amount).minus(new_token_x_amount); - const broken_min_token_y_amount = - Big(min_token_y_amount).minus(new_token_y_amount); - if (broken_min_token_x_amount.gt(0)) { - total_token_x_amount = total_token_x_amount.plus( - broken_min_token_x_amount || 0 - ); - } - if (broken_min_token_y_amount.gt(0)) { - total_token_y_amount = total_token_y_amount.plus( - broken_min_token_y_amount || 0 - ); - } - }); - } - if (tokenPriceList && tokenMetadata_x_y) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const priceX = tokenPriceList[tokenX.id]?.price || 0; - const priceY = tokenPriceList[tokenY.id]?.price || 0; - const token_x_value = total_token_x_amount.mul(priceX); - const token_y_value = total_token_y_amount.mul(priceY); - minimum_total_value = token_x_value.plus(token_y_value); - } - const rate = (100 - slippageTolerance) / 100; - return { - total_token_x_amount: total_token_x_amount.toFixed(), - total_token_y_amount: total_token_y_amount.toFixed(), - minimum_total_value: minimum_total_value.toFixed(), - total_value: minimum_total_value.div(rate).toFixed(), - }; - } - function get_un_deleted_range(liquidity: UserLiquidityInfo) { - const { left_point, right_point } = liquidity; - // intersection part - const intersection_l = Math.min(right_point, minBoxPoint); - const intersection_r = Math.max(left_point, maxBoxPoint); - // intersection part - let un_intersection_l; - let un_intersection_r; - if (removeType == 'left') { - un_intersection_l = intersection_r; - un_intersection_r = left_point; - } else if (removeType == 'right') { - un_intersection_l = right_point; - un_intersection_r = intersection_l; - } - return [un_intersection_r, un_intersection_l]; - } - function get_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const [tokenX, tokenY] = tokenMetadata_x_y; - const { left_point, right_point, amount: L } = liquidity; - const { current_point } = poolDetail; - let curTokenXAmount = '0'; - let curTokenYAmount = '0'; - // in range - if (current_point >= left_point && right_point > current_point) { - curTokenXAmount = getX(current_point + 1, right_point, L, tokenX); - curTokenYAmount = getY(left_point, current_point, L, tokenY); - const { amountx, amounty } = get_X_Y_In_CurrentPoint(tokenX, tokenY, L); - curTokenXAmount = Big(amountx || '0') - .plus(curTokenXAmount || '0') - .toFixed(); - curTokenYAmount = Big(amounty || '0') - .plus(curTokenYAmount || '0') - .toFixed(); - } - // only y token - if (current_point >= right_point) { - curTokenYAmount = getY(left_point, right_point, L, tokenY); - } - // only x token - if (left_point > current_point) { - curTokenXAmount = getX(left_point, right_point, L, tokenX); - } - return [curTokenXAmount, curTokenYAmount]; - } - function get_min_x_y_amount_of_liquidity(liquidity: { - left_point: number; - right_point: number; - amount: string; - }) { - const rate = (100 - slippageTolerance) / 100; - const [token_x_amount, token_y_amount] = - get_x_y_amount_of_liquidity(liquidity); - const min_token_x_amount = Big(token_x_amount || 0) - .mul(rate) - .toFixed(); - const min_token_y_amount = Big(token_y_amount || 0) - .mul(rate) - .toFixed(); - return [min_token_x_amount, min_token_y_amount]; - } - const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; - return ( - - - {/* Title */} -
- - - -
- -
-
- {/* Symbol pairs */} -
-
-
- - -
- - {tokens[1]?.symbol}/{tokens[0]?.symbol} - -
- - {min_received_total_value} - -
-
- {maxPoint && ( - - )} -
- {/* Removing way */} -
-
- -
-
-
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('left'); - setMinBoxPrice(minPrice); - setMinBoxPoint(maxPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('right'); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(minPoint); - }} - > - -
- -
{ - e.preventDefault(); - e.stopPropagation(); - setRemoveType('all'); - setMinBoxPrice(minPrice); - setMinBoxPoint(maxPoint); - setMaxBoxPrice(maxPrice); - setMaxBoxPoint(minPoint); - }} - > - -
-
-
- {/* remove slider */} - {/* binBoxAmount 控制 */} - { - setBinBoxAmount(v.toString()); - }} - value={+binBoxAmount} - min={0} - max={+maxBinAmount} - step={1} - > - {/* Set points */} -
- {/* min price */} -
- - - - - { - const value = e.target.value; - setMinBoxPrice(value); - }} - inputMode="decimal" - onBlur={() => { - handleMinBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'right'} - > -
- {/* max price */} -
- - - - { - const value = e.target.value; - setMaxBoxPrice(value); - }} - value={maxBoxPrice} - inputMode="decimal" - onBlur={() => { - handleMaxBoxPriceToAppropriatePoint(); - }} - disabled={removeType !== 'left'} - > -
- {/* bin amount */} -
- - - - - -
-
- {/* Slippage */} -
- -
- {/* Minimum received */} -
-
- - - - -
-
- - - - {min_received_y_amount} - -
-
- - - - {min_received_x_amount} - -
-
-
- {/* Button */} - {isSignedIn ? ( - - } - /> - - ) : ( -
- -
- )} -
-
- ); -}; -export function IntegerInputComponent({ - value, - setValue, - disabled, - className, - max, - onBlur, -}: any) { - const removeLeadingZeros = (s: string) => { - const oldLen = s.length; - s = s.replace(/^0+/, ''); - - if (s.length === 0 && oldLen > 0) { - s = '0'; - } - - if (max && Number(s) > max) { - return max; - } - - return s; - }; - - const handleChange = (val: string) => { - val = val.replace(/[^\d]/g, ''); - val = removeLeadingZeros(val); - setValue(val); - }; - - return ( -
- { - if (onBlur) { - onBlur(); - } else if (!target.value) { - setValue(1); - } - }} - onChange={({ target }) => { - handleChange(target.value); - }} - /> -
- ); -} diff --git a/src/components/portfolio/Farms.tsx b/src/components/portfolio/Farms.tsx index 8364c208e..732dd6449 100644 --- a/src/components/portfolio/Farms.tsx +++ b/src/components/portfolio/Farms.tsx @@ -708,7 +708,8 @@ function DclFarmRowMobile() { switch_off ? 'hidden' : '' }`} > -
+ {/* border-b border-limitOrderFeeTiersBorderColor */} +
@@ -763,23 +764,6 @@ function DclFarmRowMobile() {
-
- {listLiquidities_inFarimg.length > 0 ? ( - <> - {listLiquidities_inFarimg.map((liquidity: UserLiquidityInfo) => { - return ( - - ); - })} - - ) : null} -
); @@ -845,7 +829,8 @@ function DclFarmRowPc() {
-
+ {/* border-b border-gray1 */} +
@@ -897,26 +882,6 @@ function DclFarmRowPc() {
-
-

- -

- {listLiquidities_inFarimg.length > 0 ? ( - <> - {listLiquidities_inFarimg.map((liquidity: UserLiquidityInfo) => { - return ( - - ); - })} - - ) : null} -
); diff --git a/src/pages/poolsV3/components/detail/UserTabBox.tsx b/src/pages/poolsV3/components/detail/UserTabBox.tsx index d0a6eed54..bbb88561d 100644 --- a/src/pages/poolsV3/components/detail/UserTabBox.tsx +++ b/src/pages/poolsV3/components/detail/UserTabBox.tsx @@ -123,7 +123,7 @@ function UserTabBox(props: { show ? '' : 'hidden' }`} > -
+
{ diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 26a4f6256..b94d61c8a 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -419,11 +419,11 @@ export function YourLiquidityBox(props: { return (
-
- +
+ - + {getTotalLiquditiesTvl()}
diff --git a/src/services/indexer.ts b/src/services/indexer.ts index fd967d88e..d4a63fe5b 100644 --- a/src/services/indexer.ts +++ b/src/services/indexer.ts @@ -488,7 +488,7 @@ export const getDCLTopBinFee = async (props: { }): Promise => { const { pool_id, bin, start_point, end_point } = props; const result = await getDclPoolPoints(pool_id, bin, start_point, end_point); - return result?.top_bin_fee_data || {}; + return result?.top_bin_fee_data; }; export const getDCLAccountFee = async (props: { From 9c9ea7f5ae6bddf85e04cb195a02fcf173799d65 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 20 Sep 2023 19:46:13 +0800 Subject: [PATCH 126/204] fix bug --- src/components/farm/FarmsDclDetail.tsx | 17 ++++++++++------- src/components/swap/SwapLimitOrderChart.tsx | 2 +- src/components/swap/SwapProTab.tsx | 6 +++--- src/components/swap/SwapRateChart.tsx | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 9 ++++++--- .../poolsV3/components/detail/TablePool.tsx | 13 +++++++++++-- 6 files changed, 32 insertions(+), 17 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index b368ab76c..5d2bbd191 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -415,6 +415,8 @@ export default function FarmsDclDetail(props: { set_listLiquidities_unFarimg(temp_free_final); set_listLiquidities_unavailable(temp_unavailable_final); setListLiquidities(matched_liquidities); + } + if (!user_data_loading) { setListLiquiditiesLoading(false); } } @@ -1688,12 +1690,13 @@ function AddLiquidityEntryBar(props: { const { loading, inFarimg, unFarimg, unavailable, detailData, isEnded } = props; if (!loading && inFarimg.length == 0 && unFarimg.length == 0) { - if (unavailable.length == 0) { - tip = ; - } else { - tip = - 'The price range of your liquidity is out of reward range. Please add liquidity within reward range.'; - } + // if (unavailable.length == 0) { + // tip = ; + // } else { + // tip = + // 'The price range of your liquidity is out of reward range. Please add liquidity within reward range.'; + // } + tip = "You don't have liquidity during the farm reward range, click 'Add Liquidity' to start farming."; } if (loading || !tip || isEnded) return null; return ( @@ -1712,7 +1715,7 @@ function AddLiquidityEntryBar(props: { }} color="#fff" minWidth="9rem" - className={`flex-shrink-0 px-1 h-8 ml-5 text-center text-sm text-white focus:outline-none font-semibold `} + className={`flex-shrink-0 px-1 h-8 lg:ml-5 text-center text-sm text-white focus:outline-none font-semibold xsm:w-full `} > diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index c2c69ae09..d4f686370 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -388,7 +388,7 @@ export default function SwapLimitOrderChart() { {cur_pairs_price_mode}
- +
{/* chart area */} diff --git a/src/components/swap/SwapProTab.tsx b/src/components/swap/SwapProTab.tsx index 5525e313b..5380fc6d8 100644 --- a/src/components/swap/SwapProTab.tsx +++ b/src/components/swap/SwapProTab.tsx @@ -1,8 +1,8 @@ import React, { useContext, useEffect, useState } from 'react'; -import { SwapProContext } from '../../pages/SwapPage'; +import { SwapProContext, SWAP_MODE } from '../../pages/SwapPage'; export default function ProTab() { - const { proTab, setProTab, dcl_pool_id } = useContext(SwapProContext); - if (!dcl_pool_id) return null; + const { proTab, setProTab, dcl_pool_id, swapMode } = useContext(SwapProContext); + if (!dcl_pool_id || swapMode == SWAP_MODE.NORMAL) return null; return (
- +
diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 9ee9a5616..e2e507645 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -348,6 +348,7 @@ export default function PoolDetailV3() {
) ) : null} - - + > : null + } +
{/* title for mobile */}
diff --git a/src/pages/poolsV3/components/detail/TablePool.tsx b/src/pages/poolsV3/components/detail/TablePool.tsx index 81397d025..48ea8ad57 100644 --- a/src/pages/poolsV3/components/detail/TablePool.tsx +++ b/src/pages/poolsV3/components/detail/TablePool.tsx @@ -24,9 +24,11 @@ import { reverse_price, } from '~services/commonV3'; import { pointToPrice } from '~services/swapV3'; +import { RelatedFarmsBox } from './RelatedFarmsBox'; +import { isClientMobie } from '../../../../utils/device'; export function TablePool(props: any) { - const { poolDetail, tokenPriceList } = props; + const { poolDetail, tokenPriceList, sole_seed } = props; const [tokens, setTokens] = useState([]); const intl = useIntl(); useEffect(() => { @@ -88,6 +90,7 @@ export function TablePool(props: any) { return '$' + toInternationalCurrencySystem(tvl.toString(), 2); } } + const isMobile = isClientMobie(); return (
@@ -178,7 +181,13 @@ export function TablePool(props: any) {
))}
- + { + isMobile ? : null + } t.meta)} pool_id={poolDetail.pool_id} From c22829787262550296d83bac4570dff4e628d1f3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 20 Sep 2023 22:11:49 +0800 Subject: [PATCH 127/204] fix bugs --- src/components/farm/FarmsDclDetail.tsx | 3 +- src/components/pool/RemovePoolV3.tsx | 6 +-- src/components/swap/SwapProTab.tsx | 3 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 39 ++++++++----------- src/pages/poolsV3/PoolDetailV3.tsx | 15 ++++--- .../components/add/NoDataComponent.tsx | 8 ++-- .../components/add/PointsComponent.tsx | 4 +- .../poolsV3/components/detail/TablePool.tsx | 14 +++---- .../components/detail/UnclaimedFeesBox.tsx | 2 +- 9 files changed, 40 insertions(+), 54 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 5d2bbd191..446bae8fa 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1696,7 +1696,8 @@ function AddLiquidityEntryBar(props: { // tip = // 'The price range of your liquidity is out of reward range. Please add liquidity within reward range.'; // } - tip = "You don't have liquidity during the farm reward range, click 'Add Liquidity' to start farming."; + tip = + "You don't have liquidity during the farm reward range, click 'Add Liquidity' to start farming."; } if (loading || !tip || isEnded) return null; return ( diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 78960460a..68b74adb7 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -140,10 +140,6 @@ export const RemovePoolV3 = (props: any) => { maxBoxPoint, slippageTolerance, ]); - /** - * NOTE 删除一个点的场景暂时不考虑 - * @returns - */ function get_will_deleted_nfts() { let whole_deleted_nfts: UserLiquidityInfo[] = []; let broken_deleted_nfts: UserLiquidityInfo[] = []; @@ -461,7 +457,7 @@ export const RemovePoolV3 = (props: any) => { const { amount, lpt_id, left_point, right_point, mft_id } = l; const [new_left_point, new_right_point] = get_un_deleted_range(l); const [new_token_x_amount, new_token_y_amount] = - get_x_y_amount_of_liquidity({ + get_min_x_y_amount_of_liquidity({ left_point: new_left_point, right_point: new_right_point, amount, diff --git a/src/components/swap/SwapProTab.tsx b/src/components/swap/SwapProTab.tsx index 5380fc6d8..8439389d1 100644 --- a/src/components/swap/SwapProTab.tsx +++ b/src/components/swap/SwapProTab.tsx @@ -1,7 +1,8 @@ import React, { useContext, useEffect, useState } from 'react'; import { SwapProContext, SWAP_MODE } from '../../pages/SwapPage'; export default function ProTab() { - const { proTab, setProTab, dcl_pool_id, swapMode } = useContext(SwapProContext); + const { proTab, setProTab, dcl_pool_id, swapMode } = + useContext(SwapProContext); if (!dcl_pool_id || swapMode == SWAP_MODE.NORMAL) return null; return (
diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 7558a0599..4959e7457 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1779,7 +1779,7 @@ export default function AddYourLiquidityPageV3() { > {/* head */}
{ history.goBack(); }} @@ -1796,29 +1796,22 @@ export default function AddYourLiquidityPageV3() {
{/* content */} -
+
- {/* no Data */} - {currentSelectedPool ? null : } - - {/* empty pool */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - - {/* add Liquidity part */} - - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} +
+ {/* no Data */} + {(currentSelectedPool && + !currentSelectedPool.pool_id && + !OPEN_CREATE_POOL_ENTRY) || + !currentSelectedPool ? ( + + ) : null} + + {/* left area */} + {currentSelectedPool && currentSelectedPool.pool_id ? ( + + ) : null} +
{/* right area */}
) ) : null} - { - !isMobile ? : null - } - + {!isMobile ? ( + + ) : null}
{/* title for mobile */}
diff --git a/src/pages/poolsV3/components/add/NoDataComponent.tsx b/src/pages/poolsV3/components/add/NoDataComponent.tsx index d168b334e..1aac3191b 100644 --- a/src/pages/poolsV3/components/add/NoDataComponent.tsx +++ b/src/pages/poolsV3/components/add/NoDataComponent.tsx @@ -10,12 +10,10 @@ export function NoDataComponent() { const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; return ( -
+
{/* chart area */} -
-
+
+
{ setChartTab('liquidity'); diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx index a64ae3305..1ed85b59d 100644 --- a/src/pages/poolsV3/components/add/PointsComponent.tsx +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -320,9 +320,7 @@ export function PointsComponent() { } const is_mobile = isMobile(); return ( -
+
{/* chart area */}
))}
- { - isMobile ? : null - } + {isMobile ? ( + + ) : null} t.meta)} pool_id={poolDetail.pool_id} diff --git a/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx b/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx index 82e5a0b15..a47346cfa 100644 --- a/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx +++ b/src/pages/poolsV3/components/detail/UnclaimedFeesBox.tsx @@ -148,7 +148,7 @@ export function UnclaimedFeesBox(props: any) {
- + {token_y_metadata.symbol} From a461b08e2492b9bde2838a8fe86ee45988acc598 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 20 Sep 2023 22:58:17 +0800 Subject: [PATCH 128/204] fix bugs --- src/pages/SwapPage.tsx | 11 +++++++---- 1 file changed, 7 insertions(+), 4 deletions(-) diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index b792dee08..5d9f478e0 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -348,12 +348,15 @@ function SwapPage() { maxWidth: '850px', }} > - {((dcl_pool_id && proTab == 'PRICE') || !dcl_pool_id) && ( + {(swapMode === SWAP_MODE.NORMAL || + (SWAP_MODE.LIMIT && dcl_pool_id && proTab == 'PRICE')) && ( )} - {dcl_pool_id && proTab == 'ORDER' && ( - - )} + {dcl_pool_id && + proTab == 'ORDER' && + swapMode === SWAP_MODE.LIMIT && ( + + )} {swapMode === SWAP_MODE.NORMAL ? ( <>
Date: Thu, 21 Sep 2023 16:12:26 +0800 Subject: [PATCH 129/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 34 +- src/components/d3Chart/DclChartCopy.tsx | 3321 --------- src/components/pool/RemovePoolV3.tsx | 3 +- src/components/pool/YourLiquidityV2.tsx | 25 +- src/components/portfolio/Orders.tsx | 3 +- .../poolsV3/AddYourLiquidityPageV3Copy.tsx | 6159 ----------------- .../components/add/AddLiquidityButton.tsx | 8 - .../components/detail/YourLiquidityBox.tsx | 12 +- src/services/commonV3.ts | 7 +- src/services/swapV3.ts | 6 +- 10 files changed, 69 insertions(+), 9509 deletions(-) delete mode 100644 src/components/d3Chart/DclChartCopy.tsx delete mode 100644 src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index ebd2a4ddd..eb54f8ac3 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -7,6 +7,7 @@ import { PoolInfo, list_liquidities, get_pool_marketdepth, + get_liquidity, } from '../../services/swapV3'; import { getPriceByPoint, @@ -52,6 +53,7 @@ import { getBoostTokenPrices } from '../../services/farm'; import { toReadableNumber, formatWithCommas } from '~utils/numbers'; import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; import { BlueCircleLoading } from '../../components/layout/Loading'; +import { get_unClaimed_fee_data } from '../../pages/poolsV3/components/detail/DetailFun'; export default function DclChart({ pool_id, leftPoint, @@ -336,19 +338,34 @@ export default function DclChart({ let total_value = Big(0); let total_fee_earned = Big(0); let apr_24 = ''; + let total_earned_fee_x = '0'; + let total_earned_fee_y = '0'; if (dcl_fee_result) { + // total unClaimed fee + const [ + unClaimed_tvl_fee, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + ] = get_unClaimed_fee_data(user_liquidities, pool, tokenPriceList); const dclAccountFee: IDCLAccountFee = dcl_fee_result; const { total_earned_fee } = dclAccountFee; // total earned fee const { total_fee_x, total_fee_y } = total_earned_fee || {}; - const total_earned_fee_x = toReadableNumber( + total_earned_fee_x = toReadableNumber( token_x_metadata.decimals, Big(total_fee_x || 0).toFixed() ); - const total_earned_fee_y = toReadableNumber( + total_earned_fee_x = Big(total_earned_fee_x) + .plus(unClaimed_amount_x_fee) + .toFixed(); + + total_earned_fee_y = toReadableNumber( token_y_metadata.decimals, Big(total_fee_y || 0).toFixed() ); + total_earned_fee_y = Big(total_earned_fee_y) + .plus(unClaimed_amount_y_fee) + .toFixed(); const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); total_fee_earned = total_earned_fee_x_value.plus( @@ -356,7 +373,12 @@ export default function DclChart({ ); // 24h profit apr_24 = formatPercentage( - get_account_24_apr(dcl_fee_result, pool, tokenPriceList) + get_account_24_apr( + unClaimed_tvl_fee, + dcl_fee_result, + pool, + tokenPriceList + ) ); } user_liquidities.forEach((l: UserLiquidityInfo) => { @@ -452,7 +474,11 @@ export default function DclChart({ const nfts = liquidities.filter((l: UserLiquidityInfo) => { return l.pool_id == pool_id; }); - set_user_liquidities(nfts); + const liquiditiesPromise = nfts.map((liquidity: UserLiquidityInfo) => { + return get_liquidity(liquidity.lpt_id); + }); + const nft_details = await Promise.all(liquiditiesPromise); + set_user_liquidities(nft_details); list = divide_liquidities_into_bins_user({ liquidities: nfts, slot_number_in_a_bin: bin_final, diff --git a/src/components/d3Chart/DclChartCopy.tsx b/src/components/d3Chart/DclChartCopy.tsx deleted file mode 100644 index d73e7fca3..000000000 --- a/src/components/d3Chart/DclChartCopy.tsx +++ /dev/null @@ -1,3321 +0,0 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { FormattedMessage } from 'react-intl'; -import { isMobile } from '../../utils/device'; -import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; -import { - get_pool, - PoolInfo, - list_liquidities, - get_pool_marketdepth, -} from '../../services/swapV3'; -import { - getPriceByPoint, - getPointByPrice, - POINTLEFTRANGE, - POINTRIGHTRANGE, - divide_liquidities_into_bins_user, - UserLiquidityInfo, - getBinPointByPoint, - get_x_y_amount_by_condition, - get_account_24_apr, - divide_liquidities_into_bins_pool, - get_token_amount_in_user_liquidities, - reverse_price, -} from '../../services/commonV3'; -import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; -import { sortBy, debounce } from 'lodash'; -import { - IChartData, - IChartItemConfig, - IChartConfig, - IBinDetail, - IPoolChartConfig, - IUserLiquiditiesDetail, - IDCLAccountFee, - IRMTYPE, - IDclChartProps, -} from './interfaces'; -import { - formatPrice, - formatNumber, - formatPercentage, - formatWithCommas_usd, - formatWithCommas_number, - formatPriceWithCommas, -} from './utils'; -import { - get_custom_config_for_chart, - get_default_config_for_chart, -} from './config'; -import Big from 'big.js'; -import * as d3 from 'd3'; -import { useWalletSelector } from '../../context/WalletSelectorContext'; -import { getBoostTokenPrices } from '../../services/farm'; -import { toNonDivisibleNumber, toReadableNumber } from '~utils/numbers'; -import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; -export default function DclChart(props: IDclChartProps) { - if (props.reverse) { - return ; - } else { - return ; - } -} - -function DclChartForward({ - pool_id, - leftPoint, - rightPoint, - setLeftPoint, - setRightPoint, - config, - chartType, - removeParams, - newlyAddedLiquidities, -}: IDclChartProps) { - const [pool, setPool] = useState(); - const [price_range, set_price_range] = useState(); - const [chartDataList, setChartDataList] = useState(); - const [binDetail, setBinDetail] = useState(); - const [dragLeftPoint, setDragLeftPoint] = useState(); - const [dragRightPoint, setDragRightPoint] = useState(); - const [zoom, setZoom] = useState(); - const [randomId, setRandomId] = useState('.' + createRandomString()); - const [drawChartDone, setDrawChartDone] = useState(false); - const [user_liquidities, set_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - const [user_liquidities_detail, set_user_liquidities_detail] = - useState(); - const [tokenPriceList, setTokenPriceList] = useState>(); - /** constant start */ - const appearanceConfig: IPoolChartConfig = config || {}; - let [timerObj, setTimerObj] = useState({ - timer: '', - }); - const dragBarWidth = 28; - const percentBoxWidth = 44; - const min_bar_height = 2; - const svgWidth = +(appearanceConfig.svgWidth || 520); - const svgHeight = +(appearanceConfig.svgHeight || 250); - const axisHeight = appearanceConfig.axisHidden - ? appearanceConfig.controlHidden - ? 0 - : 5 - : 21; - const wholeBarHeight = svgHeight - axisHeight; - const disFromHoverBoxToPointer = +( - appearanceConfig.disFromPercentBoxToDragBar || 20 - ); - const disFromPercentBoxToDragBar = +( - appearanceConfig.disFromPercentBoxToDragBar || 2 - ); - const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); - const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% - // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 - const whole_bars_background_padding = +( - appearanceConfig.whole_bars_background_padding || 20 - ); - /** constant end */ - const { accountId } = useWalletSelector(); - useEffect(() => { - // get all token prices - getBoostTokenPrices().then((result) => { - setTokenPriceList(result); - }); - }, []); - // init - useEffect(() => { - clearTimeout(timerObj.timer); - if (pool_id) { - timerObj.timer = setTimeout(() => { - get_pool_detail(pool_id); - }, 500); - } - }, [pool_id]); - // init 从后端获取数据 - useEffect(() => { - if (pool) { - get_chart_data(); - } - }, [pool, accountId]); - // 更新用户数据 - useEffect(() => { - if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { - const new_list = get_latest_user_chart_data(); - setChartDataList(new_list); - } - }, [newlyAddedLiquidities, user_liquidities]); - // 绘制图表 - useEffect(() => { - if ( - (chartType !== 'USER' && price_range && chartDataList) || - (chartType == 'USER' && chartDataList?.length) - ) { - drawChart(); - setDrawChartDone(true); - } - }, [price_range, chartDataList]); - useEffect(() => { - if ( - isValid(dragLeftPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragLeftPoint; - const newPrice = get_price_by_point(newPoint); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = rightX - x - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`) - .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) - .attr('width', W); - } - }, [dragLeftPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(leftPoint)) { - setDragLeftPoint(leftPoint); - } - }, [leftPoint]); - useEffect(() => { - if ( - isValid(dragRightPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragRightPoint; - const newPrice = get_price_by_point(newPoint); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr( - 'transform', - `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = x - leftX - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`).attr('width', W); - } - }, [dragRightPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(rightPoint)) { - setDragRightPoint(rightPoint); - } - }, [rightPoint]); - useEffect(() => { - if (config?.radiusMode && config?.targetPoint && drawChartDone) { - // hide drag bar and show target price bar - draw_radius_mode_bar(); - d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); - d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); - } else { - d3.select(`${randomId} .leftBar`).attr('style', ''); - d3.select(`${randomId} .rightBar`).attr('style', ''); - d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); - } - }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); - /** - * 中文 - * remove 流动性,当参数改变,重新绘制删除区域的北京框 - */ - useEffect(() => { - if (removeParams && drawChartDone) { - const scale = scaleAxis(); - const scaleBar = scaleAxisY(); - draw_background_bars_for_select_area({ scale, scaleBar }); - } - }, [ - removeParams?.all, - removeParams?.fromLeft, - removeParams?.fromRight, - removeParams?.point, - drawChartDone, - ]); - /** - * 获取个人流动性图表详情信息 - * 用户 hover 框里数据展示 - */ - useEffect(() => { - if ( - user_liquidities.length && - pool && - chartType == 'USER' && - tokenPriceList - ) { - get_user_liquidities_detail(); - } - }, [user_liquidities, pool, chartType, tokenPriceList]); - async function get_user_liquidities_detail() { - const { token_x_metadata, token_y_metadata, pool_id } = pool; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ - pool_id, - account_id: accountId, - }); - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - - const points: number[] = []; - let total_x_amount = Big(0); - let total_y_amount = Big(0); - let total_value = Big(0); - let total_fee_earned = Big(0); - let apr_24 = ''; - if (dcl_fee_result) { - const dclAccountFee: IDCLAccountFee = dcl_fee_result; - const { total_earned_fee } = dclAccountFee; - // 总共赚到的fee - const { total_fee_x, total_fee_y } = total_earned_fee || {}; - const total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - const total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value.plus( - total_earned_fee_y_value - ); - // 24h 利润 - apr_24 = formatPercentage( - get_account_24_apr(dcl_fee_result, pool, tokenPriceList) - ); - } - user_liquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point } = l; - points.push(left_point, right_point); - }); - const [total_token_x_amount, total_token_y_amount] = - get_token_amount_in_user_liquidities({ - user_liquidities, - pool, - token_x_metadata, - token_y_metadata, - }); - - total_x_amount = total_x_amount.plus(total_token_x_amount); - total_y_amount = total_y_amount.plus(total_token_y_amount); - - const total_x_value = Big(price_x).mul(total_x_amount); - const total_y_value = Big(price_y).mul(total_y_amount); - - total_value = total_x_value.plus(total_y_value); - points.sort((b: number, a: number) => { - return b - a; - }); - const min_point = points[0]; - const max_point = points[points.length - 1]; - const min_price = get_price_by_point(min_point); - const max_price = get_price_by_point(max_point); - set_user_liquidities_detail({ - total_value: formatWithCommas_usd(total_value.toFixed()), - min_price: formatPrice(min_price), - max_price: formatPrice(max_price), - total_x_amount: formatNumber(total_x_amount.toFixed()), - total_y_amount: formatNumber(total_y_amount.toFixed()), - apr_24, - total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), - }); - } - function get_latest_user_chart_data() { - const { token_x_metadata, token_y_metadata } = pool; - const { bin: bin_final } = getConfig(); - const nfts = user_liquidities.concat(newlyAddedLiquidities || []); - const list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - return list; - } - /** - * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 - */ - async function get_pool_detail(pool_id: string) { - const p: PoolInfo = await get_pool(pool_id); - const { token_x, token_y } = p; - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - setPool(p); - } - async function get_chart_data() { - const { range } = getConfig(); - const list = await get_data_from_back_end(); - if (chartType !== 'USER') { - const [price_l_default, price_r_default] = - get_price_range_by_percent(range); - set_price_range([+price_l_default, +price_r_default]); - setZoom(range); - } - setChartDataList(list); - } - async function get_data_from_back_end() { - const { point_delta, token_x_metadata, token_y_metadata, pool_id } = pool; - const { bin: bin_final, rangeGear } = getConfig(); - const decimalRate_point = - Math.pow(10, token_y_metadata.decimals) / - Math.pow(10, token_x_metadata.decimals); - const [price_l, price_r] = get_price_range_by_percent(rangeGear[0]); - const point_l = getPointByPrice(point_delta, price_l, decimalRate_point); - const point_r = getPointByPrice(point_delta, price_r, decimalRate_point); - let list = []; - if (chartType == 'USER' && accountId) { - const liquidities = await list_liquidities(); - const nfts = liquidities.filter((l: UserLiquidityInfo) => { - return l.pool_id == pool_id; - }); - set_user_liquidities(nfts); - list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - } else { - const pointsData_apr = await getDclPoolPoints( - pool_id, - bin_final, - point_l, - point_r - ); - const marketdepthData = await get_pool_marketdepth(pool_id); - const { liquidities, orders } = marketdepthData; - let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); - - /** - * 去找 left_point左点位是当前点位,右点位也是当前点位的数据,如果有两条,这需要把这两条数据合并,否则不动。 - */ - let merged_current_point_liquidity: ILiquidityInfoPool; - const contain_current_point_liquidities: ILiquidityInfoPool[] = []; - const exclude_current_point_liquidities_array = liquidities_array.filter( - (l: ILiquidityInfoPool) => { - const { left_point, right_point } = l; - if ( - right_point == pool.current_point || - left_point == pool.current_point - ) { - contain_current_point_liquidities.push(l); - return false; - } - return true; - } - ); - if (contain_current_point_liquidities.length == 2) { - contain_current_point_liquidities.sort( - (b: ILiquidityInfoPool, a: ILiquidityInfoPool) => { - return b.left_point - a.left_point; - } - ); - merged_current_point_liquidity = { - left_point: contain_current_point_liquidities[0].left_point, - right_point: contain_current_point_liquidities[1].right_point, - amount_l: contain_current_point_liquidities[0].amount_l, - }; - exclude_current_point_liquidities_array.push( - merged_current_point_liquidity - ); - liquidities_array = exclude_current_point_liquidities_array; - } - const orders_array: IOrderInfoPool[] = Object.values(orders); - const pointsData_l = divide_liquidities_into_bins_pool({ - liquidities: liquidities_array, - orders: orders_array, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - console.log('pointsData_l', pointsData_l); - console.log('pointsData_apr', pointsData_apr.point_data); - const binWidth = bin_final * point_delta; - list = combine_data( - pointsData_apr?.point_data, - pointsData_l, - point_l, - point_r, - binWidth - ); - // list = pointsData_apr.point_data; - } - return list; - } - function combine_data( - pointsData_apr: IChartData[], - pointsData_l: IChartData[], - min_point: number, - max_point: number, - binWidth: number - ) { - const pointsData: IChartData[] = []; - const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - const pointsData_l_inRange = pointsData_l?.filter((d: IChartData) => { - return d.point >= min_point && d.point + binWidth <= max_point; - }); - const pointsData_l_map = pointsData_l_inRange?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - if (pointsData_l_map) { - Object.keys(pointsData_l_map).forEach((point_l: string) => { - const { - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - point, - pool_id, - } = pointsData_l_map[point_l]; - - const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; - - pointsData.push({ - fee: fee || '0', - total_liquidity: total_liquidity || '0', - pool_id, - point, - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - }); - pointsData.sort((b: IChartData, a: IChartData) => { - return b.point - a.point; - }); - }); - } - return pointsData; - } - function getChartDataListInRange() { - const point_l = get_point_by_price(price_range[0].toString()); - const point_r = get_point_by_price( - price_range[price_range.length - 1].toString() - ); - const bin_width = getConfig().bin * pool.point_delta; - const chartDataListInRange = chartDataList.filter((d: IChartData) => { - const { point } = d; - const point_right = point + bin_width; - return point_right > point_l && point < point_r; - }); - return chartDataListInRange; - } - function get_price_range_by_percent(percent: number): [string, string] { - const p_l_r = percent / 100; - const price = get_current_price(); - const price_l_temp = Big(1 - p_l_r).mul(price); - const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); - const price_r = Big(1 + p_l_r) - .mul(price) - .toFixed(); - - return [price_l, price_r]; - } - function drawChart() { - const data: IChartData[] = process_back_end_data_in_range(); - const scale = scaleAxis(); - const scaleBar = scaleAxisY(); - // down bars - draw_down_bars({ data, scale, scaleBar }); - // up bars - if (chartType !== 'USER') { - draw_up_bars({ data, scale, scaleBar }); - } - // 创建横坐标轴 - if (appearanceConfig.axisHidden) { - d3.select(`${randomId} .axis`).remove(); - } else { - draw_axis({ scale }); - } - // background bars - if (appearanceConfig.hoverBoxHidden) { - d3.select(`${randomId} .bars_background`).remove(); - d3.select(`${randomId} .overBox`).remove(); - d3.select(`${randomId} .whole_bars_background`).remove(); - d3.select(`${randomId} .wholeOverBox`).remove(); - } else { - draw_background_bars({ data, scale, scaleBar }); - } - // remove select area - if (chartType == 'USER' && removeParams) { - draw_background_bars_for_select_area({ scale, scaleBar }); - } else { - d3.select('.remove_bars_background').remove(); - } - - // current line - if (appearanceConfig.currentBarHidden) { - d3.select(`${randomId} .currentLine`).remove(); - } else { - draw_current_bar({ scale }); - } - if (appearanceConfig.controlHidden) { - remove_control(); - } else { - // init - // drag left - draw_drag_left({ scale }); - // drag right - draw_drag_right({ scale }); - } - } - function process_back_end_data_in_range() { - const { bin: bin_final } = getConfig(); - const { token_x_metadata, token_y_metadata, point_delta } = pool; - const decimalRate_price = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - const list = - chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const data: IChartData[] = list.map((o: IChartData) => { - const { point } = o; - const price_l = getPriceByPoint(+point, decimalRate_price); - const point_r = +point + point_delta * bin_final; - const price_r = getPriceByPoint(point_r, decimalRate_price); - - return { - ...o, - liquidity: Big(o.liquidity || 0).toFixed(), - order_liquidity: Big(o.order_liquidity || 0).toFixed(), - token_x: Big(o.token_x || 0).toFixed(), - token_y: Big(o.token_y || 0).toFixed(), - order_x: Big(o.order_x || 0).toFixed(), - order_y: Big(o.order_y || 0).toFixed(), - total_liquidity: Big(o.total_liquidity || 0).toFixed(), - fee: Big(o.fee || 0).toFixed(), - price: price_l.toString(), - price_r: price_r.toString(), - point_r: point_r, - }; - }); - return data; - } - function hoverBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = - d; - const { colors } = getConfig(); - - const total_token_x = Big(token_x).plus(order_x); - const total_token_y = Big(token_y).plus(order_y); - const price_by_token_x = get_price_by_point(+point); - const price_by_token_y = reverse_price(price_by_token_x); - const apr = Big(total_liquidity).gt(0) - ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() - : '0'; - setBinDetail({ - feeApr: formatPercentage(apr), - colors, - ...(total_token_x.gt(0) - ? { - token_x_amount: formatWithCommas_number(total_token_x.toFixed()), - token_x_amount_in_liquidity: formatWithCommas_number(token_x), - token_x_amount_in_order: formatWithCommas_number(order_x), - } - : {}), - ...(total_token_y.gt(0) - ? { - token_y_amount: formatWithCommas_number(total_token_y.toFixed()), - token_y_amount_in_liquidity: formatWithCommas_number(token_y), - token_y_amount_in_order: formatWithCommas_number(order_y), - } - : {}), - price_by_token_x: formatPrice(price_by_token_x), - price_by_token_y: formatPrice(price_by_token_y), - }); - } - function LeaveBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function hoverUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function LeaveUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function remove_control() { - d3.select(`${randomId} .control`).remove(); - d3.select(`${randomId} .overlap`).remove(); - d3.select(`${randomId} .rightBar`).remove(); - d3.select(`${randomId} .leftBar`).remove(); - } - function draw_axis({ scale }: { scale: any }) { - const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); - if (appearanceConfig.ticks || chartType == 'USER') { - axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { - const dBig = new Big(d); - if (dBig.gte(10000)) { - return dBig.toFixed(0); - } else { - return d; - } - }); - } - d3.select(`${randomId} svg .axis`) - .call(axis) - .selectAll('text') - .attr('fill', '#7E8A93'); - d3.select(`${randomId} svg .axis`) - .attr('transform', `translate(0, ${svgHeight - axisHeight})`) - .select('.domain') - .attr('stroke', 'transparent'); - } - function draw_down_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { current_point } = pool; - const { colors } = getConfig(); - d3.select(`${randomId} .bars_liquidity`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }); - } - function draw_up_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { colors } = getConfig(); - const { current_point } = pool; - d3.select(`${randomId} .bars_order`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.order_liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return ( - wholeBarHeight - - get_final_bar_height(scaleBar(+d.liquidity)) - - get_final_bar_height(scaleBar(+d.order_liquidity)) - ); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }) - .attr('opacity', '0.7'); - } - function get_final_bar_height(h: number) { - if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) - return min_bar_height; - return h; - } - function draw_background_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - if (chartType == 'USER') { - draw_background_bars_user({ - scale, - scaleBar, - }); - } else { - draw_background_bars_pool({ - data, - scale, - }); - } - } - function draw_background_bars_pool({ - data, - scale, - }: { - data: IChartData[]; - scale: Function; - }) { - d3.select(`${randomId} .bars_background`) - .selectAll('rect') - .data(data) - .join('rect') - .on('mousemove', function (e, d) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverBox(e, d); - }) - .on('mouseleave', function (e, d) { - d3.select(this).attr('fill', 'transparent'); - LeaveBox(e, d); - }) - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price).toNumber()) - ); - }) - .attr('height', function (d) { - return wholeBarHeight; - }) - .attr('x', function (d) { - return scale(Big(d.price).toNumber()); - }) - .attr('y', function (d) { - return 0; - }) - .attr('rx', 2) - .attr('fill', 'transparent'); - } - function draw_background_bars_user({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - d3.select(`${randomId} .whole_bars_background`) - .on('mousemove', function (e) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverUserBox(e); - }) - .on('mouseleave', function (e) { - d3.select(this).attr('fill', 'transparent'); - LeaveUserBox(e); - }) - .transition() - .attr('width', function () { - return ( - scale(sortP[sortP.length - 1]) - - scale(sortP[0]) + - whole_bars_background_padding * 2 - ); - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - return scale(sortP[0]) - whole_bars_background_padding; - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'transparent'); - } - function draw_background_bars_for_select_area({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - const min_bin_price = sortP[0]; - const max_bin_price = sortP[sortP.length - 1]; - const { fromLeft, fromRight, all, point } = removeParams; - d3.select(`${randomId} .remove_bars_background`) - .attr('width', function () { - if (all) { - return scale(max_bin_price) - scale(min_bin_price); - } else if (fromLeft) { - return scale(get_price_by_point(point)) - scale(min_bin_price); - } else if (fromRight) { - return scale(max_bin_price) - scale(get_price_by_point(point)); - } - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - if (fromRight) { - return scale(get_price_by_point(point)); - } else { - return scale(min_bin_price); - } - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'rgba(255,255,255,0.1)'); - } - function draw_current_bar({ scale }: { scale: Function }) { - d3.select(`${randomId} .currentLine`).attr( - 'style', - `transform:translate(${ - scale(+get_current_price()) + svgPaddingX - }px, -${axisHeight}px)` - ); - } - function draw_drag_left({ scale }: { scale: any }) { - // 初始化左的位置 - const price = get_current_price(); - let price_l; - if (dragLeftPoint) { - price_l = get_price_by_point(dragLeftPoint); - } else { - const price_l_temp = Big(1 - defaultPercent / 100) - .mul(price) - .toFixed(); - const newLeftPoint = get_nearby_bin_left_point( - get_point_by_price(price_l_temp) - ); - price_l = get_price_by_point(newLeftPoint); - setDragLeftPoint(newLeftPoint); - } - const x = scale(price_l) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(`${diffPrices(price_l)}%`) - .attr('fill', 'white'); - const dragLeft = d3.drag().on('drag', function (e) { - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - if (rightX < e.x || e.x < dragBarWidth / 2) return; - const p = scale.invert(e.x); - const newLeftPoint = get_nearby_bin_left_point(get_point_by_price(p)); - - setDragLeftPoint(newLeftPoint); - setLeftPoint && setLeftPoint(newLeftPoint); - }); - d3.select(`${randomId} .drag-left`).call(dragLeft); - } - function draw_drag_right({ scale }: { scale: any }) { - // 初始化右的位置 - const price = get_current_price(); - let price_r; - if (dragRightPoint) { - price_r = get_price_by_point(dragRightPoint); - } else { - const price_r_temp = Big(1 + defaultPercent / 100) - .mul(price) - .toFixed(); - const newRightPoint = get_nearby_bin_right_point( - get_point_by_price(price_r_temp) - ); - price_r = get_price_by_point(newRightPoint); - setDragRightPoint(newRightPoint); - } - const x = scale(price_r); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) - .select('text') - .text(`${diffPrices(price_r)}%`) - .attr('fill', 'white'); - const dragRight = d3.drag().on('drag', (e) => { - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); - if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; - const p = scale.invert(e.x); - const newRightPoint = get_nearby_bin_right_point(get_point_by_price(p)); - setDragRightPoint(newRightPoint); - setRightPoint && setRightPoint(newRightPoint); - }); - d3.select(`${randomId} .drag-right`).call(dragRight); - } - function draw_radius_mode_bar() { - const scale: any = scaleAxis(); - const { targetPoint } = config; - const price = get_price_by_point(targetPoint); - const x = scale(price); - d3.select(`${randomId} .radiusBar`) - .attr('transform', `translate(${x}, -${axisHeight})`) - .attr('style', 'display:block'); - } - function get_current_price() { - return get_price_by_point(pool.current_point); - } - function get_point_by_price(price: string) { - const { point_delta, token_x_metadata, token_y_metadata } = pool; - const decimalRate_point = - Math.pow(10, token_y_metadata.decimals) / - Math.pow(10, token_x_metadata.decimals); - const point = getPointByPrice(point_delta, price, decimalRate_point); - return point; - } - function get_price_by_point(point: number) { - const { token_x_metadata, token_y_metadata } = pool; - const decimalRate_price = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - return getPriceByPoint(point, decimalRate_price); - } - function get_nearby_bin_right_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin + slots; - } - function get_nearby_bin_left_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin; - } - function clickToLeft() { - const { bin } = getConfig(); - const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); - setDragLeftPoint(newPoint_nearby_bin); - setLeftPoint && setLeftPoint(newPoint_nearby_bin); - } - function clickToRight() { - const { bin } = getConfig(); - const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); - setDragRightPoint(newPoint_nearby_bin); - setRightPoint && setRightPoint(newPoint_nearby_bin); - } - function get_bin_point_by_point(point: number, type?: IRMTYPE) { - const { point_delta } = pool; - const slot_num = getConfig().bin; - return getBinPointByPoint(point_delta, slot_num, point, type); - } - function scaleAxis() { - if (chartType == 'USER') { - return scaleAxis_User(); - } else { - return scaleAxis_Pool(); - } - } - function scaleAxis_Pool() { - return d3 - .scaleLinear() - .domain(price_range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxis_User() { - const binWidth = get_bin_width(); - const min_point = Math.max( - chartDataList[0].point - binWidth * 2, - POINTLEFTRANGE - ); - const max_point = Math.min( - chartDataList[chartDataList.length - 1].point + binWidth * 2, - POINTRIGHTRANGE - ); - const min_price = get_price_by_point(min_point); - const max_price = get_price_by_point(max_point); - const range = [+min_price, +max_price]; - return d3 - .scaleLinear() - .domain(range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxisY() { - if (chartType == 'USER') { - return scaleAxisY_User(); - } else { - return scaleAxisY_Pool(); - } - } - function scaleAxisY_Pool() { - const data: IChartData[] = process_back_end_data_in_range(); - const L: number[] = []; - data.forEach((o: IChartData) => { - const { liquidity, order_liquidity } = o; - L.push( - +liquidity, - +order_liquidity, - Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() - ); - }); - const sortL = sortBy(L); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]); - } - function scaleAxisY_User() { - const { sortY: sortL } = get_price_and_liquidity_range(); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight - whole_bars_background_padding]) - .clamp(true); - } - function get_bin_width() { - const slot_num_in_a_bin = getConfig().bin; - const { point_delta } = pool; - return point_delta * slot_num_in_a_bin; - } - function get_price_and_liquidity_range() { - // todo - const Y: number[] = []; - const X: number[] = []; - const chartDataListInrange = - chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const binWidth = getConfig().bin * pool.point_delta; - chartDataListInrange.forEach((o: IChartData, index) => { - const { liquidity, point } = o; - Y.push(+liquidity); - X.push(+point); - if (index == chartDataListInrange.length - 1) { - X.push(+point + binWidth); - } - }); - const sortY = sortBy(Y); - const sortX = sortBy(X); - const sortX_Price = sortX.map((x) => { - return get_price_by_point(x); - }); - return { sortP: sortX_Price, sortY }; - } - function diffPrices(newPrice: string, peferencePrice?: string) { - let movePercent; - const price = peferencePrice || get_current_price(); - if (+price > +newPrice) { - movePercent = -Big(1) - .minus(Big(newPrice).div(price)) - .mul(100) - .toFixed(0, 1); - } else { - movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); - } - return movePercent; - } - function getConfig(): IChartItemConfig { - const { bin, range, colors, rangeGear } = - get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const percent_final = custom_config[pool_id]?.range || range; - const bin_final = custom_config[pool_id]?.bin || bin; - const colors_final = custom_config[pool_id]?.colors || colors; - const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; - return { - bin: bin_final, - range: percent_final, - colors: colors_final, - rangeGear: rangeGear_final, - }; - } - function get_current_price_by_token_x() { - if (pool) { - return formatPriceWithCommas(get_current_price()); - } - return '-'; - } - function get_current_price_by_token_y() { - if (pool) { - return formatPriceWithCommas(reverse_price(get_current_price())); - } - return '-'; - } - function zoomOut() { - const { rangeGear } = getConfig(); - const targetPercent = rangeGear.find((item) => item < zoom); - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - function zoomIn() { - const { rangeGear } = getConfig(); - const index = rangeGear.findIndex((item) => item == zoom); - let targetPercent; - if (index !== 0) { - targetPercent = rangeGear[index - 1]; - } - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - const rangeGear = getConfig().rangeGear; - const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; - const is_in_min_zoom = zoom == rangeGear[0]; - return ( -
- {/* 控件按钮*/} -
- {/*
- -
*/} -
- -
-
- -
- {/*
- -
*/} -
- - - - - - - - {/* 横坐标轴 */} - - {/* 拖拽线 left */} - - - - - - - - - - - - - - {/* 拖拽线 right */} - - - - - - - - - - - - - - {/* 左右坐标轴中间的重叠区域 */} - - - - {/* radius 模式下 target price 对应的柱子 */} - - - - - - - - - {/* hover到柱子(bin)上的悬浮框 */} -
-
- APR(24h) - - {binDetail?.feeApr} - -
-
- Price - - {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} /{' '} - {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} - -
- {binDetail?.token_x_amount ? ( - <> -
- - {pool?.token_x_metadata?.symbol} Amount - - - {binDetail.token_x_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_x_amount_in_liquidity} - -
-
-
- - in Limit Orders -
- - {binDetail.token_x_amount_in_order} - -
- - ) : null} - {binDetail?.token_y_amount ? ( - <> -
- - {pool?.token_y_metadata?.symbol} Amount - - - {binDetail.token_y_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_y_amount_in_liquidity} - -
-
-
- - in Limit Orders -
- - {binDetail.token_y_amount_in_order} - -
- - ) : null} -
-
-
- Your Liquidity - - {user_liquidities_detail?.total_value || '-'} - -
-
- Price Range - - {user_liquidities_detail?.min_price} -{' '} - {user_liquidities_detail?.max_price} - -
-
- Position - - {user_liquidities_detail?.total_x_amount}{' '} - {pool?.token_x_metadata.symbol} +{' '} - {user_liquidities_detail?.total_y_amount}{' '} - {pool?.token_y_metadata.symbol} - -
-
- APR(24h) - - {user_liquidities_detail?.apr_24 || '-'} - -
-
- Total Earned Fee - - {user_liquidities_detail?.total_earned_fee || '-'} - -
-
- {/* current 价格 */} -
-
-
-
- - {pool?.token_x_metadata?.symbol}:{' '} - - - - {pool?.token_y_metadata?.symbol} - -
-
- - {pool?.token_y_metadata?.symbol}:{' '} - - - - {pool?.token_x_metadata?.symbol} - -
-
-
-
- ); -} -function DclChartReverse({ - pool_id, - leftPoint, - rightPoint, - setLeftPoint, - setRightPoint, - config, - chartType, - removeParams, - newlyAddedLiquidities, -}: { - pool_id: string; - leftPoint?: number; - rightPoint?: number; - setLeftPoint?: Function; - setRightPoint?: Function; - config?: IPoolChartConfig; - chartType?: 'POOL' | 'USER'; - removeParams?: { - fromLeft?: boolean; - fromRight?: boolean; - point?: number; - all?: boolean; - }; - newlyAddedLiquidities?: UserLiquidityInfo[]; -}) { - const [pool, setPool] = useState(); - const [price_range, set_price_range] = useState(); - const [chartDataList, setChartDataList] = useState(); - const [binDetail, setBinDetail] = useState(); - const [dragLeftPoint, setDragLeftPoint] = useState(); - const [dragRightPoint, setDragRightPoint] = useState(); - const [zoom, setZoom] = useState(); - const [randomId, setRandomId] = useState('.' + createRandomString()); - const [drawChartDone, setDrawChartDone] = useState(false); - const [user_liquidities, set_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - const [user_liquidities_detail, set_user_liquidities_detail] = - useState(); - const [tokenPriceList, setTokenPriceList] = useState>(); - /** constant start */ - const appearanceConfig: IPoolChartConfig = config || {}; - let [timerObj, setTimerObj] = useState({ - timer: '', - }); - const dragBarWidth = 28; - const percentBoxWidth = 44; - const min_bar_height = 2; - const svgWidth = +(appearanceConfig.svgWidth || 520); - const svgHeight = +(appearanceConfig.svgHeight || 250); - const axisHeight = appearanceConfig.axisHidden - ? appearanceConfig.controlHidden - ? 0 - : 5 - : 21; - const wholeBarHeight = svgHeight - axisHeight; - const disFromHoverBoxToPointer = +( - appearanceConfig.disFromPercentBoxToDragBar || 20 - ); - const disFromPercentBoxToDragBar = +( - appearanceConfig.disFromPercentBoxToDragBar || 2 - ); - const svgPaddingX = +(appearanceConfig.svgPaddingX || 10); - const defaultPercent = +(appearanceConfig.defaultPercent || 10); // 初始化左侧右侧价格与当前价格的间距百分比 10===》10%, e.g. 右侧价格是当前价格的 1 + 10% - // hover 到用户图表上时,hover出来的背景框要大些,设置多扩充出来的大小。 - const whole_bars_background_padding = +( - appearanceConfig.whole_bars_background_padding || 20 - ); - /** constant end */ - const { accountId } = useWalletSelector(); - useEffect(() => { - // get all token prices - getBoostTokenPrices().then((result) => { - setTokenPriceList(result); - }); - }, []); - // init - useEffect(() => { - clearTimeout(timerObj.timer); - if (pool_id) { - timerObj.timer = setTimeout(() => { - get_pool_detail(pool_id); - }, 500); - } - }, [pool_id]); - // init 从后端获取数据 - useEffect(() => { - // =========>ok - if (pool) { - get_chart_data(); - } - }, [pool, accountId]); - // 更新用户数据 - useEffect(() => { - // ==========>todo 等会看 - if (pool && accountId && newlyAddedLiquidities && chartType == 'USER') { - const new_list = get_latest_user_chart_data(); - setChartDataList(new_list); - } - }, [newlyAddedLiquidities, user_liquidities]); - // 绘制图表 - useEffect(() => { - // =========>ok - if ( - (chartType !== 'USER' && price_range && chartDataList) || - (chartType == 'USER' && chartDataList?.length) - ) { - drawChart(); - setDrawChartDone(true); - } - }, [price_range, chartDataList]); - useEffect(() => { - // =========> ok - if ( - isValid(dragLeftPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragLeftPoint; - const newPrice = reverse_price(get_price_by_point(newPoint)); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = rightX - x - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`) - .attr('transform', `translate(${x + dragBarWidth / 2}, 0)`) - .attr('width', W); - } - }, [dragLeftPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(leftPoint)) { - setDragLeftPoint(leftPoint); - } - }, [leftPoint]); - useEffect(() => { - // =========> ok - if ( - isValid(dragRightPoint) && - !appearanceConfig.controlHidden && - drawChartDone - ) { - const scale = scaleAxis(); - const newPoint = dragRightPoint; - const newPrice = reverse_price(get_price_by_point(newPoint)); - const movePercent = diffPrices(newPrice); - const x = scale(+newPrice); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr( - 'transform', - `translate(${x + (disFromPercentBoxToDragBar + 2)}, 0)` - ) - .select('text') - .text(movePercent + '%') - .attr('fill', 'white'); - - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const W = x - leftX - dragBarWidth / 2; - d3.select(`${randomId} .overlap rect`).attr('width', W); - } - }, [dragRightPoint, price_range, drawChartDone]); - useEffect(() => { - if (isValid(rightPoint)) { - setDragRightPoint(rightPoint); - } - }, [rightPoint]); - useEffect(() => { - // ========> todo 待看 - if (config?.radiusMode && config?.targetPoint && drawChartDone) { - // hide drag bar and show target price bar - draw_radius_mode_bar(); - d3.select(`${randomId} .leftBar`).attr('style', 'display:none'); - d3.select(`${randomId} .rightBar`).attr('style', 'display:none'); - } else { - d3.select(`${randomId} .leftBar`).attr('style', ''); - d3.select(`${randomId} .rightBar`).attr('style', ''); - d3.select(`${randomId} .radiusBar`).attr('style', 'display:none'); - } - }, [config?.radiusMode, config?.targetPoint, pool_id, drawChartDone]); - /** - * 中文 - * remove 流动性,当参数改变,重新绘制删除区域的北京框 - */ - useEffect(() => { - // =======> ok - if (removeParams && drawChartDone) { - const scale = scaleAxis(); - const scaleBar = scaleAxisY(); - draw_background_bars_for_select_area({ scale, scaleBar }); - } - }, [ - removeParams?.all, - removeParams?.fromLeft, - removeParams?.fromRight, - removeParams?.point, - drawChartDone, - ]); - /** - * 获取个人流动性图表详情信息 - * 用户 hover 框里数据展示 - */ - useEffect(() => { - // =======> ok - if ( - user_liquidities.length && - pool && - chartType == 'USER' && - tokenPriceList - ) { - get_user_liquidities_detail(); - } - }, [user_liquidities, pool, chartType, tokenPriceList]); - async function get_user_liquidities_detail() { - const { token_x_metadata, token_y_metadata, pool_id } = pool; - const dcl_fee_result: IDCLAccountFee | any = await getDCLAccountFee({ - pool_id, - account_id: accountId, - }); - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - - const points: number[] = []; - let total_x_amount = Big(0); - let total_y_amount = Big(0); - let total_value = Big(0); - let total_fee_earned = Big(0); - let apr_24 = ''; - if (dcl_fee_result) { - const dclAccountFee: IDCLAccountFee = dcl_fee_result; - const { total_earned_fee } = dclAccountFee; - // 总共赚到的fee - const { total_fee_x, total_fee_y } = total_earned_fee || {}; - const total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - const total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value.plus( - total_earned_fee_y_value - ); - // 24h 利润 - apr_24 = formatPercentage( - get_account_24_apr(dcl_fee_result, pool, tokenPriceList) - ); - } - user_liquidities.forEach((l: UserLiquidityInfo) => { - const { left_point, right_point, amount } = l; - points.push(left_point, right_point); - const { total_x, total_y } = get_x_y_amount_by_condition({ - left_point, - right_point, - amount, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - total_x_amount = total_x_amount.plus(total_x); - total_y_amount = total_y_amount.plus(total_y); - }); - const total_x_value = Big(price_x).mul(total_x_amount); - const total_y_value = Big(price_y).mul(total_y_amount); - total_value = total_x_value.plus(total_y_value); - points.sort((b: number, a: number) => { - return b - a; - }); - const min_point = points[0]; - const max_point = points[points.length - 1]; - const min_price = reverse_price(get_price_by_point(max_point)); - const max_price = reverse_price(get_price_by_point(min_point)); - set_user_liquidities_detail({ - total_value: formatWithCommas_usd(total_value.toFixed()), - min_price: formatPrice(min_price), - max_price: formatPrice(max_price), - total_x_amount: formatNumber(total_x_amount.toFixed()), - total_y_amount: formatNumber(total_y_amount.toFixed()), - apr_24, - total_earned_fee: formatWithCommas_usd(total_fee_earned.toFixed()), - }); - } - function get_latest_user_chart_data() { - const { token_x_metadata, token_y_metadata } = pool; - const { bin: bin_final } = getConfig(); - const nfts = user_liquidities.concat(newlyAddedLiquidities || []); - const list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - return list; - } - /** - * 用户图表来说,新增的Liquidities发生改变时,把数据叠加重新绘制图表 - */ - async function get_pool_detail(pool_id: string) { - const p: PoolInfo = await get_pool(pool_id); - const { token_x, token_y } = p; - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - setPool(p); - } - async function get_chart_data() { - const { range } = getConfig(); - const list = await get_data_from_back_end(); - if (chartType !== 'USER') { - const [price_l_default, price_r_default] = - get_price_range_by_percent(range); - set_price_range([+price_l_default, +price_r_default]); - setZoom(range); - } - setChartDataList(list); - } - async function get_data_from_back_end() { - const { token_x_metadata, token_y_metadata, pool_id } = pool; - const { bin: bin_final, rangeGear } = getConfig(); - const [price_l, price_r] = get_price_range_by_percent(rangeGear[0], true); - const point_l = get_point_by_price(price_l); - const point_r = get_point_by_price(price_r); - let list = []; - if (chartType == 'USER' && accountId) { - const liquidities = await list_liquidities(); - const nfts = liquidities.filter((l: UserLiquidityInfo) => { - return l.pool_id == pool_id; - }); - set_user_liquidities(nfts); - list = divide_liquidities_into_bins_user({ - liquidities: nfts, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - } else { - const pointsData_apr = await getDclPoolPoints( - pool_id, - bin_final, - point_l, - point_r - ); - const marketdepthData = await get_pool_marketdepth(pool_id); - const { liquidities, orders } = marketdepthData; - let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); - - // 去找 left_point左点位是当前点位,右点位也是当前点位的两条数据,把这两条数据合并成一条,因为合约侧是从当前点位从两侧开始查找 - let range_contain_current_point: any; - liquidities_array = liquidities_array.filter((l: ILiquidityInfoPool) => { - const { left_point, right_point, amount_l } = l; - if (right_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['left_point'] = left_point; - range_contain_current_point['amount_l'] = amount_l; - return false; - } - if (left_point == pool.current_point) { - range_contain_current_point = range_contain_current_point || {}; - range_contain_current_point['right_point'] = right_point; - range_contain_current_point['amount_l'] = amount_l; - return false; - } - return true; - }); - if (range_contain_current_point) { - liquidities_array.push(range_contain_current_point); - } - const orders_array: IOrderInfoPool[] = Object.values(orders); - const pointsData_l = divide_liquidities_into_bins_pool({ - liquidities: liquidities_array, - orders: orders_array, - slot_number_in_a_bin: bin_final, - tokenX: token_x_metadata, - tokenY: token_y_metadata, - poolDetail: pool, - }); - list = combine_data(pointsData_apr?.point_data, pointsData_l); - } - return list; - } - function combine_data( - pointsData_apr: IChartData[], - pointsData_l: IChartData[] - ) { - const pointsData: IChartData[] = []; - const pointsData_apr_map = pointsData_apr?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - const pointsData_l_map = pointsData_l?.reduce((acc, cur) => { - return { - ...acc, - [cur.point]: cur, - }; - }, {}); - if (pointsData_l_map) { - Object.keys(pointsData_l_map).forEach((point_l: string) => { - const { - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - point, - pool_id, - } = pointsData_l_map[point_l]; - - const { fee, total_liquidity } = pointsData_apr_map?.[point_l] || {}; - - pointsData.push({ - fee: fee || '0', - total_liquidity: total_liquidity || '0', - pool_id, - point, - liquidity, - token_x, - token_y, - order_liquidity, - order_x, - order_y, - }); - pointsData.sort((b: IChartData, a: IChartData) => { - return b.point - a.point; - }); - }); - } - return pointsData; - } - function getChartDataListInRange() { - const price_range_min = price_range[0].toString(); - const price_range_max = price_range[price_range.length - 1].toString(); - const price_range_point_max = get_point_by_price( - reverse_price(price_range_min) - ); - const price_range_point_min = get_point_by_price( - reverse_price(price_range_max) - ); - const bin_width = getConfig().bin * pool.point_delta; - const chartDataListInRange = chartDataList.filter((d: IChartData) => { - const { point } = d; - const point_right = point + bin_width; - return ( - point >= price_range_point_min && point_right <= price_range_point_max - ); - }); - return chartDataListInRange; - } - function get_price_range_by_percent( - percent: number, - forward?: boolean - ): [string, string] { - const p_l_r = percent / 100; - const price = get_current_price(forward); - const price_l_temp = Big(1 - p_l_r).mul(price); - const price_l = price_l_temp.lt(0) ? '0' : price_l_temp.toFixed(); - const price_r = Big(1 + p_l_r) - .mul(price) - .toFixed(); - - return [price_l, price_r]; - } - function drawChart() { - const data: IChartData[] = process_back_end_data_in_range(); // ======>ok - const scale = scaleAxis(); // ======== ok - const scaleBar = scaleAxisY(); // ====== ok - // down bars - draw_down_bars({ data, scale, scaleBar }); // ======== ok - // up bars - if (chartType !== 'USER') { - draw_up_bars({ data, scale, scaleBar }); // ========ok - } - // 创建横坐标轴 - if (appearanceConfig.axisHidden) { - d3.select(`${randomId} .axis`).remove(); - } else { - draw_axis({ scale }); - } - // background bars - if (appearanceConfig.hoverBoxHidden) { - d3.select(`${randomId} .bars_background`).remove(); - d3.select(`${randomId} .overBox`).remove(); - d3.select(`${randomId} .whole_bars_background`).remove(); - d3.select(`${randomId} .wholeOverBox`).remove(); - } else { - draw_background_bars({ data, scale, scaleBar }); // ======= ok - } - // remove select area - if (chartType == 'USER' && removeParams) { - draw_background_bars_for_select_area({ scale, scaleBar }); // ======== ok - } else { - d3.select('.remove_bars_background').remove(); - } - - // current line - if (appearanceConfig.currentBarHidden) { - d3.select(`${randomId} .currentLine`).remove(); - } else { - draw_current_bar({ scale }); // ======== ok - } - if (appearanceConfig.controlHidden) { - remove_control(); - } else { - // init - // drag left - draw_drag_left({ scale }); // ======== ok - // drag right - draw_drag_right({ scale }); // ======= ok - } - } - function process_back_end_data_in_range() { - const { bin: bin_final } = getConfig(); - const { point_delta } = pool; - const list = - chartType == 'USER' ? chartDataList : getChartDataListInRange(); - const data: IChartData[] = list.map((o: IChartData) => { - const { point } = o; - const price_r = reverse_price(get_price_by_point(+point)); - const point_max = +point + point_delta * bin_final; - const price_l = reverse_price(get_price_by_point(point_max)); - return { - ...o, - liquidity: Big(o.liquidity || 0).toFixed(), - order_liquidity: Big(o.order_liquidity || 0).toFixed(), - token_x: Big(o.token_x || 0).toFixed(), - token_y: Big(o.token_y || 0).toFixed(), - order_x: Big(o.order_x || 0).toFixed(), - order_y: Big(o.order_y || 0).toFixed(), - total_liquidity: Big(o.total_liquidity || 0).toFixed(), - fee: Big(o.fee || 0).toFixed(), - - price_l, - price_r, - point_l: point_max, - point_r: point, - }; - }); - return data; - } - function hoverBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - const { point, token_x, token_y, order_x, order_y, fee, total_liquidity } = - d; - const { colors } = getConfig(); - - const total_token_x = Big(token_x).plus(order_x); - const total_token_y = Big(token_y).plus(order_y); - const price_by_token_x = get_price_by_point(+point); - const price_by_token_y = reverse_price(price_by_token_x); - const apr = Big(total_liquidity).gt(0) - ? Big(fee).div(total_liquidity).mul(365).mul(100).toFixed() - : '0'; - setBinDetail({ - feeApr: formatPercentage(apr), - colors, - ...(total_token_x.gt(0) - ? { - token_x_amount: formatWithCommas_number(total_token_x.toFixed()), - token_x_amount_in_liquidity: formatWithCommas_number(token_x), - token_x_amount_in_order: formatWithCommas_number(order_x), - } - : {}), - ...(total_token_y.gt(0) - ? { - token_y_amount: formatWithCommas_number(total_token_y.toFixed()), - token_y_amount_in_liquidity: formatWithCommas_number(token_y), - token_y_amount_in_order: formatWithCommas_number(order_y), - } - : {}), - price_by_token_x: formatPrice(price_by_token_x), - price_by_token_y: formatPrice(price_by_token_y), - }); - } - function LeaveBox(e: any, d: IChartData) { - d3.select(`${randomId} .overBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function hoverUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:visible;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function LeaveUserBox(e: any) { - d3.select(`${randomId} .wholeOverBox`).attr( - 'style', - `visibility:hidden;transform:translate(${ - e.offsetX + disFromHoverBoxToPointer - }px, ${e.offsetY / 2}px)` - ); - } - function remove_control() { - d3.select(`${randomId} .control`).remove(); - d3.select(`${randomId} .overlap`).remove(); - d3.select(`${randomId} .rightBar`).remove(); - d3.select(`${randomId} .leftBar`).remove(); - } - function draw_axis({ scale }: { scale: any }) { - const axis: any = d3.axisBottom(scale).tickSize(0).tickPadding(10); - if (appearanceConfig.ticks || chartType == 'USER') { - axis.ticks(appearanceConfig.ticks || 5).tickFormat(function (d: any) { - const dBig = new Big(d); - if (dBig.gte(10000)) { - return dBig.toFixed(0); - } else { - return d; - } - }); - } - d3.select(`${randomId} svg .axis`) - .call(axis) - .selectAll('text') - .attr('fill', '#7E8A93'); - d3.select(`${randomId} svg .axis`) - .attr('transform', `translate(0, ${svgHeight - axisHeight})`) - .select('.domain') - .attr('stroke', 'transparent'); - } - function draw_down_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { current_point } = pool; - const { colors } = getConfig(); - d3.select(`${randomId} .bars_liquidity`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price_l).toNumber()); - }) - .attr('y', function (d) { - return wholeBarHeight - get_final_bar_height(scaleBar(+d.liquidity)); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }); - } - function draw_up_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - const { colors } = getConfig(); - const { current_point } = pool; - d3.select(`${randomId} .bars_order`) - .selectAll('rect') - .data(data) - .join('rect') - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) - ); - }) - .attr('height', function (d) { - return get_final_bar_height(scaleBar(+d.order_liquidity)); - }) - .attr('x', function (d) { - return scale(Big(d.price_l).toNumber()); - }) - .attr('y', function (d) { - return ( - wholeBarHeight - - get_final_bar_height(scaleBar(+d.liquidity)) - - get_final_bar_height(scaleBar(+d.order_liquidity)) - ); - }) - .attr('rx', 2) - .attr('fill', function (d) { - return +d.point >= current_point ? colors[1] : colors[0]; - }) - .attr('opacity', '0.7'); - } - function get_final_bar_height(h: number) { - if (Big(h || 0).lt(min_bar_height) && Big(h || 0).gt(0)) - return min_bar_height; - return h; - } - function draw_background_bars({ - data, - scale, - scaleBar, - }: { - data: IChartData[]; - scale: Function; - scaleBar: Function; - }) { - if (chartType == 'USER') { - draw_background_bars_user({ - scale, - scaleBar, - }); - } else { - draw_background_bars_pool({ - data, - scale, - }); - } - } - function draw_background_bars_pool({ - data, - scale, - }: { - data: IChartData[]; - scale: Function; - }) { - d3.select(`${randomId} .bars_background`) - .selectAll('rect') - .data(data) - .join('rect') - .on('mousemove', function (e, d) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverBox(e, d); - }) - .on('mouseleave', function (e, d) { - d3.select(this).attr('fill', 'transparent'); - LeaveBox(e, d); - }) - .transition() - .attr('width', function (d) { - return ( - scale(Big(d.price_r).toNumber()) - scale(Big(d.price_l).toNumber()) - ); - }) - .attr('height', function (d) { - return wholeBarHeight; - }) - .attr('x', function (d) { - return scale(Big(d.price_l).toNumber()); - }) - .attr('y', function (d) { - return 0; - }) - .attr('rx', 2) - .attr('fill', 'transparent'); - } - function draw_background_bars_user({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - d3.select(`${randomId} .whole_bars_background`) - .on('mousemove', function (e) { - d3.select(this).attr('fill', 'rgba(255,255,255,0.1)'); - hoverUserBox(e); - }) - .on('mouseleave', function (e) { - d3.select(this).attr('fill', 'transparent'); - LeaveUserBox(e); - }) - .transition() - .attr('width', function () { - return ( - scale(sortP[sortP.length - 1]) - - scale(sortP[0]) + - whole_bars_background_padding * 2 - ); - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - return scale(sortP[0]) - whole_bars_background_padding; - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'transparent'); - } - function draw_background_bars_for_select_area({ - scale, - scaleBar, - }: { - scale: Function; - scaleBar: Function; - }) { - const { sortP, sortY } = get_price_and_liquidity_range(); - const min_bin_price = sortP[0]; - const max_bin_price = sortP[sortP.length - 1]; - const { fromLeft, fromRight, all, point } = removeParams; - const remove_to_price = reverse_price(get_price_by_point(point)); - - d3.select(`${randomId} .remove_bars_background`) - .attr('width', function () { - if (all) { - return scale(max_bin_price) - scale(min_bin_price); - } else if (fromLeft) { - return scale(remove_to_price) - scale(min_bin_price); - } else if (fromRight) { - return scale(max_bin_price) - scale(remove_to_price); - } - }) - .attr('height', function () { - return ( - scaleBar(sortY[sortY.length - 1]) + whole_bars_background_padding - ); - }) - .attr('x', function () { - if (fromRight) { - return scale(remove_to_price); - } else { - return scale(min_bin_price); - } - }) - .attr('y', function () { - return ( - wholeBarHeight - - scaleBar(sortY[sortY.length - 1]) - - whole_bars_background_padding - ); - }) - .attr('rx', 4) - .attr('fill', 'rgba(255,255,255,0.1)'); - } - function draw_current_bar({ scale }: { scale: Function }) { - d3.select(`${randomId} .currentLine`).attr( - 'style', - `transform:translate(${ - scale(+get_current_price()) + svgPaddingX - }px, -${axisHeight}px)` - ); - } - function draw_drag_left({ scale }: { scale: any }) { - // 初始化左的位置 - const price = get_current_price(); - let price_l; - if (dragLeftPoint) { - price_l = reverse_price(get_price_by_point(dragLeftPoint)); - } else { - const price_l_temp = Big(1 - defaultPercent / 100) - .mul(price) - .toFixed(); - - const newLeftPoint = get_nearby_bin_right_point( - get_point_by_price(reverse_price(price_l_temp)) - ); - price_l = reverse_price(get_price_by_point(newLeftPoint)); - - setDragLeftPoint(newLeftPoint); - } - const x = scale(price_l) - dragBarWidth / 2; - d3.select(`${randomId} .drag-left`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentLeft`) - .attr( - 'transform', - `translate(${ - x - (percentBoxWidth - dragBarWidth / 2 + disFromPercentBoxToDragBar) - }, 0)` - ) - .select('text') - .text(`${diffPrices(price_l)}%`) - .attr('fill', 'white'); - const dragLeft = d3.drag().on('drag', function (e) { - const rightX = Number( - d3 - .select(`${randomId} .drag-right`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - if (rightX < e.x || e.x < dragBarWidth / 2) return; - let p = reverse_price(scale.invert(e.x)); - const newLeftPoint = get_nearby_bin_right_point(get_point_by_price(p)); - setDragLeftPoint(newLeftPoint); - setLeftPoint && setLeftPoint(newLeftPoint); - }); - d3.select(`${randomId} .drag-left`).call(dragLeft); - } - function draw_drag_right({ scale }: { scale: any }) { - // 初始化右的位置 - const price = get_current_price(); - let price_r; - if (dragRightPoint) { - price_r = reverse_price(get_price_by_point(dragRightPoint)); - } else { - const price_r_temp = Big(1 + defaultPercent / 100) - .mul(price) - .toFixed(); - - const newRightPoint = get_nearby_bin_left_point( - get_point_by_price(reverse_price(price_r_temp)) - ); - price_r = reverse_price(get_price_by_point(newRightPoint)); - setDragRightPoint(newRightPoint); - } - const x = scale(price_r); - d3.select(`${randomId} .drag-right`).attr( - 'transform', - `translate(${x}, -${axisHeight})` - ); - d3.select(`${randomId} .percentRight`) - .attr('transform', `translate(${x + disFromPercentBoxToDragBar + 2}, 0)`) - .select('text') - .text(`${diffPrices(price_r)}%`) - .attr('fill', 'white'); - const dragRight = d3.drag().on('drag', (e) => { - const leftX = Number( - d3 - .select(`${randomId} .drag-left`) - .attr('transform') - .split(',')[0] - .slice(10) - ); - const limitx = svgWidth - (svgPaddingX * 2 + dragBarWidth); - if (leftX > e.x - dragBarWidth / 2 || e.x > limitx) return; - const p = scale.invert(e.x); - const newRightPoint = get_nearby_bin_left_point( - get_point_by_price(reverse_price(p)) - ); - setDragRightPoint(newRightPoint); - setRightPoint && setRightPoint(newRightPoint); - }); - d3.select(`${randomId} .drag-right`).call(dragRight); - } - function draw_radius_mode_bar() { - const scale: any = scaleAxis(); - const { targetPoint } = config; - const price = get_price_by_point(targetPoint); - const x = scale(price); - d3.select(`${randomId} .radiusBar`) - .attr('transform', `translate(${x}, -${axisHeight})`) - .attr('style', 'display:block'); - } - function get_current_price(forward?: boolean) { - const current_price = get_price_by_point(pool.current_point); - if (forward) { - return current_price; - } else { - return reverse_price(current_price); - } - } - function get_point_by_price(price: string) { - const { point_delta, token_x_metadata, token_y_metadata } = pool; - const decimalRate_point = - Math.pow(10, token_y_metadata.decimals) / - Math.pow(10, token_x_metadata.decimals); - const point = getPointByPrice(point_delta, price, decimalRate_point); - return point; - } - function get_price_by_point(point: number) { - const { token_x_metadata, token_y_metadata } = pool; - const decimalRate_price = - Math.pow(10, token_x_metadata.decimals) / - Math.pow(10, token_y_metadata.decimals); - return getPriceByPoint(point, decimalRate_price); - } - function get_nearby_bin_right_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin + slots; - } - function get_nearby_bin_left_point(p: number) { - const { point_delta } = pool; - const slots = getConfig().bin * point_delta; - const point_int_bin = Math.round(p / slots) * slots; - if (point_int_bin < POINTLEFTRANGE) { - return POINTLEFTRANGE; - } else if (point_int_bin > POINTRIGHTRANGE) { - return 800000; - } - return point_int_bin; - } - function clickToLeft() { - const { bin } = getConfig(); - const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); - setDragLeftPoint(newPoint_nearby_bin); - setLeftPoint && setLeftPoint(newPoint_nearby_bin); - } - function clickToRight() { - const { bin } = getConfig(); - const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); - setDragRightPoint(newPoint_nearby_bin); - setRightPoint && setRightPoint(newPoint_nearby_bin); - } - function get_bin_point_by_point(point: number, type?: IRMTYPE) { - const { point_delta } = pool; - const slot_num = getConfig().bin; - return getBinPointByPoint(point_delta, slot_num, point, type); - } - function scaleAxis() { - if (chartType == 'USER') { - return scaleAxis_User(); - } else { - return scaleAxis_Pool(); - } - } - function scaleAxis_User() { - const { sortP } = get_price_and_liquidity_range(); - const range = [+sortP[0], +sortP[sortP.length - 1]]; - return d3 - .scaleLinear() - .domain(range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxis_Pool() { - return d3 - .scaleLinear() - .domain(price_range) - .range([0, svgWidth - svgPaddingX * 2]); - } - function scaleAxisY() { - if (chartType == 'USER') { - return scaleAxisY_User(); - } else { - return scaleAxisY_Pool(); - } - } - function scaleAxisY_Pool() { - const data: IChartData[] = process_back_end_data_in_range(); - const L: number[] = []; - data.forEach((o: IChartData) => { - const { liquidity, order_liquidity } = o; - L.push( - +liquidity, - +order_liquidity, - Big(liquidity).plus(order_liquidity).plus(min_bar_height).toNumber() - ); - }); - const sortL = sortBy(L); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight]); - } - function scaleAxisY_User() { - const { sortY: sortL } = get_price_and_liquidity_range(); - return d3 - .scaleLinear() - .domain([0, +sortL[sortL.length - 1]]) - .range([0, wholeBarHeight - whole_bars_background_padding]) - .clamp(true); - } - function get_bin_width() { - const slot_num_in_a_bin = getConfig().bin; - const { point_delta } = pool; - return point_delta * slot_num_in_a_bin; - } - function get_price_and_liquidity_range() { - const Y: number[] = []; - const X: number[] = []; - const list = process_back_end_data_in_range(); - list.forEach((o: IChartData) => { - const { liquidity, price_l, price_r } = o; - Y.push(+liquidity); - X.push(+price_l, +price_r); - }); - X.push(+get_current_price()); - const sortY = sortBy(Y); - const sortX = sortBy(X); - return { sortP: sortX, sortY }; - } - function diffPrices(newPrice: string, peferencePrice?: string) { - let movePercent; - const price = peferencePrice || get_current_price(); - if (+price > +newPrice) { - movePercent = -Big(1) - .minus(Big(newPrice).div(price)) - .mul(100) - .toFixed(0, 1); - } else { - movePercent = Big(newPrice).div(price).minus(1).mul(100).toFixed(0, 1); - } - return movePercent; - } - function getConfig(): IChartItemConfig { - const { bin, range, colors, rangeGear } = - get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const percent_final = custom_config[pool_id]?.range || range; - const bin_final = custom_config[pool_id]?.bin || bin; - const colors_final = custom_config[pool_id]?.colors || colors; - const rangeGear_final = custom_config[pool_id]?.rangeGear || rangeGear; - return { - bin: bin_final, - range: percent_final, - colors: colors_final, - rangeGear: rangeGear_final, - }; - } - function get_current_price_by_token_x() { - if (pool) { - return formatPriceWithCommas(get_current_price()); - } - return '-'; - } - function get_current_price_by_token_y() { - if (pool) { - return formatPriceWithCommas(reverse_price(get_current_price())); - } - return '-'; - } - function zoomOut() { - const { rangeGear } = getConfig(); - const targetPercent = rangeGear.find((item) => item < zoom); - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - function zoomIn() { - const { rangeGear } = getConfig(); - const index = rangeGear.findIndex((item) => item == zoom); - let targetPercent; - if (index !== 0) { - targetPercent = rangeGear[index - 1]; - } - if (targetPercent) { - const [new_left_price, new_right_price] = - get_price_range_by_percent(targetPercent); - set_price_range([+new_left_price, +new_right_price]); - setZoom(targetPercent); - } - } - const rangeGear = getConfig().rangeGear; - const is_in_max_zoom = zoom == rangeGear[rangeGear.length - 1]; - const is_in_min_zoom = zoom == rangeGear[0]; - return ( -
- {/* 控件按钮*/} -
- {/*
- -
*/} -
- -
-
- -
- {/*
- -
*/} -
- - - - - - - - {/* 横坐标轴 */} - - {/* 拖拽线 left */} - - - - - - - - - - - - - - {/* 拖拽线 right */} - - - - - - - - - - - - - - {/* 左右坐标轴中间的重叠区域 */} - - - - {/* radius 模式下 target price 对应的柱子 */} - - - - - - - - - {/* hover到柱子(bin)上的悬浮框 */} -
-
- APR(24h) - - {binDetail?.feeApr} - -
-
- Price - - {binDetail?.price_by_token_y} {pool?.token_y_metadata?.symbol} /{' '} - {binDetail?.price_by_token_x} {pool?.token_x_metadata?.symbol} - -
- {binDetail?.token_x_amount ? ( - <> -
- - {pool?.token_x_metadata?.symbol} Amount - - - {binDetail.token_x_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_x_amount_in_liquidity} - -
-
-
- - in Limit Orders -
- - {binDetail.token_x_amount_in_order} - -
- - ) : null} - {binDetail?.token_y_amount ? ( - <> -
- - {pool?.token_y_metadata?.symbol} Amount - - - {binDetail.token_y_amount} - -
-
-
- - in Liquidity -
- - {binDetail.token_y_amount_in_liquidity} - -
-
-
- - in Limit Orders -
- - {binDetail.token_y_amount_in_order} - -
- - ) : null} -
-
-
- Your Liquidity - - {user_liquidities_detail?.total_value || '-'} - -
-
- Price Range - - {user_liquidities_detail?.min_price} -{' '} - {user_liquidities_detail?.max_price} - -
-
- Position - - {user_liquidities_detail?.total_x_amount}{' '} - {pool?.token_x_metadata.symbol} +{' '} - {user_liquidities_detail?.total_y_amount}{' '} - {pool?.token_y_metadata.symbol} - -
-
- APR(24h) - - {user_liquidities_detail?.apr_24 || '-'} - -
-
- Total Earned Fee - - {user_liquidities_detail?.total_earned_fee || '-'} - -
-
- {/* current 价格 */} -
-
-
-
- - {pool?.token_y_metadata?.symbol} - - - - {pool?.token_x_metadata?.symbol} - -
-
- - {pool?.token_x_metadata?.symbol} - - - - {pool?.token_y_metadata?.symbol} - -
-
-
-
- ); -} -function isValid(n: number) { - if (n !== undefined && n !== null) return true; - return false; -} -function LeftArrowIcon(props: any) { - return ( - - - - ); -} -function RightArrowIcon(props: any) { - return ( - - - - ); -} -function AddIcon(props: any) { - return ( - - - - - ); -} -function SubIcon(props: any) { - return ( - - - - ); -} -function createRandomChar(c = 'a-z') { - switch (c) { - case 'A-Z': - return String.fromCharCode(Math.trunc(Math.random() * 25) + 65); - case 'a-z': - return String.fromCharCode(Math.trunc(Math.random() * 25) + 97); - case '0-9': - default: - return String.fromCharCode(Math.trunc(Math.random() * 10) + 48); - } -} -function createRandomString(length = 4) { - let str = ''; - for (let i = 0; i < length; i++) str += createRandomChar(); - return str; -} diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 68b74adb7..655954f44 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -698,6 +698,7 @@ export const RemovePoolV3 = (props: any) => { const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; const is_mobile = isMobile(); const cardWidth = is_mobile ? '100vw' : '550px'; + const cardHeight = is_mobile ? '70vh' : '95vh'; return ( { }} > {/* Title */} diff --git a/src/components/pool/YourLiquidityV2.tsx b/src/components/pool/YourLiquidityV2.tsx index 56cfd2e93..0826be358 100644 --- a/src/components/pool/YourLiquidityV2.tsx +++ b/src/components/pool/YourLiquidityV2.tsx @@ -77,6 +77,7 @@ import { formatWithCommas_usd_down, } from './utils'; import { FarmStampNew } from '~components/icon/FarmStamp'; +import { get_unClaimed_fee_data } from '../../pages/poolsV3/components/detail/DetailFun'; const is_mobile = isMobile(); const { REF_UNI_V3_SWAP_CONTRACT_ID } = getConfig(); const LiquidityContext = createContext(null); @@ -729,10 +730,15 @@ function UserLiquidityLineStyleGroup({ const { accountId } = useWalletSelector(); const history = useHistory(); useEffect(() => { - if (poolDetail && tokenPriceList && tokenMetadata_x_y) { + if ( + poolDetail && + tokenPriceList && + tokenMetadata_x_y && + liquidities_list.length + ) { get_24_apr(); } - }, [poolDetail, tokenPriceList, tokenMetadata_x_y]); + }, [poolDetail, tokenPriceList, tokenMetadata_x_y, liquidities_list]); useEffect(() => { if ( all_seeds.length && @@ -894,10 +900,21 @@ function UserLiquidityLineStyleGroup({ account_id: accountId, }); if (dcl_fee_result) { - // 24h 利润 中文 + // 24h profit poolDetail.token_x_metadata = tokenMetadata_x_y[0]; poolDetail.token_y_metadata = tokenMetadata_x_y[1]; - apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); + // total unClaimed fee + const [unClaimed_tvl_fee] = get_unClaimed_fee_data( + liquidities_list, + poolDetail, + tokenPriceList + ); + apr_24 = get_account_24_apr( + unClaimed_tvl_fee, + dcl_fee_result, + poolDetail, + tokenPriceList + ); } setAccountAPR(formatPercentage(apr_24)); } diff --git a/src/components/portfolio/Orders.tsx b/src/components/portfolio/Orders.tsx index c1a633757..0b3ee113b 100644 --- a/src/components/portfolio/Orders.tsx +++ b/src/components/portfolio/Orders.tsx @@ -1107,7 +1107,8 @@ function OrderCard({ > { - openUrl('/myOrder'); + localStorage.setItem(SWAP_MODE_KEY, SWAP_MODE.LIMIT); + openUrl('/'); }} className="flex items-center justify-center text-xs text-v3SwapGray relative -top-3 " > diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx deleted file mode 100644 index 72337fe51..000000000 --- a/src/pages/poolsV3/AddYourLiquidityPageV3Copy.tsx +++ /dev/null @@ -1,6159 +0,0 @@ -import React, { - useState, - useContext, - useEffect, - useRef, - createContext, -} from 'react'; - -import { - ReturnIcon, - AddIcon, - SelectIcon, - BgIcon, - SwitchButton, - AddButton, - ReduceButton, - SwitchIcon, - BoxDarkBg, - SideIcon, - InvalidIcon, - WarningIcon, - EmptyIcon, - WarningMark, - SwitchLRButton, - SwitchArrowL, - SwitchArrowR, -} from '~components/icon/V3'; -import { FormattedMessage, useIntl } from 'react-intl'; -import { useHistory } from 'react-router-dom'; -import { - GradientButton, - ButtonTextWrapper, - ConnectToNearBtn, -} from '~components/button/Button'; -import SelectToken from '~components/forms/SelectToken'; -import { useTriTokens, useWhitelistTokens } from '../../state/token'; -import { useTriTokenIdsOnRef } from '../../services/aurora/aurora'; -import { - TokenMetadata, - ftGetBalance, - ftGetTokenMetadata, -} from '../../services/ft-contract'; -import { getTokenPriceList } from '../../services/indexer'; -import { - getBoostTokenPrices, - Seed, - FarmBoost, - classificationOfCoins, -} from '../../services/farm'; -import { useTokenBalances, useDepositableBalance } from '../../state/token'; -import Loading from '~components/layout/Loading'; -import { - list_pools, - add_liquidity, - create_pool, - PoolInfo, - get_pool_marketdepth, - regularizedPoint, - batch_add_liquidity, -} from '../../services/swapV3'; -import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; -import { - getPriceByPoint, - getPointByPrice, - CONSTANT_D, - FEELIST, - POINTDELTAMAP, - DEFAULTSELECTEDFEE, - POINTLEFTRANGE, - POINTRIGHTRANGE, - get_matched_seeds_for_dcl_pool, - get_all_seeds, - get_pool_id, - get_pool_name, - openUrl, - getBinPointByPrice, - getBinPointByPoint, - get_l_amount_by_condition, - UserLiquidityInfo, - reverse_price, - sort_tokens_by_base, -} from '../../services/commonV3'; -import { - formatWithCommas, - toPrecision, - toReadableNumber, - toNonDivisibleNumber, - checkAllocations, - scientificNotationToString, - getAllocationsLeastOne, - toInternationalCurrencySystem, - ONLY_ZEROS, -} from '~utils/numbers'; -import { WalletContext } from '../../utils/wallets-integration'; -import _, { forEach, set } from 'lodash'; -import BigNumber from 'bignumber.js'; -import { toRealSymbol } from '../../utils/token'; -import ReactTooltip from 'react-tooltip'; -import { getURLInfo } from '../../components/layout/transactionTipPopUp'; -import { BlueCircleLoading } from '../../components/layout/Loading'; -import { isMobile } from '../../utils/device'; -import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; -import ReactSlider from 'react-slider'; -import Big from 'big.js'; -import { SelectTokenDCL } from '../../components/forms/SelectToken'; -import { SliderCurColor } from '~components/icon/Info'; -import { - CurveShape, - SpotShape, - BidAskShape, -} from '../Orderly/components/Common/Icons'; -import DclChart from '../../components/d3Chart/DclChart'; -import { IChartData } from '../../components/d3Chart/interfaces'; -import { - IAddLiquidityInfo, - IAddLiquidityInfoHelp, - LiquidityShape, - PriceRangeModeType, -} from './interfaces'; -import { - get_custom_config_for_chart, - get_default_config_for_chart, - RADIUS_DEFAULT_NUMBER, - max_nft_divisional_per_side, -} from '../../components/d3Chart/config'; -import { - IChartItemConfig, - IChartConfig, -} from '../../components/d3Chart/interfaces'; -import { isInvalid } from '../../components/d3Chart/utils'; -const LiquidityProviderData = createContext(null); -const AddLiquidityProviderData = createContext(null); -const SLIDER_BIN_NUMBER = 20; -export default function AddYourLiquidityPageV3() { - // return - return ; -} - -function AddYourLiquidityPageV3Forward() { - const [tokenX, setTokenX] = useState(null); - const [tokenY, setTokenY] = useState(null); - const [tokenXAmount, setTokenXAmount] = useState(''); - const [tokenYAmount, setTokenYAmount] = useState(''); - const [leftPoint, setLeftPoint] = useState(); - const [rightPoint, setRightPoint] = useState(); - const [currentPoint, setCurrentPoint] = useState(); - const [onlyAddYToken, setOnlyAddYToken] = useState(false); - const [onlyAddXToken, setOnlyAddXToken] = useState(false); - const [invalidRange, setInvalidRange] = useState(false); - const [currentSelectedPool, setCurrentSelectedPool] = - useState(null); - const [listPool, setListPool] = useState([]); - const [tokenPriceList, setTokenPriceList] = useState>({}); - const [currentPools, setCurrentPools] = - useState>(null); - const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); - const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - - const [feeBoxStatus, setFeeBoxStatus] = useState(true); - const [buttonSort, setButtonSort] = useState(false); - const [selectHover, setSelectHover] = useState(false); - const [hoverFeeBox, setHoverFeeBox] = useState(false); - - // abandon - const [seed_list, set_seed_list] = useState(); - const [related_seeds, set_related_seeds] = useState([]); - - // new - const [binNumber, setBinNumber] = useState(); - const [liquidityShape, setLiquidityShape] = useState('Spot'); - const [topPairs, setTopPairs] = useState([]); - const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); - const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); - const [token_amount_tip, set_token_amount_tip] = - useState(); - const [only_suppport_spot_shape, set_only_suppport_spot_shape] = - useState(false); - const [switch_pool_loading, set_switch_pool_loading] = - useState(true); - const [new_user_liquidities, set_new_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - - const history = useHistory(); - const triTokenIds = useTriTokenIdsOnRef(); - const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); - const triTokens = useTriTokens(); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const nearBalance = useDepositableBalance('NEAR'); - const intl = useIntl(); - const OPEN_CREATE_POOL_ENTRY = false; - - useEffect(() => { - getBoostTokenPrices().then(setTokenPriceList); - get_list_pools(); - get_seeds(); - }, []); - useEffect(() => { - if (tokenX) { - const tokenXId = tokenX.id; - if (tokenXId) { - if (isSignedIn) { - ftGetBalance(tokenXId).then((available: string) => - setTokenXBalanceFromNear( - toReadableNumber( - tokenX.decimals, - tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - if (tokenY) { - const tokenYId = tokenY.id; - if (tokenYId) { - if (isSignedIn) { - ftGetBalance(tokenYId).then((available: string) => - setTokenYBalanceFromNear( - toReadableNumber( - tokenY.decimals, - tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - }, [tokenX, tokenY, isSignedIn, nearBalance]); - useEffect(() => { - if (listPool.length > 0) { - get_init_pool(); - } - }, [listPool]); - useEffect(() => { - if (currentSelectedPool && tokenX && tokenY) { - const { fee } = currentSelectedPool; - const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); - history.replace(`#${link}`); - if (seed_list && currentSelectedPool.pool_id) { - get_optional_seeds(); - } - } - }, [currentSelectedPool, tokenX, tokenY, seed_list]); - useEffect(() => { - if (tokenX && tokenY) { - searchPools(); - } - }, [tokenX, tokenY, tokenPriceList, listPool]); - useEffect(() => { - if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { - getTopPairs(); - } - }, [listPool, tokenPriceList]); - // new - useEffect(() => { - // init - if (currentSelectedPool?.pool_id) { - const { current_point, point_delta } = currentSelectedPool; - const n = get_slot_number_in_a_bin(); - const bin_width = n * point_delta; - SET_SLOT_NUMBER(n); - SET_BIN_WIDTH(bin_width); - setCurrentPoint(current_point); - set_switch_pool_loading(false); - } - }, [currentSelectedPool]); - // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 - useEffect(() => { - set_only_suppport_spot_shape(false); - if (currentSelectedPool) { - const { point_delta } = currentSelectedPool; - if (leftPoint <= currentPoint && rightPoint > currentPoint) { - // inrange - const binWidth = SLOT_NUMBER * point_delta; - const binNumber = (rightPoint - leftPoint) / binWidth; - if (binNumber == 1) { - setLiquidityShape('Spot'); - set_only_suppport_spot_shape(true); - if (tokenXAmount) { - changeTokenXAmount(tokenXAmount); - } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount); - } - } - } - } - }, [ - leftPoint, - rightPoint, - currentPoint, - currentSelectedPool, - liquidityShape, - ]); - useEffect(() => { - set_token_amount_tip(null); - }, [tokenXAmount, tokenYAmount, currentSelectedPool]); - async function getTopPairs() { - const listPromise = listPool.map(async (p: PoolInfo) => { - const token_x = p.token_x; - const token_y = p.token_y; - - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged) - .toFixed(); - const tvlx = - Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * - Number(pricey); - - p.tvl = tvlx + tvly; - - return p; - }); - const list: PoolInfo[] = await Promise.all(listPromise); - list.sort((b: PoolInfo, a: PoolInfo) => { - return a.tvl - b.tvl; - }); - const top3 = list.slice(0, 3); - setTopPairs(top3); - } - if (!refTokens || !triTokens || !triTokenIds) return ; - async function get_seeds() { - const seeds = await get_all_seeds(); - set_seed_list(seeds); - } - function get_optional_seeds() { - const optional_seeds = get_matched_seeds_for_dcl_pool({ - seeds: seed_list, - pool_id: currentSelectedPool.pool_id, - }); - if (optional_seeds.length) { - set_related_seeds(optional_seeds); - } else { - set_related_seeds([]); - } - } - async function get_init_pool() { - let tokenx_id, tokeny_id, pool_fee; - const hash = decodeURIComponent(location.hash); - if (hash.indexOf('<>') > -1) { - // new link - [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); - } else { - // old link - [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); - } - if (tokenx_id && tokeny_id && pool_fee) { - const tokenx = await ftGetTokenMetadata(tokenx_id); - const tokeny = await ftGetTokenMetadata(tokeny_id); - setTokenX(tokenx); - setTokenY(tokeny); - } - } - function goYourLiquidityPage() { - if (history.length == 2) { - history.push('/yourliquidity'); - } else { - history.goBack(); - } - } - async function get_list_pools() { - const list: PoolInfo[] = await list_pools(); - if (list.length > 0) { - setListPool(list); - } - } - function searchPools() { - const hash = decodeURIComponent(location.hash); - let url_fee; - if (hash.indexOf('<>') > -1) { - url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; - } else { - url_fee = +hash.slice(1).split('|')[2]; - } - const currentPoolsMap = {}; - if (listPool.length > 0 && tokenX && tokenY) { - set_switch_pool_loading(true); - const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { - // TODO 增加pool 状态的判断 - const { token_x, token_y, state } = pool; - if ( - (token_x == tokenX.id && token_y == tokenY.id) || - (token_x == tokenY.id && token_y == tokenX.id) - ) - return true; - }); - if (availablePools.length > 0) { - /*** percent start */ - const tvlList: number[] = []; - availablePools.map((p: PoolInfo) => { - const { - total_x, - total_y, - token_x, - token_y, - total_fee_x_charged, - total_fee_y_charged, - } = p; - const firstToken = tokenX.id == token_x ? tokenX : tokenY; - const secondToken = tokenY.id == token_y ? tokenY : tokenX; - const firstTokenPrice = - (tokenPriceList && - tokenPriceList[firstToken.id] && - tokenPriceList[firstToken.id].price) || - '0'; - const secondTokenPrice = - (tokenPriceList && - tokenPriceList[secondToken.id] && - tokenPriceList[secondToken.id].price) || - '0'; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged || 0) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged || 0) - .toFixed(); - const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) - .times(firstTokenPrice) - .toNumber(); - const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) - .times(secondTokenPrice) - .toNumber(); - const totalTvl = tvlx + tvly; - p.tvl = totalTvl; - tvlList.push(totalTvl); - return p; - }); - const sumOfTvlList = _.sum(tvlList); - const tvlPercents = - sumOfTvlList === 0 - ? ['0', '0', '0', '0'] - : availablePools.map((p: PoolInfo) => - scientificNotationToString( - ((p.tvl / sumOfTvlList) * 100).toString() - ) - ); - const nonZeroIndexes: number[] = []; - tvlPercents.forEach((p, index) => { - if (Number(p) > 0) { - nonZeroIndexes.push(index); - } - }); - const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); - const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); - const finalPercents = tvlPercents.map((p, index) => { - if (nonZeroIndexes.includes(index)) { - const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; - return newP; - } - return p; - }); - const maxPercent = _.max(finalPercents); - let maxPercentPool; - availablePools.forEach((pool: PoolInfo, index) => { - const f = pool.fee; - const temp: PoolInfo = { - ...pool, - percent: finalPercents[index], - tvl: tvlList[index], - }; - currentPoolsMap[f] = temp; - if (finalPercents[index] == maxPercent) { - maxPercentPool = temp; - } - }); - // url-fee-pool - const urlFeePool = url_fee - ? currentPoolsMap[url_fee] || { fee: url_fee } - : null; - setCurrentPools(currentPoolsMap); - setCurrentSelectedPool(urlFeePool || maxPercentPool); - } else { - setCurrentPools({}); - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } else { - setCurrentPools({}); - if (tokenX && tokenY) { - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } - } - function switchSelectedFee(fee: number) { - if (tokenX && tokenY && currentPools) { - const pool = currentPools[fee]; - setCurrentSelectedPool(pool || { fee }); - if (!pool) { - setOnlyAddXToken(false); - setOnlyAddYToken(false); - setInvalidRange(false); - } - } - } - function changeTokenXAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenXAmount(amount); - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenYAmount(amount_result); - } - } - function changeTokenYAmount(amount: string = '0') { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setTokenYAmount(amount); - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - function getTokenYAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - // X-->L - const L = - +amount * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); - // L-->current Y - const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); - // L--> Y - const Y = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1)); - const decimalsRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - - const Y_result = (Y + Yc) * decimalsRate; - return Y_result.toString(); - } - } - function getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - let L; - // Yc-->L - if (leftPoint == currentPoint) { - L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); - } else { - // Y-->L - L = - +amount * - ((Math.sqrt(CONSTANT_D) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); - } - const X = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); - const decimalsRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const X_result = X * decimalsRate; - return X_result.toString(); - } - } - function pointChange({ - leftPoint, - rightPoint, - currentPoint, - }: { - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const sort = tokenX.id == token_x; - setInvalidRange(false); - setOnlyAddXToken(false); - setOnlyAddYToken(false); - // invalid point - if (leftPoint >= rightPoint) { - setInvalidRange(true); - setTokenXAmount(''); - setTokenYAmount(''); - return; - } - // can only add x token - if (leftPoint > currentPoint) { - setOnlyAddXToken(true); - if (sort) { - setTokenYAmount(''); - } else { - setTokenXAmount(''); - } - return; - } - // can only add y token - if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { - setOnlyAddYToken(true); - if (sort) { - setTokenXAmount(''); - } else { - setTokenYAmount(''); - } - return; - } - - if (liquidityShape !== 'Spot') return; - if (sort) { - if (tokenXAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } else { - if (tokenXAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } else if (tokenYAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - } - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; - } - } - // start - function get_slot_number_in_a_bin() { - const pool_id = currentSelectedPool?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - /** - * step1 当数据发生改变 - * leftPoint, rightPoint 有效 - * tokenXAmount, tokenYAmount 至少有一个有值 - * ===> 可以触发 - * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 - * step3 把新增的liquidity传递给Chart组件, - * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 - * step5 疑问;?实时修改图表 会导致效率什么问题吗? - */ - function generate_new_user_chart() { - if ( - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - (+tokenXAmount > 0 || +tokenYAmount > 0) - ) { - let new_nfts: any; - if (liquidityShape == 'Spot') { - const new_nft = getLiquiditySpot(); - new_nfts = [new_nft]; - } else { - new_nfts = getLiquidityForCurveAndBidAskMode(); - if (!new_nfts) return; - } - const processed_new_nfts = process_liquidities(new_nfts); - set_new_user_liquidities(processed_new_nfts); - } else { - set_new_user_liquidities([]); - } - } - function getLiquiditySpot() { - const { pool_id } = currentSelectedPool; - return { - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - token_x: tokenX, - token_y: tokenY, - }; - } - function formatWithCommas_for_tip(v: string, commas?: boolean) { - const v_big = Big(v || 0); - let v_temp; - if (v_big.lt(0.001)) { - v_temp = v_big.toFixed(6, 3); - } else { - if (commas) { - v_temp = formatWithCommas(v_big.toFixed(3, 3)); - } else { - v_temp = v_big.toFixed(3, 3); - } - } - return v_temp; - } - function getLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_x_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 - * step2 分配好后,获得对右侧的最小token数量要求 - * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 - */ - const { point_delta, current_point } = currentSelectedPool; - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'floor' - ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'ceil' - ); - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(null); - if (liquidityShape == 'Curve') { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_curve({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 1 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_decline_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_curve({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 2 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } else { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_bid_ask({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 3 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_bid_ask({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 4 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } - } - return nftList; - } - /** - * curve 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == exclude_cur_total_nft_number) { - amount_x = dis - .mul(exclude_cur_total_nft_number + 1) - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 1).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == exclude_cur_total_nft_number) { - amount_y = dis - .mul(exclude_cur_total_nft_number + 1) - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 2).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == 0) { - amount_y = dis - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - // 不同点1 - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - // 不同点2 - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 2).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == 0) { - amount_x = dis - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 和 bid ask 上升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_rise_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - const left_i_point = left_point + nftWidth * i; - let right_i_point; - if (i == total_nft_number - 1) { - right_i_point = right_point; - } else { - right_i_point = left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - /** - * curve 和 bid ask 下降升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_decline_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐下降模式 - * nft 从右往左计算 - * e.g. - * 公式推导: - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - let left_i_point; - let right_i_point; - if (i == total_nft_number - 1) { - left_i_point = left_point; - } else { - left_i_point = right_point - nftWidth * (i + 1); - } - right_i_point = right_point - nftWidth * i; - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - function formula_of_token_x(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ); - } - function formula_of_token_y(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - } - /** - * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 - */ - function process_liquidities(liquidities: IAddLiquidityInfo[]) { - const { pool_id } = currentSelectedPool; - const new_liquidities: UserLiquidityInfo[] = []; - liquidities.forEach((l: IAddLiquidityInfo) => { - const { left_point, right_point, amount_x, amount_y } = l; - const L = get_l_amount_by_condition({ - left_point, - right_point, - token_x_amount: amount_x, - token_y_amount: amount_y, - poolDetail: currentSelectedPool, - }); - new_liquidities.push({ - pool_id, - left_point, - right_point, - amount: L, - }); - }); - return new_liquidities; - } - const tokenSort = tokenX?.id == currentSelectedPool?.token_x; - const mobileDevice = isMobile(); - - return ( - -
- {/* 缩略图 */} - {/* */} - {/* 详情页图 */} - {/* */} - {/* 添加页图 */} - {/* */} - {/* 用户流动性图表*/} - {/* */} - {/* 删除流动性图表 从右侧部分删除 */} - {/* */} - {/* 删除流动性图表 从左侧部分删除 */} - {/* */} - {/* 删除流动性图表 全部删除 */} - {/* */} -
- - {/* mobile head */} -
-
- -
- - - -
-
- {/* pc head */} -
{ - history.goBack(); - }} - > -
- -
- - - -
- - {/* content */} -
-
- {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} - {/* right area */} -
-
-
- -
- - { - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
- - - - {token_amount_tip ? ( -
- - {token_amount_tip} -
- ) : null} - -
-
- -
- -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - - -
{ - setHoverFeeBox(false); - }} - onMouseEnter={() => { - setHoverFeeBox(true); - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
- -
- -
- -
- {[SpotShape, CurveShape, BidAskShape].map( - (Shape, index: number) => { - let disabled = false; - if ( - (index == 1 || index == 2) && - only_suppport_spot_shape - ) { - disabled = true; - } - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - if (index === 0) setLiquidityShape('Spot'); - else if (index === 1 && !only_suppport_spot_shape) - setLiquidityShape('Curve'); - else if (index == 2 && !only_suppport_spot_shape) - setLiquidityShape('BidAsk'); - }} - > - - - - {index === 0 && ( - - )} - {index === 1 && ( - - )} - - {index === 2 && ( - - )} - -
- ); - } - )} -
- {/* new user chart part */} - {isSignedIn && currentSelectedPool ? ( -
-
-
- -
-
- Generate -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- ) : null} - - {currentSelectedPool && currentSelectedPool.pool_id && ( - - )} -
- - -
-
-
-
-
-
- ); -} -function AddYourLiquidityPageV3Reverse() { - const [tokenX, setTokenX] = useState(null); - const [tokenY, setTokenY] = useState(null); - const [tokenXAmount, setTokenXAmount] = useState(''); - const [tokenYAmount, setTokenYAmount] = useState(''); - const [leftPoint, setLeftPoint] = useState(); - const [rightPoint, setRightPoint] = useState(); - const [currentPoint, setCurrentPoint] = useState(); - const [onlyAddYToken, setOnlyAddYToken] = useState(false); - const [onlyAddXToken, setOnlyAddXToken] = useState(false); - const [invalidRange, setInvalidRange] = useState(false); - const [currentSelectedPool, setCurrentSelectedPool] = - useState(null); - - const [listPool, setListPool] = useState([]); - const [tokenPriceList, setTokenPriceList] = useState>({}); - const [currentPools, setCurrentPools] = - useState>(null); - const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); - const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - - const [feeBoxStatus, setFeeBoxStatus] = useState(true); - const [buttonSort, setButtonSort] = useState(false); - const [selectHover, setSelectHover] = useState(false); - const [hoverFeeBox, setHoverFeeBox] = useState(false); - - // abandon - const [seed_list, set_seed_list] = useState(); - const [related_seeds, set_related_seeds] = useState([]); - - // new - const [binNumber, setBinNumber] = useState(); - const [liquidityShape, setLiquidityShape] = useState('Spot'); - const [topPairs, setTopPairs] = useState([]); - const [SLOT_NUMBER, SET_SLOT_NUMBER] = useState(); - const [BIN_WIDTH, SET_BIN_WIDTH] = useState(); - const [token_amount_tip, set_token_amount_tip] = - useState(); - const [only_suppport_spot_shape, set_only_suppport_spot_shape] = - useState(false); - const [switch_pool_loading, set_switch_pool_loading] = - useState(true); - const [new_user_liquidities, set_new_user_liquidities] = useState< - UserLiquidityInfo[] - >([]); - - const history = useHistory(); - const triTokenIds = useTriTokenIdsOnRef(); - const refTokens = useWhitelistTokens((triTokenIds || []).concat(['aurora'])); - const triTokens = useTriTokens(); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const nearBalance = useDepositableBalance('NEAR'); - const intl = useIntl(); - const OPEN_CREATE_POOL_ENTRY = false; - - useEffect(() => { - getBoostTokenPrices().then(setTokenPriceList); - get_list_pools(); - get_seeds(); - }, []); - useEffect(() => { - if (tokenX) { - const tokenXId = tokenX.id; - if (tokenXId) { - if (isSignedIn) { - ftGetBalance(tokenXId).then((available: string) => - setTokenXBalanceFromNear( - toReadableNumber( - tokenX.decimals, - tokenX.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - if (tokenY) { - const tokenYId = tokenY.id; - if (tokenYId) { - if (isSignedIn) { - ftGetBalance(tokenYId).then((available: string) => - setTokenYBalanceFromNear( - toReadableNumber( - tokenY.decimals, - tokenY.id === WRAP_NEAR_CONTRACT_ID ? nearBalance : available - ) - ) - ); - } - } - } - }, [tokenX, tokenY, isSignedIn, nearBalance]); - useEffect(() => { - if (listPool.length > 0) { - get_init_pool(); - } - }, [listPool]); - useEffect(() => { - if (currentSelectedPool && tokenX && tokenY) { - const { fee } = currentSelectedPool; - const link = get_pool_name(`${tokenX.id}|${tokenY.id}|${fee}`); - history.replace(`#${link}`); - if (seed_list && currentSelectedPool.pool_id) { - get_optional_seeds(); - } - } - }, [currentSelectedPool, tokenX, tokenY, seed_list]); - useEffect(() => { - if (tokenX && tokenY) { - searchPools(); - } - }, [tokenX, tokenY, tokenPriceList, listPool]); - useEffect(() => { - if (listPool.length > 0 && Object.keys(tokenPriceList).length > 0) { - getTopPairs(); - } - }, [listPool, tokenPriceList]); - // new - useEffect(() => { - // init - if (currentSelectedPool?.pool_id) { - console.log('come in currentSelectedPool', currentSelectedPool); - const { current_point, point_delta } = currentSelectedPool; - const n = get_slot_number_in_a_bin(); - const bin_width = n * point_delta; - SET_SLOT_NUMBER(n); - SET_BIN_WIDTH(bin_width); - setCurrentPoint(current_point); - set_switch_pool_loading(false); - } - }, [currentSelectedPool]); - // 中文 如果只有一个 bin 且 双边 则只允许设置成spot模式 - useEffect(() => { - set_only_suppport_spot_shape(false); - if (currentSelectedPool) { - const { point_delta } = currentSelectedPool; - if (leftPoint <= currentPoint && rightPoint > currentPoint) { - // inrange - const binWidth = SLOT_NUMBER * point_delta; - const binNumber = (rightPoint - leftPoint) / binWidth; - if (binNumber == 1) { - setLiquidityShape('Spot'); - set_only_suppport_spot_shape(true); - if (tokenXAmount) { - changeTokenXAmount(tokenXAmount); - } else if (tokenYAmount) { - changeTokenYAmount(tokenYAmount); - } - } - } - } - }, [ - leftPoint, - rightPoint, - currentPoint, - currentSelectedPool, - liquidityShape, - ]); - useEffect(() => { - set_token_amount_tip(null); - }, [tokenXAmount, tokenYAmount, currentSelectedPool]); - async function getTopPairs() { - const listPromise = listPool.map(async (p: PoolInfo) => { - const token_x = p.token_x; - const token_y = p.token_y; - - p.token_x_metadata = await ftGetTokenMetadata(token_x); - p.token_y_metadata = await ftGetTokenMetadata(token_y); - const pricex = tokenPriceList[token_x]?.price || 0; - const pricey = tokenPriceList[token_y]?.price || 0; - const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = p; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged) - .toFixed(); - const tvlx = - Number(toReadableNumber(p.token_x_metadata.decimals, totalX)) * - Number(pricex); - const tvly = - Number(toReadableNumber(p.token_y_metadata.decimals, totalY)) * - Number(pricey); - - p.tvl = tvlx + tvly; - - return p; - }); - const list: PoolInfo[] = await Promise.all(listPromise); - list.sort((b: PoolInfo, a: PoolInfo) => { - return a.tvl - b.tvl; - }); - const top3 = list.slice(0, 3); - setTopPairs(top3); - } - if (!refTokens || !triTokens || !triTokenIds) return ; - async function get_seeds() { - const seeds = await get_all_seeds(); - set_seed_list(seeds); - } - function get_optional_seeds() { - const optional_seeds = get_matched_seeds_for_dcl_pool({ - seeds: seed_list, - pool_id: currentSelectedPool.pool_id, - }); - if (optional_seeds.length) { - set_related_seeds(optional_seeds); - } else { - set_related_seeds([]); - } - } - async function get_init_pool() { - let tokenx_id, tokeny_id, pool_fee; - const hash = decodeURIComponent(location.hash); - if (hash.indexOf('<>') > -1) { - // new link - [tokenx_id, tokeny_id, pool_fee] = get_pool_id(hash.slice(1)).split('|'); - } else { - // old link - [tokenx_id, tokeny_id, pool_fee] = hash.slice(1).split('|'); - } - if (tokenx_id && tokeny_id && pool_fee) { - const tokenx = await ftGetTokenMetadata(tokenx_id); - const tokeny = await ftGetTokenMetadata(tokeny_id); - setTokenX(tokenx); - setTokenY(tokeny); - } - } - function goYourLiquidityPage() { - if (history.length == 2) { - history.push('/yourliquidity'); - } else { - history.goBack(); - } - } - async function get_list_pools() { - const list: PoolInfo[] = await list_pools(); - if (list.length > 0) { - setListPool(list); - } - } - function searchPools() { - const hash = decodeURIComponent(location.hash); - let url_fee; - if (hash.indexOf('<>') > -1) { - url_fee = +get_pool_id(hash.slice(1)).split('|')[2]; - } else { - url_fee = +hash.slice(1).split('|')[2]; - } - const currentPoolsMap = {}; - if (listPool.length > 0 && tokenX && tokenY) { - set_switch_pool_loading(true); - const availablePools: PoolInfo[] = listPool.filter((pool: PoolInfo) => { - // TODO 增加pool 状态的判断 - const { token_x, token_y, state } = pool; - if ( - (token_x == tokenX.id && token_y == tokenY.id) || - (token_x == tokenY.id && token_y == tokenX.id) - ) - return true; - }); - if (availablePools.length > 0) { - /*** percent start */ - const tvlList: number[] = []; - availablePools.map((p: PoolInfo) => { - const { - total_x, - total_y, - token_x, - token_y, - total_fee_x_charged, - total_fee_y_charged, - } = p; - const firstToken = tokenX.id == token_x ? tokenX : tokenY; - const secondToken = tokenY.id == token_y ? tokenY : tokenX; - const firstTokenPrice = - (tokenPriceList && - tokenPriceList[firstToken.id] && - tokenPriceList[firstToken.id].price) || - '0'; - const secondTokenPrice = - (tokenPriceList && - tokenPriceList[secondToken.id] && - tokenPriceList[secondToken.id].price) || - '0'; - const totalX = new BigNumber(total_x) - .minus(total_fee_x_charged || 0) - .toFixed(); - const totalY = new BigNumber(total_y) - .minus(total_fee_y_charged || 0) - .toFixed(); - const tvlx = new Big(toReadableNumber(firstToken.decimals, totalX)) - .times(firstTokenPrice) - .toNumber(); - const tvly = new Big(toReadableNumber(secondToken.decimals, totalY)) - .times(secondTokenPrice) - .toNumber(); - const totalTvl = tvlx + tvly; - p.tvl = totalTvl; - tvlList.push(totalTvl); - return p; - }); - const sumOfTvlList = _.sum(tvlList); - const tvlPercents = - sumOfTvlList === 0 - ? ['0', '0', '0', '0'] - : availablePools.map((p: PoolInfo) => - scientificNotationToString( - ((p.tvl / sumOfTvlList) * 100).toString() - ) - ); - const nonZeroIndexes: number[] = []; - tvlPercents.forEach((p, index) => { - if (Number(p) > 0) { - nonZeroIndexes.push(index); - } - }); - const nonZeroPercents = tvlPercents.filter((r) => Number(r) > 0); - const checkedNonZero = getAllocationsLeastOne(nonZeroPercents); - const finalPercents = tvlPercents.map((p, index) => { - if (nonZeroIndexes.includes(index)) { - const newP = checkedNonZero[nonZeroIndexes.indexOf(index)]; - return newP; - } - return p; - }); - const maxPercent = _.max(finalPercents); - let maxPercentPool; - availablePools.forEach((pool: PoolInfo, index) => { - const f = pool.fee; - const temp: PoolInfo = { - ...pool, - percent: finalPercents[index], - tvl: tvlList[index], - }; - currentPoolsMap[f] = temp; - if (finalPercents[index] == maxPercent) { - maxPercentPool = temp; - } - }); - // url-fee-pool - const urlFeePool = url_fee - ? currentPoolsMap[url_fee] || { fee: url_fee } - : null; - setCurrentPools(currentPoolsMap); - setCurrentSelectedPool(urlFeePool || maxPercentPool); - } else { - setCurrentPools({}); - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } else { - setCurrentPools({}); - if (tokenX && tokenY) { - setCurrentSelectedPool({ fee: url_fee || DEFAULTSELECTEDFEE }); - } - } - } - function switchSelectedFee(fee: number) { - if (tokenX && tokenY && currentPools) { - const pool = currentPools[fee]; - setCurrentSelectedPool(pool || { fee }); - if (!pool) { - setOnlyAddXToken(false); - setOnlyAddYToken(false); - setInvalidRange(false); - } - } - } - function changeTokenXAmount(amount: string = '0') { - setTokenXAmount(amount); - if (!onlyAddXToken && liquidityShape === 'Spot') { - const amount_result = getTokenYAmountByCondition({ - amount, - leftPoint: leftPoint, - rightPoint: rightPoint, - currentPoint: currentPoint, - }); - setTokenYAmount(amount_result); - } - } - function changeTokenYAmount(amount: string = '0') { - setTokenYAmount(amount); - if (!onlyAddYToken && liquidityShape === 'Spot') { - const amount_result = getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } - } - function getTokenYAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - // X-->L - const L = - +amount * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1)); - // L-->current Y - const Yc = L * Math.pow(Math.sqrt(CONSTANT_D), currentPoint); - // L--> Y - const Y = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1)); - const decimalsRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - - const Y_result = (Y + Yc) * decimalsRate; - return Y_result.toString(); - } - } - function getTokenXAmountByCondition({ - amount, - leftPoint, - rightPoint, - currentPoint, - }: { - amount: string; - leftPoint: number; - rightPoint: number; - currentPoint: number; - }) { - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - if (+amount == 0) { - return ''; - } else { - let L; - // Yc-->L - if (leftPoint == currentPoint) { - L = +amount * (1 / Math.pow(Math.sqrt(CONSTANT_D), currentPoint)); - } else { - // Y-->L - L = - +amount * - ((Math.sqrt(CONSTANT_D) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), currentPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint))); - } - const X = - L * - ((Math.pow(Math.sqrt(CONSTANT_D), rightPoint - currentPoint - 1) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1))); - const decimalsRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const X_result = X * decimalsRate; - return X_result.toString(); - } - } - function pointChange({ - leftPoint, - rightPoint, - }: { - leftPoint: number; - rightPoint: number; - }) { - const sort = true; - setInvalidRange(false); - setOnlyAddXToken(false); - setOnlyAddYToken(false); - setLeftPoint(leftPoint); - setRightPoint(rightPoint); - // invalid point - if (leftPoint >= rightPoint) { - setInvalidRange(true); - setTokenXAmount(''); - setTokenYAmount(''); - return; - } - // can only add x token - if (leftPoint > currentPoint) { - setOnlyAddXToken(true); - setTokenYAmount(''); - return; - } - // can only add y token - if (rightPoint <= currentPoint || currentPoint == rightPoint - 1) { - setOnlyAddYToken(true); - setTokenXAmount(''); - return; - } - if (liquidityShape !== 'Spot') return; - if (tokenYAmount) { - const amount_result = getTokenXAmountByCondition({ - amount: tokenYAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenXAmount(amount_result); - } else if (tokenXAmount) { - const amount_result = getTokenYAmountByCondition({ - amount: tokenXAmount, - leftPoint, - rightPoint, - currentPoint, - }); - setTokenYAmount(amount_result); - } - } - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; - } - } - // start - function get_slot_number_in_a_bin() { - const pool_id = currentSelectedPool?.pool_id; - const { bin } = get_default_config_for_chart() as IChartItemConfig; - const custom_config: IChartConfig = get_custom_config_for_chart(); - const slots = custom_config[pool_id]?.bin || bin; - return slots; - } - /** - * step1 当数据发生改变 - * leftPoint, rightPoint 有效 - * tokenXAmount, tokenYAmount 至少有一个有值 - * ===> 可以触发 - * step2 根据当前数据获实时获取新的 liquidtiy数据--->改成UserLiquidityInfo数据格式 - * step3 把新增的liquidity传递给Chart组件, - * step4 chart组件把用户已有的liquidtiy + 新增的,划分成bin数据,生成新的图表 - * step5 疑问;?实时修改图表 会导致效率什么问题吗? - */ - function generate_new_user_chart() { - if ( - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - (+tokenXAmount > 0 || +tokenYAmount > 0) - ) { - let new_nfts: any; - if (liquidityShape == 'Spot') { - const new_nft = getLiquiditySpot(); - new_nfts = [new_nft]; - } else { - new_nfts = getLiquidityForCurveAndBidAskMode(); - if (!new_nfts) return; - } - const processed_new_nfts = process_liquidities(new_nfts); - set_new_user_liquidities(processed_new_nfts); - } else { - set_new_user_liquidities([]); - } - } - function getLiquiditySpot() { - const { pool_id } = currentSelectedPool; - return { - pool_id, - left_point: leftPoint, - right_point: rightPoint, - amount_x: toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0'), - amount_y: toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0'), - token_x: tokenX, - token_y: tokenY, - }; - } - function formatWithCommas_for_tip(v: string, commas?: boolean) { - const v_big = Big(v || 0); - let v_temp; - if (v_big.lt(0.001)) { - v_temp = v_big.toFixed(6, 3); - } else { - if (commas) { - v_temp = formatWithCommas(v_big.toFixed(3, 3)); - } else { - v_temp = v_big.toFixed(3, 3); - } - } - return v_temp; - } - function getLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - let nftList: IAddLiquidityInfo[] = []; - const get_x_nfts = - liquidityShape == 'Curve' - ? get_decline_pattern_nfts - : get_rise_pattern_nfts; - const get_y_nfts = - liquidityShape == 'Curve' - ? get_rise_pattern_nfts - : get_decline_pattern_nfts; - if (onlyAddYToken) { - nftList = get_y_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenY, - token_amount: tokenYAmount, - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - if (onlyAddXToken) { - nftList = get_x_nfts({ - left_point: leftPoint, - right_point: rightPoint, - token: tokenX, - token_amount: tokenXAmount, - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - if (!onlyAddXToken && !onlyAddYToken) { - /** - * step1 先判断左侧bin的数量是否 > 1,是的话,左侧包含当前点作等差,否则右侧包含当前点位作等差 - * step2 分配好后,获得对右侧的最小token数量要求 - * step3 另外一侧 总的token数量减去 当前bin中包含的,剩下的 作单边 等差分配即可 - */ - const { point_delta, current_point } = currentSelectedPool; - const current_l_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'floor' - ); - const current_r_point = getBinPointByPoint( - point_delta, - SLOT_NUMBER, - current_point, - 'ceil' - ); - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const bin_number_left = (current_point - leftPoint) / binWidth; - set_token_amount_tip(null); - if (liquidityShape == 'Curve') { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_curve({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 1 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_decline_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_curve({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 2 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_rise_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } else { - if (bin_number_left > 1) { - // 左侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_x_amount_needed } = - get_y_nfts_contain_current_bid_ask({ - left_point: leftPoint, - right_point: current_r_point, - }); - nftList_y = addLiquidityInfoList; - const remain_token_x_amount = Big(tokenXAmount).minus( - min_token_x_amount_needed - ); - if (remain_token_x_amount.lt(0)) { - // 给出提示 token x 数量太少不能添加 3 - const a = formatWithCommas_for_tip(min_token_x_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_x_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenXAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenX.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_x = get_rise_pattern_nfts({ - left_point: current_r_point, - right_point: rightPoint, - token: tokenX, - token_amount: remain_token_x_amount.toFixed(), - formula_fun: formula_of_token_x, - is_token_x: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } else { - // 右侧做等差 - let nftList_x: IAddLiquidityInfo[] = []; - let nftList_y: IAddLiquidityInfo[] = []; - const { addLiquidityInfoList, min_token_y_amount_needed } = - get_x_nfts_contain_current_bid_ask({ - left_point: current_l_point, - right_point: rightPoint, - }); - nftList_x = addLiquidityInfoList; - - const remain_token_y_amount = Big(tokenYAmount).minus( - min_token_y_amount_needed - ); - if (remain_token_y_amount.lt(0)) { - // 给出提示 token y 数量太少不能添加 4 - const a = formatWithCommas_for_tip(min_token_y_amount_needed); - const a_display = formatWithCommas_for_tip( - min_token_y_amount_needed, - true - ); - const tip = ( - - You need at least - { - setTokenYAmount(a); - }} - className="mx-0.5 cursor-pointer underline" - > - {a_display} - - {tokenY.symbol} - - ); - set_token_amount_tip(tip); - return; - } else { - nftList_y = get_decline_pattern_nfts({ - left_point: leftPoint, - right_point: current_l_point, - token: tokenY, - token_amount: remain_token_y_amount.toFixed(), - formula_fun: formula_of_token_y, - is_token_y: true, - }); - } - nftList = nftList_x.concat(nftList_y); - } - } - } - return nftList; - } - /** - * curve 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == exclude_cur_total_nft_number) { - amount_x = dis - .mul(exclude_cur_total_nft_number + 1) - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_curve({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 1).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(exclude_cur_total_nft_number + 1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[exclude_cur_total_nft_number] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == exclude_cur_total_nft_number) { - amount_y = dis - .mul(exclude_cur_total_nft_number + 1) - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,右侧(x)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_x_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - - // 不同点1 - const contain_cur_nft_left_point = left_point; - const contain_cur_nft_right_point = left_point + binWidth; - - // 不同点2 - const exclude_cur_left_point = contain_cur_nft_right_point; - const exclude_cur_right_point = right_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - const left_i_point = exclude_cur_left_point + nftWidth * i; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - right_i_point = exclude_cur_right_point; - } else { - right_i_point = exclude_cur_left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 2).mul( - formula_of_token_x(left_i_point, right_i_point) - ); - - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_y_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenX.decimals, tokenXAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_x = Big(dis).mul(const_value).toFixed(0); - let amount_y; - if (i == 0) { - amount_y = dis - .mul(formula_of_token_y(left_point, current_point + 1)) - .toFixed(0); - min_token_y_amount_needed_nonDivisible = amount_y; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x, - amount_y: amount_y || '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_y_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_y_amount_needed: toReadableNumber( - tokenY.decimals, - min_token_y_amount_needed_nonDivisible - ), - }; - } - /** - * bid ask 模式下,左侧(y)包含当前点位的 nfts划分 - * 此区间bin的数量要求 > 1 - * 双边 - * @param param0 - * @returns - */ - function get_y_nfts_contain_current_bid_ask({ - left_point, - right_point, - }: { - left_point: number; - right_point: number; - }) { - /** - * 做等差时,包含当前点位的bin作为一个nft,宽度没必要跟其它的nft宽度一致,从而可以得到对另外一个token数量的最小要求 - * 另外一边做等差时,总的数量 - 当前点位的bin中包含的数量,剩下的做等差 - * 给的条件是左点位,大于当前点位的右点位 - * step 1 把包含当前点位bin 单独划分出来作为一个nft - * step 2 把剩余的bin 划分若干个nft - * step 3 总的nft 它们 token amount 之和固定,求出等差 - * step 4 求出等差后 得到包含当前点位的bin对另外一半的最低数量要求,数量满足就可以添加,不满足就不可以添加并给出提示 - */ - - /** - * 从左往右逐渐下降模式 - * 从右往左计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - - const { pool_id, point_delta, current_point } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - // 不同点1 - const contain_cur_nft_left_point = right_point - binWidth; - const contain_cur_nft_right_point = right_point; - - // 不同点2 - const exclude_cur_left_point = left_point; - const exclude_cur_right_point = contain_cur_nft_left_point; - - const exclude_cur_total_bin_number = - (exclude_cur_right_point - exclude_cur_left_point) / binWidth; - let exclude_cur_total_nft_number; - let exclude_cur_bin_number_in_a_nft; - if (exclude_cur_total_bin_number < max_nft_divisional_per_side) { - exclude_cur_bin_number_in_a_nft = 1; - exclude_cur_total_nft_number = exclude_cur_total_bin_number; - } else { - exclude_cur_bin_number_in_a_nft = Math.floor( - exclude_cur_total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!( - exclude_cur_total_bin_number % max_nft_divisional_per_side - ); - exclude_cur_total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = - point_delta * slot_number_in_a_bin * exclude_cur_bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < exclude_cur_total_nft_number; i++) { - // 不同点3 - let left_i_point; - let right_i_point; - if (i == exclude_cur_total_nft_number - 1) { - left_i_point = exclude_cur_left_point; - } else { - left_i_point = exclude_cur_right_point - nftWidth * (i + 1); - } - right_i_point = exclude_cur_right_point - nftWidth * i; - const const_i = Big(i + 2).mul( - formula_of_token_y(left_i_point, right_i_point) - ); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i + 1] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - - // 不同点4 - const const_last = Big(1).mul( - formula_of_token_y(contain_cur_nft_left_point, current_point + 1) - ); - total_const = total_const.plus(const_last); - - addLiquidityInfoHelp[0] = { - left_point: contain_cur_nft_left_point, - right_point: contain_cur_nft_right_point, - const_value: const_last.toFixed(), - }; - - // 不同点5 - let min_token_x_amount_needed_nonDivisible; - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(tokenY.decimals, tokenYAmount || '0') - ).div(total_const); - for (let i = 0; i < exclude_cur_total_nft_number + 1; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_y = Big(dis).mul(const_value).toFixed(0); - let amount_x; - if (i == 0) { - amount_x = dis - .mul( - formula_of_token_x(current_point + 1, contain_cur_nft_right_point) - ) - .toFixed(0); - min_token_x_amount_needed_nonDivisible = amount_x; - } - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: amount_x || '0', - amount_y: amount_y, - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - - return { - min_token_x_amount_needed_nonDivisible, - addLiquidityInfoList, - min_token_x_amount_needed: toReadableNumber( - tokenX.decimals, - min_token_x_amount_needed_nonDivisible - ), - }; - } - /** - * curve 和 bid ask 上升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_rise_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐上升模式 - * 从左往右计算 - * e.g. - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenYAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenYAmount; - * ===>dis = tokenYAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - const left_i_point = left_point + nftWidth * i; - let right_i_point; - if (i == total_nft_number - 1) { - right_i_point = right_point; - } else { - right_i_point = left_point + nftWidth * (i + 1); - } - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - /** - * curve 和 bid ask 下降升模式下 - * 单边 - * @param param0 - * @returns - */ - function get_decline_pattern_nfts({ - left_point, - right_point, - token, - token_amount, - formula_fun, - is_token_x, - is_token_y, - }: { - left_point: number; - right_point: number; - token: TokenMetadata; - token_amount: string; - formula_fun: Function; - is_token_x?: boolean; - is_token_y?: boolean; - }) { - /** - * 从左往右逐渐下降模式 - * nft 从右往左计算 - * e.g. - * 公式推导: - * (dis * 常量1) + (2dis * 常量2) + ...(n*dis * 常量n) = tokenXAmount - * ===>dis(常量1 + 2常量2 + ...n*常量n) = tokenXAmount; - * ===>dis = tokenXAmount/(常量1 + 2常量2 + ...n*常量n) - * ===>求出dis后,就可以知道每个nft的amount - * NOTE:最后一个(最高的那个nft的宽度可能跟别的nft宽度不一致) - * */ - const { pool_id, point_delta } = currentSelectedPool; - const slot_number_in_a_bin = SLOT_NUMBER; - const binWidth = slot_number_in_a_bin * point_delta; - const total_bin_number = (right_point - left_point) / binWidth; - let total_nft_number; - let bin_number_in_a_nft; - if (total_bin_number < max_nft_divisional_per_side) { - const unbroken_nft_number = Math.floor(total_bin_number); - const has_remaining = !!(total_bin_number % 1); - bin_number_in_a_nft = 1; - total_nft_number = has_remaining - ? unbroken_nft_number + 1 - : unbroken_nft_number; - } else { - bin_number_in_a_nft = Math.floor( - total_bin_number / max_nft_divisional_per_side - ); - const has_remaining = !!(total_bin_number % max_nft_divisional_per_side); - total_nft_number = has_remaining - ? max_nft_divisional_per_side + 1 - : max_nft_divisional_per_side; - } - const nftWidth = point_delta * slot_number_in_a_bin * bin_number_in_a_nft; - let total_const = Big(0); - const addLiquidityInfoList: IAddLiquidityInfo[] = []; - const addLiquidityInfoHelp: IAddLiquidityInfoHelp = {}; - for (let i = 0; i < total_nft_number; i++) { - let left_i_point; - let right_i_point; - if (i == total_nft_number - 1) { - left_i_point = left_point; - } else { - left_i_point = right_point - nftWidth * (i + 1); - } - right_i_point = right_point - nftWidth * i; - const const_i = Big(i + 1).mul(formula_fun(left_i_point, right_i_point)); - total_const = total_const.plus(const_i); - addLiquidityInfoHelp[i] = { - left_point: left_i_point, - right_point: right_i_point, - const_value: const_i.toFixed(), - }; - } - if (total_const.gt(0)) { - const dis = Big( - toNonDivisibleNumber(token.decimals, token_amount || '0') - ).div(total_const); - for (let i = 0; i < total_nft_number; i++) { - const { left_point, right_point, const_value } = - addLiquidityInfoHelp[i]; - const amount_i = Big(dis).mul(const_value).toFixed(0); - addLiquidityInfoList.push({ - pool_id, - left_point, - right_point, - amount_x: is_token_x ? amount_i : '0', - amount_y: is_token_y ? amount_i : '0', - min_amount_x: '0', - min_amount_y: '0', - }); - } - } - return addLiquidityInfoList; - } - function formula_of_token_x(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint - leftPoint) - 1) / - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), rightPoint - 1)) - ); - } - function formula_of_token_y(leftPoint: number, rightPoint: number) { - return ( - (Math.pow(Math.sqrt(CONSTANT_D), rightPoint) - - Math.pow(Math.sqrt(CONSTANT_D), leftPoint)) / - (Math.sqrt(CONSTANT_D) - 1) - ); - } - /** - * 把传递给合约的liquidities数据形式转换成用于图表展示的liquidity数据形式 - */ - function process_liquidities(liquidities: IAddLiquidityInfo[]) { - const { pool_id } = currentSelectedPool; - const new_liquidities: UserLiquidityInfo[] = []; - liquidities.forEach((l: IAddLiquidityInfo) => { - const { left_point, right_point, amount_x, amount_y } = l; - const L = get_l_amount_by_condition({ - left_point, - right_point, - token_x_amount: amount_x, - token_y_amount: amount_y, - poolDetail: currentSelectedPool, - }); - new_liquidities.push({ - pool_id, - left_point, - right_point, - amount: L, - }); - }); - return new_liquidities; - } - const mobileDevice = isMobile(); - return ( - - {/* mobile head */} -
-
- -
- - - -
-
- {/* pc head */} -
{ - history.goBack(); - }} - > -
- -
- - - -
- - {/* content */} -
-
- {/* no Data */} - {currentSelectedPool ? null : } - {/* add pool part */} - {currentSelectedPool && - !currentSelectedPool.pool_id && - OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {currentSelectedPool && - !currentSelectedPool.pool_id && - !OPEN_CREATE_POOL_ENTRY ? ( - - ) : null} - {/* add Liquidity part */} - {/* left area */} - {currentSelectedPool && currentSelectedPool.pool_id ? ( - - ) : null} - {/* right area todo */} -
-
-
- -
- - { - setTokenX(token); - setTokenXBalanceFromNear(token?.near?.toString()); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - setTokenYBalanceFromNear(token?.near?.toString()); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
- - - {token_amount_tip ? ( -
- - {token_amount_tip} -
- ) : null} - -
-
- -
- -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - - -
{ - setHoverFeeBox(false); - }} - onMouseEnter={() => { - setHoverFeeBox(true); - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
- -
- -
- -
- {[SpotShape, CurveShape, BidAskShape].map( - (Shape, index: number) => { - let disabled = false; - if ( - (index == 1 || index == 2) && - only_suppport_spot_shape - ) { - disabled = true; - } - return ( -
{ - e.preventDefault(); - e.stopPropagation(); - if (index === 0) setLiquidityShape('Spot'); - else if (index === 1 && !only_suppport_spot_shape) - setLiquidityShape('Curve'); - else if (index == 2 && !only_suppport_spot_shape) - setLiquidityShape('BidAsk'); - }} - > - - - - {index === 0 && ( - - )} - {index === 1 && ( - - )} - - {index === 2 && ( - - )} - -
- ); - } - )} -
- {/* new user chart part */} - {isSignedIn && currentSelectedPool ? ( -
-
-
- -
-
- Generate -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- ) : null} - - {currentSelectedPool && currentSelectedPool.pool_id && ( - - )} -
- - -
-
-
-
-
-
- ); -} -/** - * 双边 最小token数量不满足 提示 - * 双边 一侧token 数量太多 传递的时候只传实际使用值 - * @returns - */ -function AddLiquidityButton() { - const { - currentSelectedPool, - tokenX, - tokenY, - binNumber, - SLOT_NUMBER, - leftPoint, - rightPoint, - currentPoint, - liquidityShape, - tokenXAmount, - tokenYAmount, - tokenXBalanceFromNear, - tokenYBalanceFromNear, - onlyAddXToken, - onlyAddYToken, - invalidRange, - getLiquiditySpot, - getLiquidityForCurveAndBidAskMode, - } = useContext(LiquidityProviderData); - const tokenSort = tokenX.id == currentSelectedPool.token_x; - const [addLiquidityButtonLoading, setAddLiquidityButtonLoading] = - useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - const { token_x, token_y, point_delta, pool_id } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - - function addLiquiditySpot() { - setAddLiquidityButtonLoading(true); - const new_liquidity = getLiquiditySpot(); - add_liquidity(new_liquidity); - } - function addLiquidityForCurveAndBidAskMode() { - /** - * 已知条件: - * bin的数量、一个bin里 slot的数量、leftPoint、rightPoint、tokenXAmount、tokenYAmount - * 当前点位为point,以slot为单位 下一跳是 point + slot - * 当前点位为point,以bin为单位 下一跳是 point + bin * slots - * 最小的bin的高度就是等差的值 为dis - **/ - setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); - let nftList: IAddLiquidityInfo[] = []; - nftList = getLiquidityForCurveAndBidAskMode(); - if (!nftList) { - setAddLiquidityButtonLoading(false); - return; - } - /** - * 计算出 nftList token x tokeny 的数量,这是需要的总数量 - * tokenXAmount_nonDivisible,tokenYAmount_nonDivisible 是输入的总数量 - * 单边只有一个nft且包含当前点位的,输入的量可能会多余,所以不采用输入的值作为参数,而是采用实际使用的值作为参数 - */ - let last_total_needed_token_x_amount = Big(0); - let last_total_needed_token_y_amount = Big(0); - nftList.forEach((nft: IAddLiquidityInfo) => { - const { amount_x, amount_y } = nft; - last_total_needed_token_x_amount = last_total_needed_token_x_amount.plus( - amount_x || 0 - ); - last_total_needed_token_y_amount = last_total_needed_token_y_amount.plus( - amount_y || 0 - ); - }); - batch_add_liquidity({ - liquidityInfos: nftList, - token_x: tokenX, - token_y: tokenY, - amount_x: last_total_needed_token_x_amount.toFixed(), - amount_y: last_total_needed_token_y_amount.toFixed(), - }); - } - function getMax(token: TokenMetadata, balance: string) { - return token.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - } - function getButtonText() { - let txt: any = ( - - ); - if (invalidRange) { - txt = ( - - ); - } else if ( - (onlyAddXToken && +tokenXAmount == 0 && tokenSort) || - (onlyAddXToken && +tokenYAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - (onlyAddYToken && +tokenYAmount == 0 && tokenSort) || - (onlyAddYToken && +tokenXAmount == 0 && !tokenSort) - ) { - txt = ( - - ); - } else if ( - !onlyAddXToken && - !onlyAddYToken && - (+tokenXAmount == 0 || +tokenYAmount == 0) - ) { - txt = ( - - ); - } else if ( - +tokenXAmount > 0 && - new BigNumber(tokenXAmount).isGreaterThan( - getMax(tokenX, tokenXBalanceFromNear) - ) - ) { - txt = ( - - ); - } else if ( - +tokenYAmount > 0 && - new BigNumber(tokenYAmount).isGreaterThan( - getMax(tokenY, tokenYBalanceFromNear) - ) - ) { - txt = ( - - ); - } - return txt; - } - function getButtonStatus() { - const condition1 = currentSelectedPool?.pool_id; - let condition2; - if (onlyAddXToken) { - if (tokenSort) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } else { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - } else if (onlyAddYToken) { - if (tokenSort) { - condition2 = - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } else { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount); - } - } else if (!invalidRange) { - condition2 = - +tokenXAmount > 0 && - new BigNumber( - getMax(tokenX, tokenXBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenXAmount) && - +tokenYAmount > 0 && - new BigNumber( - getMax(tokenY, tokenYBalanceFromNear) - ).isGreaterThanOrEqualTo(tokenYAmount); - } - return !(condition1 && condition2); - } - const isAddLiquidityDisabled = getButtonStatus(); - - const add_lp_func = - liquidityShape === 'Spot' - ? addLiquiditySpot - : addLiquidityForCurveAndBidAskMode; - - return ( -
- {isSignedIn ? ( - - <>{getButtonText()}} - /> - - ) : ( - - )} -
- ); -} -function PointsComponent() { - const { pair_is_reverse } = useContext(AddLiquidityProviderData); - if (pair_is_reverse) { - return ; - } else { - return ; - } -} -function SetPointsComponent() { - const { - binNumber, - setBinNumber, - currentSelectedPool, - tokenX, - tokenY, - tokenXAmount, - tokenYAmount, - set_token_amount_tip, - - pointChange, - currentPoint, - liquidityShape, - - leftPoint, - setLeftPoint, - rightPoint, - setRightPoint, - - SLOT_NUMBER, - BIN_WIDTH, - - switch_pool_loading, - - isSignedIn, - } = useContext(LiquidityProviderData); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const [radius, setRadius] = useState(); - const [targetCustomPrice, setTargetCustomPrice] = useState(''); - const [leftCustomPrice, setLeftCustomPrice] = useState(''); - const [rightCustomPrice, setRightCustomPrice] = useState(''); - const [targetPoint, setTargetPoint] = useState(); - - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [targetInputStatus, setTargetInputStatus] = useState(false); - const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - - const [slider_point_min, set_slider_point_min] = useState(); - const [slider_point_max, set_slider_point_max] = useState(); - - const [slider_left_value, set_slider_left_value] = useState(); - const [slider_right_value, set_slider_right_value] = useState(); - - const { token_x, token_y } = currentSelectedPool; - const token_x_decimals = - tokenX.id == token_x ? tokenX.decimals : tokenY.decimals; - const token_y_decimals = - tokenY.id == token_y ? tokenY.decimals : tokenX.decimals; - const tokenSort = tokenX.id == currentSelectedPool.token_x; - - // init - useEffect(() => { - if (currentSelectedPool?.pool_id && !switch_pool_loading) { - const { current_point } = currentSelectedPool; - const right_point = handlePointToAppropriatePoint( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - const left_point = right_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; - setTargetPoint(current_point); - setRadius(RADIUS_DEFAULT_NUMBER); - setLeftPoint(left_point); - setRightPoint(right_point); - set_slider_point_range(); - setPriceRangeMode('by_range'); - setChartTab('liquidity'); - } - }, [currentSelectedPool, switch_pool_loading]); - - // 中文 左侧改变===》点位 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - // effect bin - const diff = rightPoint - leftPoint; - const bin_number_temp = diff / BIN_WIDTH; - setBinNumber(bin_number_temp); - // effect slider - const slider_left_value = get_slider_value_by_point(leftPoint); - const slider_right_value = get_slider_value_by_point(rightPoint); - - set_slider_left_value(slider_left_value); - set_slider_right_value(slider_right_value); - // effect right area - pointChange({ leftPoint, rightPoint, currentPoint }); - } - }, [leftPoint, rightPoint, BIN_WIDTH, slider_point_min, slider_point_max]); - - useEffect(() => { - if ( - liquidityShape == 'Spot' && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) - ) { - pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); - } - }, [liquidityShape]); - - // 数据有变动==》去掉token 太少提示 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - set_token_amount_tip(null); - } - }, [liquidityShape, leftPoint, rightPoint]); - - // 修改bin --> 合适的右点位 --->合适的bin - function changeBin(bin: number) { - let appropriate_right_point = leftPoint + BIN_WIDTH * bin; - if (appropriate_right_point > POINTRIGHTRANGE) { - appropriate_right_point = POINTRIGHTRANGE; - } - const appropriate_bin_number = - (appropriate_right_point - leftPoint) / BIN_WIDTH; - setRightPoint(appropriate_right_point); - setBinNumber(appropriate_bin_number); - } - - // 修改radius-->合适的左右点位 --->合适的radius - function changeRadius(radius: number) { - let appropriate_right_point = handlePointToAppropriatePoint( - targetPoint + BIN_WIDTH * radius - ); - let appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - const appropriate_radius = - (appropriate_right_point - appropriate_left_point) / (BIN_WIDTH * 2); - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - setRadius(appropriate_radius); - } - // 修改 targetPrice-->合适的左右点位--->合适的targetPrice - function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_target_point = handlePriceToAppropriatePoint(price); - const appropriate_right_point = handlePointToAppropriatePoint( - appropriate_target_point + BIN_WIDTH * radius - ); - const appropriate_left_point = handlePointToAppropriatePoint( - appropriate_right_point - BIN_WIDTH * radius * 2 - ); - appropriate_target_point = appropriate_right_point - BIN_WIDTH * radius; - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - return appropriate_target_point; - } - // 设置slider可以操作的point 左右点位区间 - function set_slider_point_range() { - const { current_point } = currentSelectedPool; - const max_point = handlePointToAppropriatePoint( - current_point + BIN_WIDTH * (SLIDER_BIN_NUMBER / 2) - ); - const min_point = max_point - SLIDER_BIN_NUMBER * BIN_WIDTH; - set_slider_point_min(min_point); - set_slider_point_max(max_point); - } - function get_slider_value_by_point(point: number) { - const value = (point - slider_point_min) / BIN_WIDTH; - return value; - } - function get_point_by_slider_value(v: number) { - const new_point = slider_point_min + v * BIN_WIDTH; - return new_point; - } - function handlePointToAppropriatePoint(point: number) { - const { point_delta } = currentSelectedPool; - return getBinPointByPoint(point_delta, SLOT_NUMBER, point); - } - function handlePriceToAppropriatePoint(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const appropriate_point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return appropriate_point; - } - function getLeftPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(leftPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getRightPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(rightPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getTargetPrice() { - if (currentSelectedPool && currentSelectedPool.pool_id) { - const { token_x, token_y } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - let price = getPriceByPoint(targetPoint, decimalRate); - // if (tokenX.id == token_y) { - // price = new BigNumber(1).dividedBy(price).toFixed(); - // } - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getPair() { - if (tokenSort) { - return `(${tokenX.symbol}/${tokenY.symbol})`; - } else { - return `(${tokenY.symbol}/${tokenX.symbol})`; - } - } - return ( -
- {/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - setChartTab('yours'); - }} - > - Yours - -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( - - )} -
- {isSignedIn && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )} -
- {/* set price range area */} -
- {/* price range mode area */} -
-
- - - - {getPair()} - -
- -
- { - setPriceRangeMode('by_range'); - }} - > - - - { - setPriceRangeMode('by_radius'); - changeRadius(radius); - }} - > - - -
-
- {/* content */} - {/*
- -
*/} -
- {/* target price input box */} -
- - - - -
- - {/* radius input box */} -
- - - - -
- - {/* min price input box */} -
- - - - -
- - {/* max price input box */} -
- - - - -
- - {/* bin number input box */} -
- - - - -
-
- {/* tip in foot */} -
- *Only NEAR is needed in the price range you choose. -
-
-
- ); -} -function SetPointsComponentReverse() { - const { - binNumber, - setBinNumber, - currentSelectedPool, - tokenX, - tokenY, - set_token_amount_tip, - - pointChange, - currentPoint, - liquidityShape, - - SLOT_NUMBER, - BIN_WIDTH, - - switch_pool_loading, - - isSignedIn, - } = useContext(LiquidityProviderData); - const [priceRangeMode, setPriceRangeMode] = useState< - 'by_range' | 'by_radius' - >('by_range'); - const [radius, setRadius] = useState(); - const [targetCustomPrice, setTargetCustomPrice] = useState(''); - const [leftCustomPrice, setLeftCustomPrice] = useState(''); - const [rightCustomPrice, setRightCustomPrice] = useState(''); - const [targetPoint, setTargetPoint] = useState(); - - const [leftInputStatus, setLeftInputStatus] = useState(false); - const [rightInputStatus, setRightInputStatus] = useState(false); - const [targetInputStatus, setTargetInputStatus] = useState(false); - - const [leftPoint, setLeftPoint] = useState(); - const [rightPoint, setRightPoint] = useState(); - - const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); - - const token_x_decimals = tokenX.decimals; - const token_y_decimals = tokenY.decimals; - - // init - useEffect(() => { - if (currentSelectedPool?.pool_id && !switch_pool_loading) { - const { current_point } = currentSelectedPool; - const left_point = get_bin_point_by_point( - current_point + BIN_WIDTH * RADIUS_DEFAULT_NUMBER - ); - const right_point = left_point - BIN_WIDTH * RADIUS_DEFAULT_NUMBER * 2; - setTargetPoint(current_point); - setRadius(RADIUS_DEFAULT_NUMBER); - setLeftPoint(left_point); - setRightPoint(right_point); - setPriceRangeMode('by_range'); - setChartTab('liquidity'); - } - }, [currentSelectedPool, switch_pool_loading]); - - // 中文 左侧点位改变 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - // effect bin - const diff = leftPoint - rightPoint; - const bin_number_temp = diff / BIN_WIDTH; - setBinNumber(bin_number_temp); - // effect right area - // todo - console.log( - 'real_leftPoint, real_rightPoint, real_current_point', - rightPoint, - leftPoint, - currentPoint - ); - pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); - } - }, [leftPoint, rightPoint, BIN_WIDTH]); - - useEffect(() => { - if ( - liquidityShape == 'Spot' && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) - ) { - pointChange({ leftPoint: rightPoint, rightPoint: leftPoint }); - } - }, [liquidityShape]); - - // 数据有变动==》去掉token 太少提示 - useEffect(() => { - if (!isInvalid(leftPoint) && !isInvalid(rightPoint)) { - set_token_amount_tip(null); - } - }, [liquidityShape, leftPoint, rightPoint]); - - // 修改bin --> 合适的右点位 --->合适的bin - function changeBin(bin: number) { - let appropriate_right_point = leftPoint - BIN_WIDTH * bin; - if (appropriate_right_point < POINTLEFTRANGE) { - appropriate_right_point = POINTLEFTRANGE; - } - const appropriate_bin_number = - (leftPoint - appropriate_right_point) / BIN_WIDTH; - setRightPoint(appropriate_right_point); - setBinNumber(appropriate_bin_number); - } - - // 修改radius-->合适的左右点位 --->合适的radius - function changeRadius(radius: number) { - let appropriate_left_point = get_bin_point_by_point( - targetPoint + BIN_WIDTH * radius - ); - let appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - const appropriate_radius = - (appropriate_left_point - appropriate_right_point) / (BIN_WIDTH * 2); - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - setRadius(appropriate_radius); - } - // 修改 targetPrice-->合适的左右点位--->合适的targetPrice - function handleTargetPriceToAppropriatePoint(price: string) { - let appropriate_target_point = get_bin_point_by_price(reverse_price(price)); - const appropriate_left_point = get_bin_point_by_point( - appropriate_target_point + BIN_WIDTH * radius - ); - const appropriate_right_point = get_bin_point_by_point( - appropriate_left_point - BIN_WIDTH * radius * 2 - ); - appropriate_target_point = appropriate_left_point - BIN_WIDTH * radius; - setLeftPoint(appropriate_left_point); - setRightPoint(appropriate_right_point); - return appropriate_target_point; - } - function getLeftPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(leftPoint) - ) { - const price = reverse_price(get_price_by_point(leftPoint)); - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getRightPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(rightPoint) - ) { - const price = reverse_price(get_price_by_point(rightPoint)); - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function getTargetPrice() { - if ( - currentSelectedPool && - currentSelectedPool.pool_id && - !isInvalid(targetPoint) - ) { - let price = reverse_price(get_price_by_point(targetPoint)); - if (new BigNumber(price).isLessThan('0.00000001')) { - return price; - } else { - return toPrecision(price.toString(), 8); - } - } else { - return ''; - } - } - function get_point_by_price(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate_point = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const point = getPointByPrice(point_delta, price, decimalRate_point); - return point; - } - function get_price_by_point(point: number) { - const decimalRate_price = - Math.pow(10, token_x_decimals) / Math.pow(10, token_y_decimals); - return getPriceByPoint(point, decimalRate_price); - } - function get_bin_point_by_price(price: string) { - const { point_delta } = currentSelectedPool; - const decimalRate = - Math.pow(10, token_y_decimals) / Math.pow(10, token_x_decimals); - const appropriate_point = getBinPointByPrice( - point_delta, - price, - decimalRate, - SLOT_NUMBER - ); - return appropriate_point; - } - function get_bin_point_by_point(point: number) { - const { point_delta } = currentSelectedPool; - return getBinPointByPoint(point_delta, SLOT_NUMBER, point); - } - function getPair() { - return `(${tokenY.symbol}/${tokenX.symbol})`; - } - return ( -
- {/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - setChartTab('yours'); - }} - > - Yours - -
-
- {!isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( - - )} -
- {isSignedIn && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- {/* */} -
- )} -
- {/* set price range area */} -
- {/* price range mode area */} -
-
- - - - {getPair()} - -
- -
- { - setPriceRangeMode('by_range'); - }} - > - - - { - setPriceRangeMode('by_radius'); - changeRadius(radius); - }} - > - - -
-
- {/* content */} -
- {/* target price input box */} -
- - - - -
- - {/* radius input box */} -
- - - - -
- - {/* min price input box */} -
- - - - { - return get_bin_point_by_price(reverse_price(price)); - }} - disbaled={priceRangeMode === 'by_radius'} - customPrice={leftCustomPrice} - getPrice={getLeftPrice} - setCustomPrice={setLeftCustomPrice} - inputStatus={leftInputStatus} - setInputStatus={setLeftInputStatus} - setPoint={setLeftPoint} - point={leftPoint} - > -
- - {/* max price input box */} -
- - - - { - return get_bin_point_by_price(reverse_price(price)); - }} - customPrice={rightCustomPrice} - getPrice={getRightPrice} - setCustomPrice={setRightCustomPrice} - inputStatus={rightInputStatus} - setInputStatus={setRightInputStatus} - setPoint={setRightPoint} - point={rightPoint} - disbaled={priceRangeMode === 'by_radius'} - > -
- - {/* bin number input box */} -
- - - - -
-
- {/* tip in foot */} -
- *Only NEAR is needed in the price range you choose. -
-
-
- ); -} - -/** - * step1 slider设置数字区间[0, 20] - * step2 数字区间 和 point区间做一个 双向 映射 - * step step 为 一个bin,区间设定逻辑是 以当前价格为起点,向左向右各辐射30个point(可配置) - * @param param0 - * @returns - */ -function Slider({ - value, - set_slider_left_value, - set_slider_right_value, - set_left_point, - set_right_point, - get_point_by_slider_value, -}: { - value: any; - set_slider_left_value: Function; - set_slider_right_value: Function; - set_left_point: Function; - set_right_point: Function; - get_point_by_slider_value: Function; -}) { - return ( - { - const [num1, num2] = v; - set_slider_left_value(num1); - set_slider_right_value(num2); - const left_point = get_point_by_slider_value(num1); - const right_point = get_point_by_slider_value(num2); - set_left_point(left_point); - set_right_point(right_point); - }} - value={value} - min={0} - max={SLIDER_BIN_NUMBER} - step={1} - pearling={true} - /> - ); -} - -function CreatePoolComponent({ - currentSelectedPool, - tokenX, - tokenY, - tokenPriceList, - buttonSort, -}: { - currentSelectedPool: PoolInfo; - tokenX: TokenMetadata; - tokenY: TokenMetadata; - tokenPriceList: Record; - buttonSort: boolean; -}) { - const [createPoolButtonLoading, setCreatePoolButtonLoading] = useState(false); - const [createPoolRate, setCreatePoolRate] = useState(''); - const [rateStatus, setRateStatus] = useState(true); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - useEffect(() => { - if (createPoolRate) { - const rateString = new BigNumber(1).dividedBy(createPoolRate).toFixed(); - setCreatePoolRate(toPrecision(rateString, 6)); - } - }, [buttonSort]); - function getCurrentPriceValue(token: TokenMetadata) { - if (token) { - const price = tokenPriceList[token.id]?.price; - return price ? `${'$' + price}` : '$-'; - } else { - return '$-'; - } - } - function createPool() { - setCreatePoolButtonLoading(true); - const { fee } = currentSelectedPool; - const pointDelta = POINTDELTAMAP[fee]; - let decimalRate = - Math.pow(10, tokenY.decimals) / Math.pow(10, tokenX.decimals); - let init_point = getPointByPrice( - pointDelta, - createPoolRate, - decimalRate, - true - ); - const arr = [tokenX.id, tokenY.id]; - arr.sort(); - if (arr[0] !== tokenX.id) { - decimalRate = - Math.pow(10, tokenX.decimals) / Math.pow(10, tokenY.decimals); - init_point = getPointByPrice( - pointDelta, - new BigNumber(1).dividedBy(createPoolRate).toFixed(), - decimalRate, - true - ); - create_pool({ - token_a: tokenY.id, - token_b: tokenX.id, - fee: currentSelectedPool.fee, - init_point, - }); - } else { - create_pool({ - token_a: tokenX.id, - token_b: tokenY.id, - fee: currentSelectedPool.fee, - init_point, - }); - } - } - function switchRate() { - setRateStatus(!rateStatus); - } - function getPoolRate() { - if (createPoolRate) { - const rate = 1 / +createPoolRate; - return toPrecision(rate.toString(), 6); - } - return ''; - } - const mobileDevice = isMobile(); - return ( -
-
- - : -
-
- -
-
-

- -

-
-
-

- -

-
- - 1 {toRealSymbol(tokenX?.symbol)} = - -
- { - setCreatePoolRate(target.value); - }} - /> - - {toRealSymbol(tokenY?.symbol)} - -
-
-
- - - -
- {rateStatus ? ( -
- 1 {toRealSymbol(tokenX?.symbol)} - - ({getCurrentPriceValue(tokenX)}) - - - - {createPoolRate} {toRealSymbol(tokenY?.symbol)} - -
- ) : ( -
- 1 {toRealSymbol(tokenY?.symbol)} - - ({getCurrentPriceValue(tokenY)}) - - - - {getPoolRate()} {toRealSymbol(tokenX?.symbol)} - -
- )} - - -
-
-
-
-
- {isSignedIn ? ( - - ( - <> - - - )} - /> - - ) : ( - - )} -
- ); -} - -function NoDataComponent(props: any) { - const { isNoPool } = props; - const [quickOptions, setQuickOptions] = useState([5, 10, 20, 50]); - const mobileDevice = isMobile(); - return ( -
-
- -
- {isNoPool ? ( -
- -
- ) : null} -
- {/* range chart area */} -
- -
- {/* input range area */} -
-
-
- - - -
-
- -
- -
- -
-
-
-
- - - -
-
- -
- -
- -
-
-
-
-
-
- - ( - <> - {isNoPool ? ( - - ) : ( - - )} - - )} - /> - -
- ); -} -function PointInputComponent({ - handlePriceToAppropriatePoint, - customPrice, - setCustomPrice, - - getPrice, - point, - setPoint, - - inputStatus, - setInputStatus, - disbaled, -}: any) { - return ( -
- { - setInputStatus(false); - if (customPrice) { - const appropriate_point_temp = - handlePriceToAppropriatePoint(customPrice); - setPoint(appropriate_point_temp); - } else { - setPoint(point); - } - }} - disabled={disbaled} - value={inputStatus ? customPrice : getPrice()} - onChange={({ target }) => { - setInputStatus(true); - const inputPrice = target.value; - if (Big(target.value || 0).lt(0)) { - setCustomPrice('0'); - } else { - setCustomPrice(inputPrice); - } - }} - /> -
- ); -} - -export function IntegerInputComponent({ - value, - setValue, - disabled, - triggerByValue, -}: any) { - const removeLeadingZeros = (s: string) => { - const oldLen = s.length; - s = s.replace(/^0+/, ''); - - if (s.length === 0 && oldLen > 0) { - s = '0'; - } - - return s; - }; - - const handleChange = (val: string) => { - val = val.replace(/[^\d]/g, ''); - val = removeLeadingZeros(val); - setValue(val); - if (val) { - triggerByValue(val); - } - }; - - return ( -
- { - if (!target.value) { - setValue(1); - triggerByValue(1); - } - }} - onChange={({ target }) => { - handleChange(target.value); - }} - /> -
- ); -} -function OneSide({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InvalidRange({ show }: { show: boolean }) { - return ( -
- - -
- -
-
- ); -} -function InputAmount({ - token, - balance, - tokenPriceList, - changeAmount, - amount, - currentSelectedPool, - hidden, -}: { - token: TokenMetadata; - balance: string; - tokenPriceList: Record; - changeAmount: any; - amount: string; - currentSelectedPool: PoolInfo; - hidden?: Boolean; -}) { - const [inputPrice, setInputPrice] = useState(''); - const [showNearTip, setShowNearTip] = useState(false); - const { globalState } = useContext(WalletContext); - const isSignedIn = globalState.isSignedIn; - useEffect(() => { - const price = token ? tokenPriceList[token.id]?.price : ''; - if (price && amount) { - setInputPrice(new BigNumber(price).multipliedBy(amount).toFixed()); - } else { - setInputPrice(''); - } - if (token?.id == WRAP_NEAR_CONTRACT_ID && amount) { - const difference = new BigNumber(maxBalance).minus(amount); - const b = difference.toFixed(); - const r = difference.isLessThan(0); - if (r) { - setShowNearTip(true); - } else { - setShowNearTip(false); - } - } else { - setShowNearTip(false); - } - }, [amount, token, tokenPriceList.length]); - function getBalance() { - let r = '0'; - if (token && balance) { - r = formatWithCommas(toPrecision(balance.toString(), 3)); - } - return isSignedIn ? r : '-'; - } - function showCurrentPrice() { - if (isNoPool) { - return '$-'; - } else if (inputPrice) { - return '$' + formatWithCommas(toPrecision(inputPrice.toString(), 3)); - } - return '$-'; - } - const maxBalance = - token?.id !== WRAP_NEAR_CONTRACT_ID - ? balance - : Number(balance) <= 0.5 - ? '0' - : String(Number(balance) - 0.5); - const isNoPool = !currentSelectedPool?.pool_id; - return ( -
- - {showNearTip && !isNoPool ? ( -
- - -
- ) : null} -
- ); -} diff --git a/src/pages/poolsV3/components/add/AddLiquidityButton.tsx b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx index 5f8b4ecba..5aedb1bd7 100644 --- a/src/pages/poolsV3/components/add/AddLiquidityButton.tsx +++ b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx @@ -58,14 +58,6 @@ export function AddLiquidityButton() { * 最小的bin的高度就是等差的值 为dis **/ setAddLiquidityButtonLoading(true); - const tokenXAmount_nonDivisible = toNonDivisibleNumber( - tokenX.decimals, - tokenXAmount || '0' - ); - const tokenYAmount_nonDivisible = toNonDivisibleNumber( - tokenY.decimals, - tokenYAmount || '0' - ); let nftList: IAddLiquidityInfo[] = []; nftList = getLiquidityForCurveAndBidAskMode(); if (!nftList) { diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index b94d61c8a..8414a7a7c 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -153,14 +153,19 @@ export function YourLiquidityBox(props: { account_id: accountId, }); if (dcl_fee_result) { - // 24h profit - apr_24 = get_account_24_apr(dcl_fee_result, poolDetail, tokenPriceList); // total unClaimed fee const [ unClaimed_tvl_fee, unClaimed_amount_x_fee, unClaimed_amount_y_fee, ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); + // 24h profit + apr_24 = get_account_24_apr( + unClaimed_tvl_fee, + dcl_fee_result, + poolDetail, + tokenPriceList + ); // total earned fee const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; total_earned_fee_x = toReadableNumber( @@ -186,9 +191,6 @@ export function YourLiquidityBox(props: { total_fee_earned = total_earned_fee_x_value .plus(total_earned_fee_y_value) .toFixed(); - total_fee_earned = Big(total_fee_earned) - .plus(unClaimed_tvl_fee) - .toFixed(); } set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 1a12d0189..ebca0b547 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1724,6 +1724,7 @@ export function get_o_l_amount_by_condition({ const second24 = 24 * 60 * 60; export function get_account_24_apr( + unClaimed_fee$: string | number, dclAccountFee: IDCLAccountFee | any, pool: PoolInfo, tokenPriceList: any @@ -1749,7 +1750,9 @@ export function get_account_24_apr( ); const fee_x_24_value = Big(fee_x_24).mul(price_x); const fee_y_24_value = Big(fee_y_24).mul(price_y); - const total_fee_24_value = fee_x_24_value.plus(fee_y_24_value); + const total_fee_24_value = fee_x_24_value + .plus(fee_y_24_value) + .plus(unClaimed_fee$ || 0); // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; @@ -1797,8 +1800,6 @@ export function get_account_24_apr( ); }); const principal = total_processed_log_value.div(second24); - console.log('processed_change_log', processed_change_log); - console.log('processed_change_log-principal', principal.toFixed()); if (principal.gt(0)) { apr_24 = total_fee_24_value.div(principal).mul(365).mul(100).toFixed(); } diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index c62436972..47ed35f5a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1283,7 +1283,7 @@ export const batch_remove_liquidity_contract = async ({ args: { lpt_ids, }, - gas: '250000000000000', + gas: '300000000000000', }, ], }); @@ -1307,7 +1307,7 @@ export const batch_remove_liquidity_contract = async ({ args: { remove_liquidity_infos: batch_remove_liquidity_i, }, - gas: '250000000000000', + gas: '300000000000000', }, ], }); @@ -1335,7 +1335,7 @@ export const batch_remove_liquidity_contract = async ({ { methodName: 'batch_update_liquidity', args: batch_update_liquidity_i, - gas: '250000000000000', + gas: '300000000000000', }, ], }); From 254598cd433e31a286c6cb3bd7ee011667a9f998 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 21 Sep 2023 17:01:12 +0800 Subject: [PATCH 130/204] fix bugs --- src/components/swap/SwapRateChart.tsx | 20 ++++++++++++----- .../Orderly/components/MyOrder/index.tsx | 22 ++++++++++++++++--- 2 files changed, 33 insertions(+), 9 deletions(-) diff --git a/src/components/swap/SwapRateChart.tsx b/src/components/swap/SwapRateChart.tsx index f77d07ed6..6c83d7f66 100644 --- a/src/components/swap/SwapRateChart.tsx +++ b/src/components/swap/SwapRateChart.tsx @@ -350,12 +350,13 @@ export default function SwapRateChart(props: SwapRateChartProps) { tokens={[displayTokenIn, displayTokenOut]} separator="/" /> - - { - setReverseToken(!reverseToken); - }} - /> +
+ { + setReverseToken(!reverseToken); + }} + /> +
@@ -418,6 +419,13 @@ export default function SwapRateChart(props: SwapRateChartProps) { {diff.percent} )} +
+ { + setReverseToken(!reverseToken); + }} + /> +
{diff && (
diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index 8bc8d09bd..928abfe37 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -2208,6 +2208,8 @@ function OrderCard({ const [select_type, set_select_type] = useState<'all' | 'current'>('all'); const [activeOrderList, setActiveOrderList] = useState(); const [historyOrderList, setHistoryOrderList] = useState(); + const [historySwapInfoList, setHistorySwapInfoList] = + useState(); const tokenIds = useMemo(() => { if (pool_id_by_url) { @@ -2244,12 +2246,26 @@ function OrderCard({ } } }, [historyOrder, select_type, pool_id_by_url]); + useEffect(() => { + if (historySwapInfo.length) { + if (select_type == 'all') { + setHistorySwapInfoList(historySwapInfo); + } else { + setHistorySwapInfoList(getCurrentPairSwapOrders(historySwapInfo)); + } + } + }, [historySwapInfo, select_type, pool_id_by_url]); function getCurrentPairOrders(orders: UserOrderInfo[]) { return orders.filter((order: UserOrderInfo) => { return order.pool_id == pool_id_by_url; }); } + function getCurrentPairSwapOrders(orders: HistoryOrderSwapInfo[]) { + return orders.filter((order: HistoryOrderSwapInfo) => { + return order.pool_id == pool_id_by_url; + }); + } function OrderTab() { if (isMobile()) { return ( @@ -2796,9 +2812,9 @@ function OrderCard({ )} {orderType === 'history' && showHistoryInfo && - historySwapInfo && - historySwapInfo.length > 0 && - historySwapInfo + historySwapInfoList && + historySwapInfoList.length > 0 && + historySwapInfoList .sort((a, b) => Number(b.timestamp) - Number(a.timestamp)) .map((sf, i) => { return ( From 101ffae573350e66e5fc23a547466e72e7a9813a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 21 Sep 2023 17:45:49 +0800 Subject: [PATCH 131/204] update style --- src/components/swap/SwapLimitOrderChart.tsx | 26 +++++++++------------ 1 file changed, 11 insertions(+), 15 deletions(-) diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index d4f686370..3b64fa1ed 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -355,7 +355,6 @@ export default function SwapLimitOrderChart() { if (targetPercent) { setZoom(targetPercent); } - console.log('缩小中- targetPercent', targetPercent); } // 放大坐标轴区间范围 function zoomIn() { @@ -1175,29 +1174,25 @@ function OrderChart() { .attr('opacity', '1') .attr('fill', dotFillColor); let translate_x = offsetX + disFromHoverBoxToPointer; + let translate_y = is_mobile + ? offsetY + disFromHoverBoxToPointer + : offsetY - disFromHoverBoxToPointer; if (offsetX > 380) { translate_x = offsetX - 235; } if (is_mobile) { - d3.select('.hoverBox').attr('style', 'display:block'); - } else { - d3.select('.hoverBox').attr( - 'style', - `visibility:visible;transform:translate(${translate_x}px, ${ - offsetY - disFromHoverBoxToPointer - }px)` - ); + translate_x = Math.min(140, translate_x); } + d3.select('.hoverBox').attr( + 'style', + `visibility:visible;transform:translate(${translate_x}px, ${translate_y}px)` + ); } function hideCrossDot() { d3.select('.verticalDashLine').attr('opacity', '0'); d3.select('.horizontalDashLine').attr('opacity', '0'); d3.select('.dot').attr('opacity', '0'); - if (is_mobile) { - d3.select('.hoverBox').attr('style', `display:hidden`); - } else { - d3.select('.hoverBox').attr('style', `visibility:invisible`); - } + d3.select('.hoverBox').attr('style', `visibility:invisible`); } return (
{/* hover上去的悬浮框 */} -
+ {/* lg:invisible xsm:hidden */} +
Side Date: Thu, 21 Sep 2023 21:21:52 +0800 Subject: [PATCH 132/204] fix bugs --- src/services/swapV3.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 47ed35f5a..bf01da423 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -925,7 +925,7 @@ export const batch_add_liquidity = async ({ args: { add_liquidity_infos: liquidityInfos, }, - gas: '150000000000000', + gas: '300000000000000', }, ], }, From d937bec2781647520f92b081a3dccd8bd9327530 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 21 Sep 2023 21:48:06 +0800 Subject: [PATCH 133/204] optimization --- src/components/pool/RemovePoolV3.tsx | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 655954f44..52c515ea2 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -894,10 +894,10 @@ export const RemovePoolV3 = (props: any) => { { const value = e.target.value; setMinBoxPrice(value); @@ -918,14 +918,14 @@ export const RemovePoolV3 = (props: any) => { > { const value = e.target.value; setMaxBoxPrice(value); }} - value={maxBoxPrice} + value={maxBoxPrice !== '' ? toPrecision(maxBoxPrice, 8) : ''} inputMode="decimal" onBlur={() => { handleMaxBoxPriceToAppropriatePoint(); @@ -971,7 +971,7 @@ export const RemovePoolV3 = (props: any) => { pair_is_reverse ? 'flex-row-reverse' : '' }`} > -
+
{ {min_received_x_amount}
-
+
Date: Fri, 22 Sep 2023 16:06:03 +0800 Subject: [PATCH 134/204] fix bugs --- src/components/farm/FarmsDclDetail.tsx | 4 ++-- src/pages/pools/LiquidityPage.tsx | 10 ++++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 446bae8fa..a2023735b 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1466,13 +1466,13 @@ export default function FarmsDclDetail(props: { {' '} available to stake
- {!isEnded && !canStake && ( + {/* {!isEnded && !canStake && (
{get_unavailable_text()}
- )} + )} */}
{!isEnded && ( diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index e7d2d0488..d22fe6e84 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -1768,7 +1768,7 @@ function PoolRowV2({ >
{'$' + toInternationalCurrencySystem(pool.tvl.toString())}
- {/* 缩略图 中文*/} - {!mark && ( + {/* {!mark && (
- )} + )} */}
); @@ -2836,7 +2835,7 @@ function LiquidityPage_({ {activeTab === 'v2' && (
-
+
@@ -2997,7 +2996,6 @@ function LiquidityPage_({ )}
-
{allPoolsV2 From 7fd07284e73a4379390675f367b53206366edd83 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 22 Sep 2023 17:18:49 +0800 Subject: [PATCH 135/204] add tip when too much token amount be entered --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 110 ++++++++++++++++++- 1 file changed, 108 insertions(+), 2 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 4959e7457..71879c321 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1029,6 +1029,7 @@ export default function AddYourLiquidityPageV3() { const slot_number_in_a_bin = SLOT_NUMBER; const binWidth = slot_number_in_a_bin * point_delta; const bin_number_left = (current_point - leftPoint) / binWidth; + const bin_number_right = (rightPoint - current_point) / binWidth; set_token_amount_tip(null); if (liquidityShape == 'Curve') { if (bin_number_left > 1) { @@ -1044,6 +1045,7 @@ export default function AddYourLiquidityPageV3() { const remain_token_x_amount = Big(tokenXAmount).minus( min_token_x_amount_needed ); + if (remain_token_x_amount.lt(0)) { // 给出提示 token x 数量太少不能添加 1 const a = formatWithCommas_for_tip(min_token_x_amount_needed); @@ -1067,6 +1069,32 @@ export default function AddYourLiquidityPageV3() { ); set_token_amount_tip(tip); return; + } else if (bin_number_right < 1) { + // 给出提示 token x 数量太多,给用户提示只需要输入指定数量 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + if (Big(tokenXAmount).gt(a)) { + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + Based on the price range you've selected, you only need to + provide + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } } else { nftList_x = get_decline_pattern_nfts({ left_point: current_r_point, @@ -1115,6 +1143,32 @@ export default function AddYourLiquidityPageV3() { ); set_token_amount_tip(tip); return; + } else if (bin_number_left < 1) { + // 给出提示 token y 数量太多,给用户提示只需要输入指定数量 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + if (Big(tokenYAmount).gt(a)) { + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + Based on the price range you've selected, you only need to + provide + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } } else { nftList_y = get_rise_pattern_nfts({ left_point: leftPoint, @@ -1164,6 +1218,32 @@ export default function AddYourLiquidityPageV3() { ); set_token_amount_tip(tip); return; + } else if (bin_number_right < 1) { + // 给出提示 token x 数量太多,给用户提示只需要输入指定数量 + const a = formatWithCommas_for_tip(min_token_x_amount_needed); + if (Big(tokenXAmount).gt(a)) { + const a_display = formatWithCommas_for_tip( + min_token_x_amount_needed, + true + ); + const tip = ( + + Based on the price range you've selected, you only need to + provide + { + setTokenXAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenX.symbol} + + ); + set_token_amount_tip(tip); + return; + } } else { nftList_x = get_rise_pattern_nfts({ left_point: current_r_point, @@ -1212,6 +1292,32 @@ export default function AddYourLiquidityPageV3() { ); set_token_amount_tip(tip); return; + } else if (bin_number_left < 1) { + // 给出提示 token y 数量太多,给用户提示只需要输入指定数量 + const a = formatWithCommas_for_tip(min_token_y_amount_needed); + if (Big(tokenYAmount).gt(a)) { + const a_display = formatWithCommas_for_tip( + min_token_y_amount_needed, + true + ); + const tip = ( + + Based on the price range you've selected, you only need to + provide + { + setTokenYAmount(a); + }} + className="mx-0.5 cursor-pointer underline" + > + {a_display} + + {tokenY.symbol} + + ); + set_token_amount_tip(tip); + return; + } } else { nftList_y = get_decline_pattern_nfts({ left_point: leftPoint, @@ -1891,8 +1997,8 @@ export default function AddYourLiquidityPageV3() { >
{token_amount_tip ? ( -
- +
+ {token_amount_tip}
) : null} From 175172c83036005cb82c3d7fb77d56245ae99c8d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 23 Sep 2023 21:48:45 +0800 Subject: [PATCH 136/204] fix bugs --- src/components/pool/RemovePoolV3.tsx | 58 +++++++++++++++++++++++++--- 1 file changed, 53 insertions(+), 5 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 52c515ea2..2bad62aa5 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -53,6 +53,9 @@ import { } from '~pages/poolsV3/interfaces'; import DclChart from '../../components/d3Chart/DclChart'; import { isMobile } from '~utils/device'; +import { WarningIcon } from '~components/icon/V3'; +import QuestionMark from '~components/farm/QuestionMark'; +import ReactTooltip from 'react-tooltip'; export type RemoveType = 'left' | 'right' | 'all'; @@ -95,6 +98,8 @@ export const RemovePoolV3 = (props: any) => { const [minBoxPoint, setMinBoxPoint] = useState(); const [maxBoxPoint, setMaxBoxPoint] = useState(); const [binBoxAmount, setBinBoxAmount] = useState(''); + const [show_boundary_tip, set_show_boundary_tip] = useState(false); + const [boundary_is_diff, set_boundary_is_diff] = useState(false); useEffect(() => { // init @@ -118,6 +123,13 @@ export const RemovePoolV3 = (props: any) => { handleBinAmountToAppropriateAmount(+binBoxAmount); } }, [binBoxAmount]); + useEffect(() => { + if (boundary_is_diff && removeType == 'all') { + set_show_boundary_tip(true); + } else { + set_show_boundary_tip(false); + } + }, [boundary_is_diff, removeType]); const [ min_received_x_amount, min_received_y_amount, @@ -206,11 +218,13 @@ export const RemovePoolV3 = (props: any) => { user_points.push(l.left_point, l.right_point); }); user_points.sort((b, a) => b - a); - const min_point = get_bin_point_by_point(user_points[0], 'floor'); - const max_point = get_bin_point_by_point( - user_points[user_points.length - 1], - 'ceil' - ); + const user_min_point = user_points[0]; + const user_max_point = user_points[user_points.length - 1]; + const min_point = get_bin_point_by_point(user_min_point, 'floor'); + const max_point = get_bin_point_by_point(user_max_point, 'ceil'); + if (min_point !== user_min_point || max_point !== user_max_point) { + set_boundary_is_diff(true); + } let min_price, max_price; if (pair_is_reverse) { min_price = reverse_price(get_bin_price_by_point(max_point)); @@ -695,6 +709,12 @@ export const RemovePoolV3 = (props: any) => { .toFixed(); return [min_token_x_amount, min_token_y_amount]; } + function get_boundary_tip() { + const tip = + 'The Min Price and Max price displayed here correspond to the boundaries of the bins. Since you added liquidity before the upgrade, the liquidity boundaries are within the bins containing the Min Price or Max price. Therefore, your actual price range may differ from the the Min Price or Max price displayed here.'; + let result: string = `
${tip}
`; + return result; + } const isRemoveLiquidityDisabled = minBoxPoint == maxBoxPoint; const is_mobile = isMobile(); const cardWidth = is_mobile ? '100vw' : '550px'; @@ -948,6 +968,34 @@ export const RemovePoolV3 = (props: any) => { />
+ {/* Tip */} + {show_boundary_tip ? ( +
+ {/* */} +
+ + +
+ + Why the Min Price or Max price here differ from my actual price + range? + +
+ ) : null} + {/* Slippage */}
Date: Sun, 24 Sep 2023 16:27:52 +0800 Subject: [PATCH 137/204] fix bugs --- src/components/d3Chart/utils.ts | 2 +- src/components/layout/SelectModal.tsx | 13 +++++++++++++ src/locales/en_US.ts | 1 + src/locales/es.ts | 1 + src/locales/ja.ts | 1 + src/locales/ko.ts | 1 + src/locales/ru.ts | 1 + src/locales/uk_UA.ts | 1 + src/locales/vi.ts | 1 + src/locales/zh_CN.ts | 1 + src/pages/pools/LiquidityPage.tsx | 20 ++++++++++++++++---- src/state/pool.ts | 27 ++++++++++++++++++--------- 12 files changed, 56 insertions(+), 14 deletions(-) diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index a5c37850c..9c8213e70 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -79,6 +79,6 @@ export const formatToInternationalCurrencySystem$ = (v: string | number) => { return '$' + toInternationalCurrencySystem(Big(v || 0).toFixed(), 2); }; export const isInvalid = function (v: any) { - if (v === '' || v === undefined || v === null) return true; + if (v === '' || v === undefined || v === null || v === '-') return true; return false; }; diff --git a/src/components/layout/SelectModal.tsx b/src/components/layout/SelectModal.tsx index 0b8cb6f41..cd3bdb773 100644 --- a/src/components/layout/SelectModal.tsx +++ b/src/components/layout/SelectModal.tsx @@ -141,6 +141,19 @@ export const SelectModalV2 = ({ >
+
{ + onSortChange('top_bin_apr'); + setShowModal(false); + }} + > + Top Bin APR (24h) +
); }; diff --git a/src/locales/en_US.ts b/src/locales/en_US.ts index d72065072..ee7e710c7 100644 --- a/src/locales/en_US.ts +++ b/src/locales/en_US.ts @@ -1320,5 +1320,6 @@ const en_US = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(en_US, en_US_in_risks_page); diff --git a/src/locales/es.ts b/src/locales/es.ts index 0c413f3c9..ad9a6a3ca 100644 --- a/src/locales/es.ts +++ b/src/locales/es.ts @@ -1208,5 +1208,6 @@ const es = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(es, es_in_risks_page); diff --git a/src/locales/ja.ts b/src/locales/ja.ts index e717f6d38..3026a0b2f 100644 --- a/src/locales/ja.ts +++ b/src/locales/ja.ts @@ -1175,5 +1175,6 @@ const ja = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(ja, ja_in_risks_page); diff --git a/src/locales/ko.ts b/src/locales/ko.ts index 0f72fe333..cee1e6e31 100644 --- a/src/locales/ko.ts +++ b/src/locales/ko.ts @@ -1166,5 +1166,6 @@ const ko = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(ko, ko_in_risks_page); diff --git a/src/locales/ru.ts b/src/locales/ru.ts index 54ff78a13..aad3717ef 100644 --- a/src/locales/ru.ts +++ b/src/locales/ru.ts @@ -1178,5 +1178,6 @@ const ru = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(ru, ru_in_risks_page); diff --git a/src/locales/uk_UA.ts b/src/locales/uk_UA.ts index c8adc79dd..7ca931546 100644 --- a/src/locales/uk_UA.ts +++ b/src/locales/uk_UA.ts @@ -1149,5 +1149,6 @@ const uk_UA = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(uk_UA, uk_UA_in_risks_page); diff --git a/src/locales/vi.ts b/src/locales/vi.ts index bc2e66131..d8bf4b1b1 100644 --- a/src/locales/vi.ts +++ b/src/locales/vi.ts @@ -1178,5 +1178,6 @@ const vi = { TokenAllocation: 'Token Allocation', netWorthTip: 'Total value of investments in Ref (including claimable rewards) + Total value of Orderly assets + Total value of Burrow assets + Total assets in wallet', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(vi, vi_in_risks_page); diff --git a/src/locales/zh_CN.ts b/src/locales/zh_CN.ts index fc4943865..b2691e919 100644 --- a/src/locales/zh_CN.ts +++ b/src/locales/zh_CN.ts @@ -1255,5 +1255,6 @@ const zh_CN = { TokenAllocation: '代币分布', netWorthTip: 'Ref中的投资总价值(包括可领取奖励)+Orderly资产总价值+ Burrow资产总价值+钱包中的总资产', + top_bin_apr: 'Top Bin APR (24h)', }; export default Object.assign(zh_CN, zh_CN_in_risks_page); diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index d22fe6e84..ef47179ac 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -146,11 +146,10 @@ import { } from '../../services/commonV3'; import { AiFillStar } from 'react-icons/ai'; -import { useTokenPriceList } from '../../state/token'; import { useSeedFarmsByPools } from '../../state/pool'; import { RiArrowRightSLine } from 'react-icons/ri'; -import DclChart from '../../components/d3Chart/DclChart'; +import { formatPercentage } from '../../components/d3Chart/utils'; const HIDE_LOW_TVL = 'REF_FI_HIDE_LOW_TVL'; @@ -514,6 +513,11 @@ function MobilePoolRowV2({ const { ref } = useInView(); const curRowTokens = useTokens([pool.token_x, pool.token_y], tokens); + const displayOfTopBinApr = useDCLTopBinFee({ + pool, + way: 'value', + }); + pool.top_bin_apr = displayOfTopBinApr; const history = useHistory(); @@ -532,6 +536,8 @@ function MobilePoolRowV2({ else if (sortBy === 'fee') return `${calculateFeePercent(value / 100)}%`; else if (sortBy === 'volume_24h') { return geth24volume(); + } else if (sortBy === 'top_bin_apr') { + return value?.toString() == '-' ? '-' : formatPercentage(value); } else return '/'; }; function goDetailV2() { @@ -849,6 +855,8 @@ function MobileLiquidityPage({ const v2 = volumes[p2.pool_id] ? parseFloat(volumes[p2.pool_id]) : 0; + const top1 = +(p1.top_bin_apr == '-' ? '0' : p1.top_bin_apr); + const top2 = +(p2.top_bin_apr == '-' ? '0' : p2.top_bin_apr); if (v2Order === 'desc') { if (v2SortBy === 'tvl') { return tvl2 - tvl1; @@ -856,6 +864,8 @@ function MobileLiquidityPage({ return f2 - f1; } else if (v2SortBy === 'volume_24h') { return v2 - v1; + } else if (v2SortBy == 'top_bin_apr') { + return top2 - top1; } } else if (v2Order === 'asc') { if (v2SortBy === 'tvl') { @@ -864,6 +874,8 @@ function MobileLiquidityPage({ return f1 - f2; } else if (v2SortBy === 'volume_24h') { return v1 - v2; + } else if (v2SortBy == 'top_bin_apr') { + return top1 - top2; } } }; @@ -1495,7 +1507,7 @@ function MobileLiquidityPage({ pool={pool} sortBy={v2SortBy} watched={!!find(watchV2Pools, { pool_id: pool.pool_id })} - key={i + '-mobile-pool-row-v2'} + key={pool.pool_id + '-mobile-pool-row-v2'} h24volume={volumes[pool.pool_id]} relatedSeed={do_farms_v2_poos[pool.pool_id]} /> @@ -3004,7 +3016,7 @@ function LiquidityPage_({ .map((pool, i) => ( { +export const useDCLTopBinFee = ({ + pool, + way, +}: { + pool: PoolInfo; + way?: 'value' | 'display'; +}) => { const [topBinApr, setTopBinApr] = useState('-'); useEffect(() => { if (!pool) return; @@ -1442,14 +1448,17 @@ export const useDCLTopBinFee = ({ pool }: { pool: PoolInfo }) => { end_point, }).then((res) => { if (!res || ONLY_ZEROS.test(res.total_liquidity)) return; - const apr = formatPercentage( - new Big(res.total_fee) - .div(res.total_liquidity) - .mul(365) - .mul(100) - .toFixed() - ); - setTopBinApr(apr); + const apr = new Big(res.total_fee) + .div(res.total_liquidity) + .mul(365) + .mul(100) + .toFixed(); + const apr_display = formatPercentage(apr); + if (way == 'value') { + setTopBinApr(apr); + } else { + setTopBinApr(apr_display); + } }); }, [pool]); From 62ee31cc867bc9fe3f7806f7fd96ef0697dd0fc4 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 00:00:22 +0800 Subject: [PATCH 138/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 2 +- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 252 ++++++------------ src/pages/poolsV3/components/add/Icon 6.tsx | 20 -- src/pages/poolsV3/components/add/Icon.tsx | 38 +++ .../components/add/IntegerInputComponent.tsx | 4 +- .../components/add/NoDataComponent.tsx | 68 ++--- .../poolsV3/components/add/PairComponent.tsx | 84 ++++++ .../components/add/PointInputComponent.tsx | 4 +- .../components/add/PointsComponent.tsx | 147 +++++----- .../poolsV3/components/add/SelectFeeTiers.tsx | 146 ++++++++++ 10 files changed, 465 insertions(+), 300 deletions(-) delete mode 100644 src/pages/poolsV3/components/add/Icon 6.tsx create mode 100644 src/pages/poolsV3/components/add/Icon.tsx create mode 100644 src/pages/poolsV3/components/add/PairComponent.tsx create mode 100644 src/pages/poolsV3/components/add/SelectFeeTiers.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index eb54f8ac3..a50d640ea 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1494,7 +1494,7 @@ export default function DclChart({ return ( <>
([]); const [pair_is_reverse, set_pair_is_reverse] = useState(false); + const [show_chart, set_show_chart] = useState(true); // callBack handle useAddLiquidityUrlHandle(); @@ -1865,6 +1868,8 @@ export default function AddYourLiquidityPageV3() { token_amount_tip, set_token_amount_tip, switch_pool_loading, + currentPools, + switchSelectedFee, get_y_nfts_contain_current_curve, get_x_nfts_contain_current_curve, @@ -1877,11 +1882,14 @@ export default function AddYourLiquidityPageV3() { getLiquiditySpot, getLiquidityForCurveAndBidAskMode, + + show_chart, + set_show_chart, }} >
{/* head */}
{/* content */} -
+
-
+ {/* only mobile */} + {mobileDevice ? ( + <> + + + ) : null} + {/* left area */} +
{/* no Data */} {(currentSelectedPool && !currentSelectedPool.pool_id && @@ -1913,7 +1928,6 @@ export default function AddYourLiquidityPageV3() { ) : null} - {/* left area */} {currentSelectedPool && currentSelectedPool.pool_id ? ( ) : null} @@ -1922,56 +1936,68 @@ export default function AddYourLiquidityPageV3() { {/* right area */}
-
-
- + {!mobileDevice ? ( +
+
+ +
+ + { + setTokenX(token); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={ +
{ + if (!mobileDevice) { + setSelectHover(true); + } + }} + onMouseLeave={() => { + if (!mobileDevice) { + setSelectHover(false); + } + }} + onClick={() => { + if (mobileDevice) { + setSelectHover(!selectHover); + } + }} + onBlur={() => { + if (mobileDevice) { + setSelectHover(false); + } + }} + > + +
+ } + /> +
+ ) : null} + {mobileDevice ? ( +
+ Input Token Amount
+ ) : null} - { - setTokenX(token); - }} - selectTokenOut={(token: TokenMetadata) => { - setTokenY(token); - }} - notNeedSortToken={true} - className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " - selected={ -
{ - if (!mobileDevice) { - setSelectHover(true); - } - }} - onMouseLeave={() => { - if (!mobileDevice) { - setSelectHover(false); - } - }} - onClick={() => { - if (mobileDevice) { - setSelectHover(!selectHover); - } - }} - onBlur={() => { - if (mobileDevice) { - setSelectHover(false); - } - }} - > - -
- } - /> -
) : null} - -
-
- -
- -
- - {!!currentSelectedPool?.fee - ? `${currentSelectedPool.fee / 10000}%` - : ''} - - -
{ - // if (mobileDevice) return; - setHoverFeeBox(false); - }} - onMouseEnter={() => { - if (mobileDevice) return; - setHoverFeeBox(true); - }} - onClick={() => { - if (mobileDevice) { - setHoverFeeBox(!hoverFeeBox); - } - }} - > -
- -
- {hoverFeeBox && ( -
-
-
- -
-
- {FEELIST.map((feeItem, index) => { - const { fee, text } = feeItem; - const isNoPool = - currentPools && !currentPools[fee]; - return ( -
{ - switchSelectedFee(fee); - }} - key={fee + index} - className={`relative xsm:w-full flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ - tokenX && tokenY ? 'cursor-pointer' : '' - } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ - isNoPool - ? 'border border-v3GreyColor' - : currentSelectedPool?.fee == fee - ? 'bg-feeBoxBgLiqudityColor' - : 'bg-v3GreyColor' - }`} - > - - {fee / 10000}% - - {tokenX && tokenY && currentPools ? ( - - {isNoPool ? ( - 'No Pool' - ) : Object.keys(tokenPriceList).length > - 0 ? ( - - {displayTvl(currentPools[fee].tvl)} - - ) : ( - 'Loading...' - )} - - ) : null} - {currentSelectedPool?.fee == fee ? ( - - ) : null} -
- ); - })} -
-
-
- )} -
-
-
- -
+ {!mobileDevice ? : null} +
-
+
- ) : ( -
- ); -} diff --git a/src/pages/poolsV3/components/add/Icon.tsx b/src/pages/poolsV3/components/add/Icon.tsx new file mode 100644 index 000000000..d397e338b --- /dev/null +++ b/src/pages/poolsV3/components/add/Icon.tsx @@ -0,0 +1,38 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +export function ArrowDown() { + return ( + + + + ); +} + +export function ChartIcon() { + return ( + + + + ); +} diff --git a/src/pages/poolsV3/components/add/IntegerInputComponent.tsx b/src/pages/poolsV3/components/add/IntegerInputComponent.tsx index 2189322f1..b5ce7c1b5 100644 --- a/src/pages/poolsV3/components/add/IntegerInputComponent.tsx +++ b/src/pages/poolsV3/components/add/IntegerInputComponent.tsx @@ -27,10 +27,10 @@ export function IntegerInputComponent({ }; return ( -
+
{/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - if (isSignedIn) { - setChartTab('yours'); - } - }} - > - Yours - -
-
- Oops! The Pool doesn’t exist +
+
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + if (isSignedIn) { + setChartTab('yours'); + } + }} + > + Yours + +
+
+ Oops! The Pool doesn’t exist +
{/* set price range area */} diff --git a/src/pages/poolsV3/components/add/PairComponent.tsx b/src/pages/poolsV3/components/add/PairComponent.tsx new file mode 100644 index 000000000..ff861c393 --- /dev/null +++ b/src/pages/poolsV3/components/add/PairComponent.tsx @@ -0,0 +1,84 @@ +import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; +import { FormattedMessage } from 'react-intl'; +import { ArrowDown, ChartIcon } from './Icon'; +import { TokenMetadata } from '~services/ft-contract'; +import { SelectTokenDCL } from '../../../../components/forms/SelectToken'; +import { LiquidityProviderData } from '../../AddYourLiquidityPageV3'; + +export function PairComponent() { + const { + tokenX, + tokenY, + setTokenX, + setTokenY, + pair_is_reverse, + show_chart, + set_show_chart, + } = useContext(LiquidityProviderData); + return ( +
+
+ {!tokenX || !tokenY ? ( + + + + ) : ( +
+
+ + +
+
+ {tokenX.symbol}-{tokenY.symbol} +
+
+ )} + + { + setTokenX(token); + }} + selectTokenOut={(token: TokenMetadata) => { + setTokenY(token); + }} + notNeedSortToken={true} + className="pt-6 absolute top-5 outline-none right-0 xs:text-white xs:font-bold xs:fixed xs:bottom-0 xs:w-full " + selected={} + /> +
+
{ + set_show_chart(!show_chart); + }} + > + +
+
+ ); +} diff --git a/src/pages/poolsV3/components/add/PointInputComponent.tsx b/src/pages/poolsV3/components/add/PointInputComponent.tsx index 097bda282..38733a3e7 100644 --- a/src/pages/poolsV3/components/add/PointInputComponent.tsx +++ b/src/pages/poolsV3/components/add/PointInputComponent.tsx @@ -15,12 +15,12 @@ export function PointInputComponent({ disbaled, }: any) { return ( -
+
{ diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx index 1ed85b59d..7043f1a6e 100644 --- a/src/pages/poolsV3/components/add/PointsComponent.tsx +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -44,6 +44,7 @@ export function PointsComponent() { onlyAddYToken, invalidRange, currentPoint, + show_chart, } = useContext(LiquidityProviderData); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' @@ -320,85 +321,91 @@ export function PointsComponent() { } const is_mobile = isMobile(); return ( -
+
{/* chart area */} -
-
- { - setChartTab('liquidity'); - }} - className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ - chartTab == 'liquidity' - ? 'text-black bg-gradientFromHover' - : 'text-primaryText' - }`} - > - Liquidity - - { - if (isSignedIn) { - setChartTab('yours'); - } - }} - > - Yours - -
-
- {!isInvalid(leftPoint) && +
+
+
+ { + setChartTab('liquidity'); + }} + className={`w-20 frcc text-xs gotham_bold px-3 py-1.5 rounded-md cursor-pointer ${ + chartTab == 'liquidity' + ? 'text-black bg-gradientFromHover' + : 'text-primaryText' + }`} + > + Liquidity + + { + if (isSignedIn) { + setChartTab('yours'); + } + }} + > + Yours + +
+
+ {!isInvalid(leftPoint) && + !isInvalid(rightPoint) && + !switch_pool_loading && ( + + )} +
+ {isSignedIn && + !isInvalid(leftPoint) && !isInvalid(rightPoint) && !switch_pool_loading && ( - +
+ +
)}
- {isSignedIn && - !isInvalid(leftPoint) && - !isInvalid(rightPoint) && - !switch_pool_loading && ( -
- -
- )}
{/* set price range area */} -
+
{/* price range mode area */}
-
+
(false); + const { + tokenX, + tokenY, + show_chart, + currentSelectedPool, + currentPools, + switchSelectedFee, + tokenPriceList, + } = useContext(LiquidityProviderData); + function displayTvl(tvl: any) { + if (!tokenPriceList) { + return '-'; + } else if (!tvl || +tvl == 0) { + return '$0'; + } else if (+tvl < 1) { + return '<$1'; + } else { + return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; + } + } + const mobileDevice = isMobile(); + return ( +
+
+ +
+ +
+ + {!!currentSelectedPool?.fee + ? `${currentSelectedPool.fee / 10000}%` + : ''} + + +
{ + // if (mobileDevice) return; + setHoverFeeBox(false); + }} + onMouseEnter={() => { + if (mobileDevice) return; + setHoverFeeBox(true); + }} + onClick={() => { + if (mobileDevice) { + setHoverFeeBox(!hoverFeeBox); + } + }} + > +
+ +
+ {hoverFeeBox && ( +
+
+
+ +
+
+ {FEELIST.map((feeItem, index) => { + const { fee, text } = feeItem; + const isNoPool = currentPools && !currentPools[fee]; + return ( +
{ + switchSelectedFee(fee); + }} + key={fee + index} + className={`relative xsm:w-full flex flex-col px-2 py-1.5 xsm:py-1 rounded-lg w-1 flex-grow ${ + tokenX && tokenY ? 'cursor-pointer' : '' + } ${index == 3 ? '' : 'mr-2.5 xsm:mr-1'} ${ + isNoPool + ? 'border border-v3GreyColor' + : currentSelectedPool?.fee == fee + ? 'bg-feeBoxBgLiqudityColor' + : 'bg-v3GreyColor' + }`} + > + + {fee / 10000}% + + {tokenX && tokenY && currentPools ? ( + + {isNoPool ? ( + 'No Pool' + ) : Object.keys(tokenPriceList).length > 0 ? ( + {displayTvl(currentPools[fee].tvl)} + ) : ( + 'Loading...' + )} + + ) : null} + {currentSelectedPool?.fee == fee ? ( + + ) : null} +
+ ); + })} +
+
+
+ )} +
+
+
+ ); +} From 4f06e1ccfd2b57fabf4b81bab9f66c78c090e22f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 11:21:04 +0800 Subject: [PATCH 139/204] fix bugs --- src/components/d3Chart/DclChart.tsx | 33 +++------- .../components/detail/YourLiquidityBox.tsx | 42 +++++-------- src/services/commonV3.ts | 63 +++++++++++++++++-- 3 files changed, 85 insertions(+), 53 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index a50d640ea..bc379f4b9 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -21,6 +21,7 @@ import { get_account_24_apr, divide_liquidities_into_bins_pool, reverse_price, + get_total_earned_fee, } from '../../services/commonV3'; import { getDclPoolPoints, getDCLAccountFee } from '../../services/indexer'; import { sortBy, debounce } from 'lodash'; @@ -338,8 +339,6 @@ export default function DclChart({ let total_value = Big(0); let total_fee_earned = Big(0); let apr_24 = ''; - let total_earned_fee_x = '0'; - let total_earned_fee_y = '0'; if (dcl_fee_result) { // total unClaimed fee const [ @@ -350,27 +349,15 @@ export default function DclChart({ const dclAccountFee: IDCLAccountFee = dcl_fee_result; const { total_earned_fee } = dclAccountFee; // total earned fee - const { total_fee_x, total_fee_y } = total_earned_fee || {}; - total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - total_earned_fee_x = Big(total_earned_fee_x) - .plus(unClaimed_amount_x_fee) - .toFixed(); - - total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - total_earned_fee_y = Big(total_earned_fee_y) - .plus(unClaimed_amount_y_fee) - .toFixed(); - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value.plus( - total_earned_fee_y_value - ); + const { total_fee_earned_money } = get_total_earned_fee({ + total_earned_fee, + token_x_metadata, + token_y_metadata, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + tokenPriceList, + }); + total_fee_earned = Big(total_fee_earned_money); // 24h profit apr_24 = formatPercentage( get_account_24_apr( diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 8414a7a7c..4dd588c15 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -23,6 +23,7 @@ import { get_pool_name, openUrl, get_account_24_apr, + get_total_earned_fee, get_token_amount_in_user_liquidities, } from '~services/commonV3'; import { Seed } from '../../../../services/farm'; @@ -167,33 +168,24 @@ export function YourLiquidityBox(props: { tokenPriceList ); // total earned fee - const { total_fee_x, total_fee_y } = dcl_fee_result.total_earned_fee; - total_earned_fee_x = toReadableNumber( - token_x_metadata.decimals, - Big(total_fee_x || 0).toFixed() - ); - total_earned_fee_x = Big(total_earned_fee_x) - .plus(unClaimed_amount_x_fee) - .toFixed(); - - total_earned_fee_y = toReadableNumber( - token_y_metadata.decimals, - Big(total_fee_y || 0).toFixed() - ); - total_earned_fee_y = Big(total_earned_fee_y) - .plus(unClaimed_amount_y_fee) - .toFixed(); - - const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; - const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); - const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); - total_fee_earned = total_earned_fee_x_value - .plus(total_earned_fee_y_value) - .toFixed(); + const { + total_earned_fee_x_amount, + total_earned_fee_y_amount, + total_fee_earned_money, + } = get_total_earned_fee({ + total_earned_fee: dcl_fee_result.total_earned_fee, + token_x_metadata, + token_y_metadata, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + tokenPriceList, + }); + total_fee_earned = total_fee_earned_money; + total_earned_fee_x = total_earned_fee_x_amount; + total_earned_fee_y = total_earned_fee_y_amount; } - set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); set_earned_fee_x_amount(formatNumber(total_earned_fee_x)); + set_earned_fee_y_amount(formatNumber(total_earned_fee_y)); set_earned_fee(formatWithCommas_usd(total_fee_earned)); setAccountAPR(formatPercentage(apr_24)); } diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index ebca0b547..9dd727bd0 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1737,9 +1737,8 @@ export function get_account_24_apr( // 24小时平均利润 const { fee_data, user_token, change_log_data } = apr; const { fee_x, fee_y } = fee_data; - // 针对后端接口 fee_x、fee_y 会有负值处理成0 - const fee_x_final = Big(fee_x || 0).lt(0) ? 0 : fee_x; - const fee_y_final = Big(fee_y || 0).lt(0) ? 0 : fee_y; + const fee_x_final = Big(fee_x).abs(); + const fee_y_final = Big(fee_y).abs(); const fee_x_24 = toReadableNumber( token_x_metadata.decimals, Big(fee_x_final || 0).toFixed() @@ -1748,8 +1747,15 @@ export function get_account_24_apr( token_y_metadata.decimals, Big(fee_y_final || 0).toFixed() ); - const fee_x_24_value = Big(fee_x_24).mul(price_x); - const fee_y_24_value = Big(fee_y_24).mul(price_y); + let fee_x_24_value = Big(fee_x_24).mul(price_x); + let fee_y_24_value = Big(fee_y_24).mul(price_y); + if (Big(fee_x).lt(0)) { + fee_x_24_value = fee_x_24_value.neg(); + } + if (Big(fee_y).lt(0)) { + fee_y_24_value = fee_y_24_value.neg(); + } + const total_fee_24_value = fee_x_24_value .plus(fee_y_24_value) .plus(unClaimed_fee$ || 0); @@ -1806,6 +1812,53 @@ export function get_account_24_apr( return apr_24; } +export function get_total_earned_fee({ + total_earned_fee, + token_x_metadata, + token_y_metadata, + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + tokenPriceList, +}: any) { + let total_earned_fee_x; + let total_earned_fee_y; + let total_fee_earned; + // total earned fee + const { total_fee_x, total_fee_y } = total_earned_fee || {}; + const total_fee_x_final = Big(total_fee_x || 0).abs(); + const total_fee_y_final = Big(total_fee_y || 0).abs(); + + total_earned_fee_x = toReadableNumber( + token_x_metadata.decimals, + total_fee_x_final.toFixed() + ); + total_earned_fee_x = Big(total_fee_x || 0).lt(0) + ? Big(total_earned_fee_x).neg() + : Big(total_earned_fee_x); + total_earned_fee_x = total_earned_fee_x.plus(unClaimed_amount_x_fee); + + total_earned_fee_y = toReadableNumber( + token_y_metadata.decimals, + total_fee_y_final.toFixed() + ); + total_earned_fee_y = Big(total_fee_y || 0).lt(0) + ? Big(total_earned_fee_y).neg() + : Big(total_earned_fee_y); + total_earned_fee_y = total_earned_fee_y.plus(unClaimed_amount_y_fee); + + const price_x = tokenPriceList[token_x_metadata.id]?.price || 0; + const price_y = tokenPriceList[token_y_metadata.id]?.price || 0; + const total_earned_fee_x_value = Big(total_earned_fee_x).mul(price_x); + const total_earned_fee_y_value = Big(total_earned_fee_y).mul(price_y); + total_fee_earned = total_earned_fee_x_value + .plus(total_earned_fee_y_value) + .toFixed(); + return { + total_earned_fee_x_amount: total_earned_fee_x?.toFixed(), + total_earned_fee_y_amount: total_earned_fee_y?.toFixed(), + total_fee_earned_money: total_fee_earned, + }; +} /** * * @param log 这笔log的本金 From 22bc658c342501b58d6e61e4c6482e3532cd0964 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 15:08:47 +0800 Subject: [PATCH 140/204] update --- src/locales/vi.ts | 2 +- src/services/commonV3.ts | 9 +++++---- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/locales/vi.ts b/src/locales/vi.ts index b789d0c75..3def56a82 100644 --- a/src/locales/vi.ts +++ b/src/locales/vi.ts @@ -1375,7 +1375,7 @@ const vi = { bridge_pure: 'Cầu nối', pool_refresh: 'Có sự cố với máy chủ, vui lòng thử lại sau.', netWorthTip: - 'Tổng giá trị của đầu tư trong Ref (bao gồm phần thưởng có thể yêu cầu) + Tổng giá trị tài sản Orderly + Tổng giá trị tài sản Burrow + Tổng tài sản trong ví', + 'Tổng giá trị của đầu tư trong Ref (bao gồm phần thưởng có thể yêu cầu) + Tổng giá trị tài sản Orderly + Tổng giá trị tài sản Burrow + Tổng tài sản trong ví', top_bin_apr: 'Top Bin APR (24h)', mark_price: 'Giá đánh dấu', last_price: 'Giá cuối cùng', diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 9dd727bd0..93759d80a 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1749,11 +1749,12 @@ export function get_account_24_apr( ); let fee_x_24_value = Big(fee_x_24).mul(price_x); let fee_y_24_value = Big(fee_y_24).mul(price_y); + debugger; if (Big(fee_x).lt(0)) { - fee_x_24_value = fee_x_24_value.neg(); + fee_x_24_value = Big(-fee_x_24_value.toNumber()); } if (Big(fee_y).lt(0)) { - fee_y_24_value = fee_y_24_value.neg(); + fee_y_24_value = Big(-fee_y_24_value.toNumber()); } const total_fee_24_value = fee_x_24_value @@ -1833,7 +1834,7 @@ export function get_total_earned_fee({ total_fee_x_final.toFixed() ); total_earned_fee_x = Big(total_fee_x || 0).lt(0) - ? Big(total_earned_fee_x).neg() + ? Big(-Number(total_earned_fee_x)) : Big(total_earned_fee_x); total_earned_fee_x = total_earned_fee_x.plus(unClaimed_amount_x_fee); @@ -1842,7 +1843,7 @@ export function get_total_earned_fee({ total_fee_y_final.toFixed() ); total_earned_fee_y = Big(total_fee_y || 0).lt(0) - ? Big(total_earned_fee_y).neg() + ? Big(-Number(total_earned_fee_y)) : Big(total_earned_fee_y); total_earned_fee_y = total_earned_fee_y.plus(unClaimed_amount_y_fee); From d5386e0caa7b8434b3d6d4d599ad4d7465c723b3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 16:23:34 +0800 Subject: [PATCH 141/204] fix bugs --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 6 +---- .../components/add/NoDataComponent.tsx | 10 +++++-- .../components/add/PointsComponent.tsx | 12 ++++----- .../poolsV3/components/add/SelectFeeTiers.tsx | 4 +-- .../poolsV3/components/detail/DetailFun.ts | 27 +++++++++++-------- src/services/commonV3.ts | 3 +-- src/services/pool.ts | 1 - 7 files changed, 33 insertions(+), 30 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 1b05fab1a..e5e288d31 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -1989,11 +1989,7 @@ export default function AddYourLiquidityPageV3() {
) : null} {mobileDevice ? ( -
+
Input Token Amount
) : null} diff --git a/src/pages/poolsV3/components/add/NoDataComponent.tsx b/src/pages/poolsV3/components/add/NoDataComponent.tsx index 1c8287f6a..963edc7e7 100644 --- a/src/pages/poolsV3/components/add/NoDataComponent.tsx +++ b/src/pages/poolsV3/components/add/NoDataComponent.tsx @@ -1,18 +1,24 @@ import React, { useEffect, useState, useContext, useMemo, useRef } from 'react'; import { FormattedMessage } from 'react-intl'; import { WalletContext } from '../../../../utils/wallets-integration'; +import { LiquidityProviderData } from '../../AddYourLiquidityPageV3'; export function NoDataComponent() { const [chartTab, setChartTab] = useState<'liquidity' | 'yours'>('liquidity'); const [priceRangeMode, setPriceRangeMode] = useState< 'by_range' | 'by_radius' >('by_range'); + const { show_chart } = useContext(LiquidityProviderData); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; return (
{/* chart area */} -
+
{/* set price range area */} -
+
{/* price range mode area */}
diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx index 7043f1a6e..36d32e63e 100644 --- a/src/pages/poolsV3/components/add/PointsComponent.tsx +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -321,13 +321,13 @@ export function PointsComponent() { } const is_mobile = isMobile(); return ( -
+
{/* chart area */} -
+
{ const { unclaimed_fee_x, unclaimed_fee_y } = liquidity; const unclaimed_fee_x_amount = toReadableNumber( @@ -24,12 +25,16 @@ export function get_unClaimed_fee_data( ); const token_x_price = tokenPriceList[token_x_metadata.id]?.price || 0; const token_y_price = tokenPriceList[token_y_metadata.id]?.price || 0; - const total_fees_price = - Number(unclaimed_fee_x_amount) * Number(token_x_price) + - Number(unclaimed_fee_y_amount) * Number(token_y_price); - total_amount_x_fee += Number(unclaimed_fee_x_amount); - total_amount_y_fee += Number(unclaimed_fee_y_amount); - total_tvl_fee += Number(total_fees_price); + const total_fees_price = Big(unclaimed_fee_x_amount) + .mul(token_x_price) + .plus(Big(unclaimed_fee_y_amount).mul(token_y_price)); + total_amount_x_fee = Big(total_amount_x_fee).plus(unclaimed_fee_x_amount); + total_amount_y_fee = Big(total_amount_y_fee).plus(unclaimed_fee_y_amount); + total_tvl_fee = Big(total_tvl_fee).plus(total_fees_price); }); - return [total_tvl_fee, total_amount_x_fee, total_amount_y_fee]; + return [ + total_tvl_fee.toNumber(), + total_amount_x_fee.toNumber(), + total_amount_y_fee.toNumber(), + ]; } diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 93759d80a..dd90c3fee 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1749,14 +1749,13 @@ export function get_account_24_apr( ); let fee_x_24_value = Big(fee_x_24).mul(price_x); let fee_y_24_value = Big(fee_y_24).mul(price_y); - debugger; if (Big(fee_x).lt(0)) { fee_x_24_value = Big(-fee_x_24_value.toNumber()); } if (Big(fee_y).lt(0)) { fee_y_24_value = Big(-fee_y_24_value.toNumber()); } - + const t = fee_x_24_value.plus(fee_y_24_value).toFixed(); const total_fee_24_value = fee_x_24_value .plus(fee_y_24_value) .plus(unClaimed_fee$ || 0); diff --git a/src/services/pool.ts b/src/services/pool.ts index 972aaf292..130d3ab8c 100644 --- a/src/services/pool.ts +++ b/src/services/pool.ts @@ -1136,7 +1136,6 @@ export const removeLiquidityByTokensFromStablePool = async ({ tokens, unregister = false, }: RemoveLiquidityByTokensFromStablePoolOptions) => { - debugger; const tokenIds = tokens.map((token) => token.id); const withDrawTransactions: Transaction[] = []; From ea157634cb5c711a50bd5ce261d7de70e1658344 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 16:58:54 +0800 Subject: [PATCH 142/204] fix bugs --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 +--- .../poolsV3/components/add/PointsComponent.tsx | 5 ----- .../components/detail/NoYourLiquditiesBox.tsx | 16 ++++++++++++---- src/services/commonV3.ts | 1 - 4 files changed, 13 insertions(+), 13 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index e5e288d31..4c7c68085 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -17,7 +17,6 @@ import { list_pools, PoolInfo } from '../../services/swapV3'; import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; import { CONSTANT_D, - FEELIST, DEFAULTSELECTEDFEE, useAddLiquidityUrlHandle, get_pool_id, @@ -42,7 +41,6 @@ import { isMobile } from '../../utils/device'; import { SelectedIcon, ArrowDownV3 } from '../../components/icon/swapV3'; import Big from 'big.js'; import { SelectTokenDCL } from '../../components/forms/SelectToken'; -import { SliderCurColor } from '~components/icon/Info'; import { CurveShape, SpotShape, @@ -2072,7 +2070,7 @@ export default function AddYourLiquidityPageV3() { {index === 0 && ( )} diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx index 36d32e63e..1e9c063e0 100644 --- a/src/pages/poolsV3/components/add/PointsComponent.tsx +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -111,11 +111,6 @@ export function PointsComponent() { pointChange({ leftPoint, rightPoint }); } } - console.log( - '00000000--leftPoint, rightPoint--00000000', - leftPoint, - rightPoint - ); }, [leftPoint, rightPoint, BIN_WIDTH]); useEffect(() => { diff --git a/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx b/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx index 84037fc26..d4f977f47 100644 --- a/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx +++ b/src/pages/poolsV3/components/detail/NoYourLiquditiesBox.tsx @@ -1,23 +1,31 @@ import React from 'react'; import { useHistory } from 'react-router'; -import { TOKEN_LIST_FOR_RATE, get_pool_name } from '~services/commonV3'; +import { get_pool_name, sort_tokens_by_base } from '~services/commonV3'; import { GradientButton } from '~components/button/Button'; -import { FormattedMessage, useIntl } from 'react-intl'; +import { FormattedMessage } from 'react-intl'; import { NoLiquidityIcon } from '../../../../components/icon/V3'; export function NoYourLiquditiesBox(props: any) { const { poolDetail } = props; - const { token_x_metadata, pool_id } = poolDetail; + const { pool_id } = poolDetail; const history = useHistory(); function goAddLiqudityPage() { const [token_x, token_y, fee] = pool_id.split('|'); let url_hash = pool_id; - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { + if (!is_reverse_fun()) { url_hash = `${token_y}|${token_x}|${fee}`; } const pool_name = get_pool_name(url_hash); history.push(`/addLiquidityV2#${pool_name}`); } + function is_reverse_fun() { + const { token_x_metadata, token_y_metadata } = poolDetail; + if (token_x_metadata && token_y_metadata) { + const tokens = sort_tokens_by_base([token_x_metadata, token_y_metadata]); + return tokens[0].id !== token_x_metadata.id; + } + return false; + } return (
diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index dd90c3fee..548759489 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1755,7 +1755,6 @@ export function get_account_24_apr( if (Big(fee_y).lt(0)) { fee_y_24_value = Big(-fee_y_24_value.toNumber()); } - const t = fee_x_24_value.plus(fee_y_24_value).toFixed(); const total_fee_24_value = fee_x_24_value .plus(fee_y_24_value) .plus(unClaimed_fee$ || 0); From 7340b00f58b4bcbd50ec2e5e2cc28c7f0fe40f20 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 25 Sep 2023 17:38:41 +0800 Subject: [PATCH 143/204] optimization --- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 29 ++-- .../poolsV3/components/add/PairComponent.tsx | 5 +- .../components/detail/SelectLiquidityBox.tsx | 126 +----------------- 3 files changed, 15 insertions(+), 145 deletions(-) diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 4c7c68085..5ba45c1bc 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -90,9 +90,7 @@ export default function AddYourLiquidityPageV3() { const [tokenXBalanceFromNear, setTokenXBalanceFromNear] = useState(); const [tokenYBalanceFromNear, setTokenYBalanceFromNear] = useState(); - const [feeBoxStatus, setFeeBoxStatus] = useState(true); const [selectHover, setSelectHover] = useState(false); - const [hoverFeeBox, setHoverFeeBox] = useState(false); const [binNumber, setBinNumber] = useState(); const [liquidityShape, setLiquidityShape] = useState('Spot'); @@ -376,8 +374,19 @@ export default function AddYourLiquidityPageV3() { if (tokenx_id && tokeny_id && pool_fee) { const tokenx = await ftGetTokenMetadata(tokenx_id); const tokeny = await ftGetTokenMetadata(tokeny_id); - setTokenX(tokenx); - setTokenY(tokeny); + listPool.find((pool: PoolInfo) => { + const { token_x, token_y } = pool; + if (token_x == tokenx.id && token_y == tokeny.id) { + setTokenX(tokenx); + setTokenY(tokeny); + return true; + } + if (token_x == tokeny.id && token_y == tokenx.id) { + setTokenX(tokeny); + setTokenY(tokenx); + return true; + } + }); } } function searchPools() { @@ -565,18 +574,6 @@ export default function AddYourLiquidityPageV3() { token_y: tokenY, }; } - - function displayTvl(tvl: any) { - if (!tokenPriceList) { - return '-'; - } else if (!tvl || +tvl == 0) { - return '$0'; - } else if (+tvl < 1) { - return '<$1'; - } else { - return `$${toInternationalCurrencySystem(tvl.toString(), 0)}`; - } - } /** * curve 模式下,右侧(x)包含当前点位的 nfts划分 * 此区间bin的数量要求 > 1 diff --git a/src/pages/poolsV3/components/add/PairComponent.tsx b/src/pages/poolsV3/components/add/PairComponent.tsx index ff861c393..962286c54 100644 --- a/src/pages/poolsV3/components/add/PairComponent.tsx +++ b/src/pages/poolsV3/components/add/PairComponent.tsx @@ -20,10 +20,7 @@ export function PairComponent() {
{!tokenX || !tokenY ? ( - + Select Token Pair ) : (
diff --git a/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx b/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx index d4ba195f2..aee33f9e5 100644 --- a/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/SelectLiquidityBox.tsx @@ -1,21 +1,6 @@ import React, { useEffect, useState } from 'react'; -import { useHistory } from 'react-router'; -import { UserLiquidityDetail } from './type'; -import { BigNumber } from 'bignumber.js'; -import { toPrecision, formatWithCommas } from '../../../../utils/numbers'; -import { isClientMobie } from '../../../../utils/device'; -import { - UserLiquidityInfo, - TOKEN_LIST_FOR_RATE, - displayNumberToAppropriateDecimals, - get_pool_name, - openUrl, -} from '~services/commonV3'; -import { FormattedMessage } from 'react-intl'; -import getConfig from '../../../../services/config'; -import { FarmBoost, Seed } from '../../../../services/farm'; +import { UserLiquidityInfo } from '~services/commonV3'; import { RemovePoolV3 } from '~components/pool/RemovePoolV3'; -const { REF_UNI_V3_SWAP_CONTRACT_ID, DCL_POOL_BLACK_LIST } = getConfig(); export function SelectLiquidityBox(props: any) { const { @@ -25,125 +10,16 @@ export function SelectLiquidityBox(props: any) { operation, tokenPriceList, user_liquidities, - matched_seeds, } = props; const [hoverHashId, setHoverHashId] = useState(''); - const [showRemoveBox, setShowRemoveBox] = useState(false); - const [showAddBox, setShowAddBox] = useState(false); - const history = useHistory(); const { token_x_metadata, token_y_metadata } = poolDetail; - function displayLiqudityTvl(liquidityDetail: UserLiquidityDetail) { - const total = +liquidityDetail.total_liqudities_price; - if (total == 0) { - return '$0'; - } else if (total < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total.toString(), 2)); - } - } - function displayLiqudityFee(liquidityDetail: UserLiquidityDetail) { - const total = +liquidityDetail.total_fees_price; - if (total == 0) { - return '$0'; - } else if (total < 0.01) { - return '<$0.01'; - } else { - return '$' + formatWithCommas(toPrecision(total.toString(), 2)); - } - } - function displayRange(liquidityDetail: UserLiquidityDetail) { - const { l_price, r_price } = liquidityDetail; - let display_l; - let display_r; - if ( - TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1 && - +r_price !== 0 && - +l_price !== 0 - ) { - display_l = new BigNumber(1).dividedBy(r_price).toFixed(); - display_r = new BigNumber(1).dividedBy(l_price).toFixed(); - } else { - display_l = l_price; - display_r = r_price; - } - display_l = displayNumberToAppropriateDecimals(display_l); - display_r = displayNumberToAppropriateDecimals(display_r); - return `${display_l} - ${display_r}`; - } - function hoverLine(hashId: string) { - setHoverHashId(hashId); - } function getCurrentLiqudity(hashId: string) { const c_l = user_liquidities.find((liquidity: UserLiquidityInfo) => { if (liquidity.lpt_id.split('#')[1] == hashId) return true; }); return c_l; } - function goAddLiqudityPage() { - const pool_id = poolDetail.pool_id; - const [token_x, token_y, fee] = pool_id.split('|'); - let url_hash = pool_id; - if (TOKEN_LIST_FOR_RATE.indexOf(token_x_metadata?.symbol) > -1) { - url_hash = `${token_y}|${token_x}|${fee}`; - } - history.push(`/addLiquidityV2#${url_hash}`); - } - function displayFarmStatus(liquidity: UserLiquidityInfo) { - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - if (is_in_farming) { - return ( - - ); - } else { - return ( - - ); - } - } - function go_farm(liquidity: UserLiquidityInfo) { - const { mft_id } = liquidity; - const [fixRange, pool_id, left_point, right_point] = mft_id.split('&'); - const link_params = `${get_pool_name( - pool_id - )}[${left_point}-${right_point}]`; - const seed_id = REF_UNI_V3_SWAP_CONTRACT_ID + '@' + mft_id.slice(1); - const temp_seeds = (matched_seeds || []).filter((seed: Seed) => { - return seed_id == seed.seed_id; - }); - let actives: FarmBoost[] = []; - temp_seeds.forEach((seed: Seed) => { - const { farmList } = seed; - const temp = farmList.filter((farm: FarmBoost) => { - return farm.status != 'Ended'; - }); - actives = actives.concat(temp); - }); - let url; - if (actives.length > 0) { - url = `/v2farms/${link_params}-r`; - } else { - url = `/v2farms/${link_params}-e`; - } - openUrl(url); - } - function is_in_farming(liquidity: UserLiquidityInfo) { - const is_in_farming = - liquidity.part_farm_ratio && +liquidity.part_farm_ratio > 0; - return is_in_farming; - } - const isMobile = isClientMobie(); - const has_no_related_seed = - matched_seeds?.length == 0 && - user_liquidities?.every( - (liquidity: UserLiquidityInfo) => +(liquidity.part_farm_ratio || 0) == 0 - ); return operation == 'remove' && isOpen ? ( Date: Mon, 25 Sep 2023 21:55:54 +0800 Subject: [PATCH 144/204] optimizatio --- src/components/d3Chart/DclChart.tsx | 22 ++++++++++++++++---- src/pages/poolsV3/AddYourLiquidityPageV3.tsx | 4 +++- 2 files changed, 21 insertions(+), 5 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 20b5f1240..7c4ee176f 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1078,12 +1078,26 @@ export default function DclChart({ .attr('fill', 'rgba(255,255,255,0.1)'); } function draw_current_bar({ scale }: { scale: Function }) { + const x = scale(+get_current_price()) + svgPaddingX; d3.select(`${randomId} .currentLine`).attr( 'style', - `transform:translate(${ - scale(+get_current_price()) + svgPaddingX - }px, -${axisHeight}px)` + `transform:translate(${x}px, -${axisHeight}px)` ); + if (is_mobile) { + if (x > 290) { + const tx = x > 320 ? '-95%' : x > 300 ? '-80%' : '-70%'; + d3.select(`${randomId} .currentLineDetail`).attr( + 'style', + `transform:translateX(${tx})` + ); + } else if (x < 50) { + const tx = x < 10 ? '-10%' : x < 20 ? '-20%' : '-30%'; + d3.select(`${randomId} .currentLineDetail`).attr( + 'style', + `transform:translateX(${tx})` + ); + } + } } function draw_drag_left({ scale }: { scale: any }) { const price = get_current_price(); @@ -1833,7 +1847,7 @@ export default function DclChart({ style={{ height: svgHeight + 'px' }} >
diff --git a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx index 5ba45c1bc..ffa4eefc2 100644 --- a/src/pages/poolsV3/AddYourLiquidityPageV3.tsx +++ b/src/pages/poolsV3/AddYourLiquidityPageV3.tsx @@ -545,7 +545,9 @@ export default function AddYourLiquidityPageV3() { if ( !isInvalid(leftPoint) && !isInvalid(rightPoint) && - (+tokenXAmount > 0 || +tokenYAmount > 0) + ((onlyAddXToken && +tokenXAmount > 0) || + (onlyAddYToken && +tokenYAmount > 0) || + (+tokenXAmount > 0 && +tokenYAmount > 0)) ) { let new_nfts: any; if (liquidityShape == 'Spot') { From 77c73c3d4047f1ef55fbda17901c17a516adc8b0 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 26 Sep 2023 11:21:19 +0800 Subject: [PATCH 145/204] optimization --- src/components/d3Chart/DclChart.tsx | 135 +-- src/components/d3Chart/Icon.tsx | 93 ++ src/components/d3Chart/utils.ts | 21 + .../Orderly/components/MyOrder/index.tsx | 992 +----------------- 4 files changed, 121 insertions(+), 1120 deletions(-) create mode 100644 src/components/d3Chart/Icon.tsx diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 7c4ee176f..d1f78c08c 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1,7 +1,6 @@ -import React, { useState, useRef, useEffect } from 'react'; -import { FormattedMessage } from 'react-intl'; +import React, { useState, useEffect } from 'react'; import { isMobile } from '../../utils/device'; -import { TokenMetadata, ftGetTokenMetadata } from '../../services/ft-contract'; +import { ftGetTokenMetadata } from '../../services/ft-contract'; import { get_pool, PoolInfo, @@ -36,7 +35,6 @@ import { IRMTYPE, } from './interfaces'; import { - formatPrice, formatNumber, formatPercentage, formatWithCommas_usd, @@ -51,10 +49,12 @@ import Big from 'big.js'; import * as d3 from 'd3'; import { useWalletSelector } from '../../context/WalletSelectorContext'; import { getBoostTokenPrices } from '../../services/farm'; -import { toReadableNumber, formatWithCommas } from '~utils/numbers'; +import { formatWithCommas } from '~utils/numbers'; import { ILiquidityInfoPool, IOrderInfoPool } from '../../services/commonV3'; import { BlueCircleLoading } from '../../components/layout/Loading'; import { get_unClaimed_fee_data } from '../../pages/poolsV3/components/detail/DetailFun'; +import { AddIcon, SubIcon } from './Icon'; +import { isValid, createRandomString } from './utils'; export default function DclChart({ pool_id, leftPoint, @@ -1342,20 +1342,6 @@ export default function DclChart({ } return point_int_bin; } - function clickToLeft() { - const { bin } = getConfig(); - const newPoint = dragLeftPoint - pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'floor'); - setDragLeftPoint(newPoint_nearby_bin); - setLeftPoint && setLeftPoint(newPoint_nearby_bin); - } - function clickToRight() { - const { bin } = getConfig(); - const newPoint = dragRightPoint + pool.point_delta * (bin + 1); - const newPoint_nearby_bin = get_bin_point_by_point(newPoint, 'ceil'); - setDragRightPoint(newPoint_nearby_bin); - setRightPoint && setRightPoint(newPoint_nearby_bin); - } function get_bin_point_by_point(point: number, type?: IRMTYPE) { const { point_delta } = pool; const slot_num = getConfig().bin; @@ -1909,114 +1895,3 @@ export default function DclChart({ ); } -function isValid(n: number) { - if (n !== undefined && n !== null) return true; - return false; -} -function AddIcon(props: any) { - return ( - - - - - ); -} -function SubIcon(props: any) { - return ( - - - - ); -} -function createRandomChar(c = 'a-z') { - switch (c) { - case 'A-Z': - return String.fromCharCode(Math.trunc(Math.random() * 25) + 65); - case 'a-z': - return String.fromCharCode(Math.trunc(Math.random() * 25) + 97); - case '0-9': - default: - return String.fromCharCode(Math.trunc(Math.random() * 10) + 48); - } -} -function createRandomString(length = 4) { - let str = ''; - for (let i = 0; i < length; i++) str += createRandomChar(); - return str; -} -function LeftArrowIcon(props: any) { - return ( - - - - ); -} -function RightArrowIcon(props: any) { - return ( - - - - ); -} diff --git a/src/components/d3Chart/Icon.tsx b/src/components/d3Chart/Icon.tsx new file mode 100644 index 000000000..7d2ba592d --- /dev/null +++ b/src/components/d3Chart/Icon.tsx @@ -0,0 +1,93 @@ +import React from 'react'; + +export function AddIcon(props: any) { + return ( + + + + + ); +} +export function SubIcon(props: any) { + return ( + + + + ); +} +export function LeftArrowIcon(props: any) { + return ( + + + + ); +} +export function RightArrowIcon(props: any) { + return ( + + + + ); +} diff --git a/src/components/d3Chart/utils.ts b/src/components/d3Chart/utils.ts index 9c8213e70..4084ef4a4 100644 --- a/src/components/d3Chart/utils.ts +++ b/src/components/d3Chart/utils.ts @@ -82,3 +82,24 @@ export const isInvalid = function (v: any) { if (v === '' || v === undefined || v === null || v === '-') return true; return false; }; + +export function isValid(n: number) { + if (n !== undefined && n !== null) return true; + return false; +} +export function createRandomChar(c = 'a-z') { + switch (c) { + case 'A-Z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 65); + case 'a-z': + return String.fromCharCode(Math.trunc(Math.random() * 25) + 97); + case '0-9': + default: + return String.fromCharCode(Math.trunc(Math.random() * 10) + 48); + } +} +export function createRandomString(length = 4) { + let str = ''; + for (let i = 0; i < length; i++) str += createRandomChar(); + return str; +} diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index cd566f895..b6da8d76a 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -99,44 +99,6 @@ const MobileInfoBanner = ({
); }; -function WarningTip() { - if (isMobile()) { - return ( - - - - ); - } - - return ( - - - - ); -} function NoOrderCard({ text }: { text: 'active' | 'history' }) { return ( @@ -435,7 +397,7 @@ function HistoryLine({ const claimTip = (
{actions} - - {/* {actions} */} {hoverOn === index && @@ -840,9 +800,6 @@ function HistoryLine({ )} - - {/* {hover && !ONLY_ZEROS.test(swapIn || '0') ? swapBanner : null} */} -
( - 'created' - ); - - const [sortOrderActive, setSorOrderActive] = useState<'asc' | 'desc'>('desc'); - - const sellAmountToBuyAmount = ( - undecimaled_amount: string, - order: UserOrderInfo, - price: string - ) => { - const buy_amount = new Big( - toReadableNumber( - tokensMap[order.sell_token].decimals, - undecimaled_amount || '0' - ) - ) - .times(price) - .toFixed(tokensMap[order.sell_token].decimals); - - return scientificNotationToString(buy_amount); - }; - - const buyAmountToSellAmount = ( - undecimaled_amount: string, - order: UserOrderInfo, - price: string - ) => { - const sell_amount = new Big( - toReadableNumber( - tokensMap[order.buy_token].decimals, - undecimaled_amount || '0' - ) - ) - .div(price) - .toString(); - return scientificNotationToString(sell_amount); - }; - function ActiveLine({ - order, - index, - }: { - order: UserOrderInfo; - index: number; - }) { - const [claimLoading, setClaimLoading] = useState(false); - - const [cancelLoading, setCancelLoading] = useState(false); - - const [hover, setHover] = useState(false); - - const buyToken = tokensMap[order.buy_token]; - - const sellToken = tokensMap[order.sell_token]; - - if (!buyToken || !sellToken) return null; - - const swapIn = toReadableNumber( - sellToken.decimals, - scientificNotationToString( - new Big(order.original_deposit_amount || '0') - .minus(order.original_amount || '0') - .toString() - ) - ); - - const swapOut = toReadableNumber( - buyToken.decimals, - order.swap_earn_amount || '0' - ); - - const orderIn = toReadableNumber( - sellToken.decimals, - order.original_amount || '0' - ); - - const totalIn = toReadableNumber( - sellToken.decimals, - order.original_deposit_amount || '0' - ); - - const calPoint = - sellToken.id === order.pool_id.split(V3_POOL_SPLITER)[0] - ? order.point - : -order.point; - - const price = pointToPrice({ - tokenA: sellToken, - tokenB: buyToken, - point: calPoint, - }); - - const unClaimedAmount = toReadableNumber( - buyToken.decimals, - order.unclaimed_amount || '0' - ); - - const claimedAmount = toReadableNumber( - buyToken.decimals, - scientificNotationToString( - new Big(order.bought_amount || '0') - .minus(order.unclaimed_amount || '0') - .toString() - ) - ); - - const buyAmountRaw = sellAmountToBuyAmount( - order.original_amount, - order, - price - ); - - const buyAmount = new Big(buyAmountRaw).gt( - toReadableNumber(buyToken.decimals, order.bought_amount || '0') - ) - ? buyAmountRaw - : toReadableNumber(buyToken.decimals, order.bought_amount || '0'); - - const totalOut = scientificNotationToString( - new Big(buyAmount).plus(swapOut).toString() - ); - - const pendingAmount = scientificNotationToString( - new Big(toPrecision(buyAmount || '0', 5, false, false) || 0) - .minus( - toPrecision( - toReadableNumber(buyToken.decimals, order.bought_amount || '0') || - '0', - 5, - false, - false - ) - ) - .toString() - ); - - const pUnClaimedAmount = new Big(unClaimedAmount) - .div(buyAmount) - .times(100) - .toNumber(); - - const pClaimedAmount = new Big(claimedAmount) - .div(buyAmount) - .times(100) - .toNumber(); - - const pPendingAmount = new Big(pendingAmount) - .div(buyAmount) - .times(100) - .toNumber(); - - const displayPercents = checkAllocations('100', [ - pClaimedAmount > 0 && pClaimedAmount < 5 - ? '5' - : scientificNotationToString(pClaimedAmount.toString()), - pUnClaimedAmount > 0 && pUnClaimedAmount < 5 - ? '5' - : scientificNotationToString(pUnClaimedAmount.toString()), - - pPendingAmount > 0 && pPendingAmount < 5 - ? '5' - : scientificNotationToString(pPendingAmount.toString()), - ]); - - const getUnclaimAmountTip = () => { - return ` -
- ${ - ONLY_ZEROS.test(claimedAmount) - ? '' - : ` -
- -
-
- - ${intl.formatMessage({ - id: 'claimed_upper', - defaultMessage: 'Claimed', - })} - -
- - - ${ - Number(claimedAmount) > 0 && Number(claimedAmount) < 0.001 - ? '< 0.001' - : toPrecision(claimedAmount, 3) - } - - -
- ` - } - - - ${ - ONLY_ZEROS.test(unClaimedAmount) - ? '' - : `
- -
-
- - ${intl.formatMessage({ - id: 'filled', - defaultMessage: 'Filled', - })} - -
- - - ${ - Number(unClaimedAmount) > 0 && Number(unClaimedAmount) < 0.001 - ? '< 0.001' - : toPrecision(unClaimedAmount, 3) - } - - -
` - } - - ${ - ONLY_ZEROS.test(pendingAmount) - ? '' - : `
- -
-
- - ${intl.formatMessage({ - id: 'open_my_order', - defaultMessage: 'Open', - })} - -
- - - ${ - Number(pendingAmount) > 0 && Number(pendingAmount) < 0.001 - ? '< 0.001' - : toPrecision(pendingAmount, 3) - } - - - -
` - } - -
- `; - }; - - const sellTokenAmount = ( -
- - - -
- - {Number(orderIn) > 0 && Number(orderIn) < 0.01 - ? '< 0.01' - : toPrecision(orderIn, 2)} - - - - {toRealSymbol(sellToken.symbol)} - -
-
- - - - -
- ); - - const buyTokenAmount = ( - - - -
- - {Number(buyAmount) > 0 && Number(buyAmount) < 0.01 - ? '< 0.01' - : toPrecision(buyAmount, 2)} - - - - {toRealSymbol(buyToken.symbol)} - -
-
- ); - - const fee = Number(order.pool_id.split(V3_POOL_SPLITER)[2]); - - const feeTier = ( - - {`${toPrecision(calculateFeePercent(fee / 100).toString(), 2)}% `} - - ); - - // const orderRate = ( - // - // - // {toPrecision(price, 2)} - // - // - // {`${toRealSymbol(buyToken.symbol)}/${toRealSymbol(sellToken.symbol)}`} - // - - // - // {`${toRealSymbol(buyToken.symbol)}`} - // - // - // ); - const sort = - TOKEN_LIST_FOR_RATE.indexOf(sellToken?.symbol) > -1 && +price !== 0; - const orderRate = useMemo(() => { - let p = price; - if (sort) { - p = new BigNumber(1).dividedBy(price).toFixed(); - } - return ( - - - {toPrecision(p, 2)} - - - {`${toRealSymbol( - sort ? sellToken?.symbol : buyToken.symbol - )}/${toRealSymbol(sort ? buyToken.symbol : sellToken.symbol)}`} - - - {`${toRealSymbol(sort ? sellToken.symbol : buyToken.symbol)}`} - - - ); - }, [buyToken, sellToken, price]); - - const unclaimTip = ( -
- - - -
- {displayPercents.map((p, i) => { - if (ONLY_ZEROS.test(p)) return null; - - const bgColor = - i === 0 - ? 'bg-gradientFrom' - : i === 1 - ? 'bg-deepBlue' - : 'bg-primaryText'; - - return ( -
- ); - })} -
- -
- ); - - const claimButton = ( -
-
- ${intl.formatMessage({ - id: 'v2_paused', - - defaultMessage: 'REF V2 has been paused for maintenance', - })} -
-
- `} - data-for="v2_paused_pool_tip_claim" - > - - - {/* */} -
- ); - - const unclaim = ( - -
-
- - - {Number( - toReadableNumber( - buyToken.decimals, - order.unclaimed_amount || '0' - ) - ) > 0 && - Number( - toReadableNumber( - buyToken.decimals, - order.unclaimed_amount || '0' - ) - ) < 0.001 - ? '< 0.001' - : toPrecision( - toReadableNumber( - buyToken.decimals, - order.unclaimed_amount || '0' - ), - 3 - )} - -
-
{unclaimTip}
-
- {claimButton} -
- ); - - const created = ( - - {moment( - Math.floor(Number(order.created_at) / TIMESTAMP_DIVISOR) * 1000 - ).format('YYYY-MM-DD HH:mm')} - - ); - - const actions = ( -
-
- Canceling will automatically claim your executed tokens. -
-
- `} - data-for="v2_paused_pool_tip_cancel_old" - > - - - -
- ); - - const tokenPrice = useContext(PriceContext); - - const sellTokenPrice = tokenPrice?.[sellToken.id]?.price || null; - const buyTokenPrice = tokenPrice?.[buyToken.id]?.price || null; - function instant_swap_tip() { - const token_sell_symbol = toRealSymbol(sellToken.symbol); - const token_buy_symbol = toRealSymbol(buyToken.symbol); - const sell_token_price = sellTokenPrice - ? `($${toPrecision(sellTokenPrice, 2)})` - : ''; - const buy_token_price = buyTokenPrice - ? `($${toPrecision(buyTokenPrice, 2)})` - : ''; - let rate = new Big(swapOut).div(ONLY_ZEROS.test(swapIn) ? 1 : swapIn); - if (sort) { - rate = new Big(1).div(rate.eq(0) ? '1' : rate); - } - const display_rate = rate.toFixed(3); - let result = ''; - if (sort) { - result = `1 ${token_buy_symbol} ${buy_token_price} = ${display_rate} ${token_sell_symbol}`; - } else { - result = `1 ${token_sell_symbol} ${sell_token_price} = ${display_rate} ${token_buy_symbol}`; - } - return result; - } - const swapBanner = ( -
-
- - - - - - - - {Number(totalIn) > 0 && Number(totalIn) < 0.01 - ? '< 0.01' - : toPrecision(totalIn, 2)} - - - {toRealSymbol(sellToken.symbol)} - - {isClientMobie() ? ( - - ) : ( - - )} - - - {Number(totalOut) > 0 && Number(totalOut) < 0.01 - ? '< 0.01' - : toPrecision(totalOut, 2)} - - - {toRealSymbol(buyToken.symbol)} - -
- -
- - - - - - - - - - {Number(swapIn) > 0 && Number(swapIn) < 0.01 - ? '< 0.01' - : toPrecision(swapIn, 2)} - - - {toRealSymbol(sellToken.symbol)} - - {isClientMobie() ? ( - - ) : ( - - )} - - - {Number(swapOut) > 0 && Number(swapOut) < 0.01 - ? '< 0.01' - : toPrecision(swapOut, 2)} - - - {toRealSymbol(buyToken.symbol)} - -
-
- ); - - return ( - <> -
{ - setHover(false); - }} - style={{ - zIndex: 20 - index, - }} - > -
{ - setHover(true); - }} - > - {sellTokenAmount} - {buyTokenAmount} - {feeTier} - {orderRate} - {created} - - {unclaim} - - {actions} -
- {hover && !ONLY_ZEROS.test(swapIn || '0') ? swapBanner : null} -
- -
- {/* title */} -
-
- {sellTokenAmount} - - {buyTokenAmount} -
- - {created} -
- {/* content */} -
- - } - value={feeTier} - /> - - - - - } - value={unclaim} - /> - - {unclaimTip} - -
- {actions} - {claimButton} -
-
- - {/* swap banner */} - {!ONLY_ZEROS.test(swapIn || '0') ? swapBanner : null} -
- - ); - } - - if (!activeOrder || activeOrder.length === 0) return null; - - const activeOrderSorting = (a: UserOrderInfo, b: UserOrderInfo) => { - if (activeSortBy === 'created') { - return sortOrderActive === 'desc' - ? Number(b.created_at) - Number(a.created_at) - : Number(a.created_at) - Number(b.created_at); - } else if (activeSortBy === 'unclaim') { - const unclaimA = toReadableNumber( - tokensMap[a.buy_token].decimals, - a.unclaimed_amount - ); - const unclaimB = toReadableNumber( - tokensMap[b.buy_token].decimals, - b.unclaimed_amount - ); - - return sortOrderActive === 'desc' - ? Number(unclaimB) - Number(unclaimA) - : Number(unclaimA) - Number(unclaimB); - } - }; - - return ( -
-
- - - - - - {isMobile() && ( - - *Canceling will automatically claim your executed tokens. - - )} -
- -
- - - - - - - - - - - - - - - - - - - - - - - -
- - {activeOrder && - activeOrder.sort(activeOrderSorting).map((order, index) => { - return ( - - ); - })} -
- ); -} - function MyOrderComponent() { const { activeOrder, historyOrder } = useMyOrders(); @@ -3877,15 +2893,11 @@ function MyOrderComponent() { return (
- {/* */} -
From 19ae2e50d1ae2298e3621ea577c38576db18de5c Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 26 Sep 2023 14:11:32 +0800 Subject: [PATCH 146/204] optimization --- src/components/d3Chart/DclChart.tsx | 35 ++++++++++++++++++++--------- 1 file changed, 24 insertions(+), 11 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index d1f78c08c..0932e0087 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -105,6 +105,8 @@ export default function DclChart({ useState(); const [tokenPriceList, setTokenPriceList] = useState>(); const [chartDataListDone, setChartDataListDone] = useState(false); + const [dclPoolPoints, setDclPoolPoints] = useState(); + const [dclPoolPointsDone, setDclPoolPointsDone] = useState(false); /** constant start */ const appearanceConfig: IPoolChartConfig = config || {}; let [timerObj, setTimerObj] = useState({ @@ -159,6 +161,12 @@ export default function DclChart({ get_chart_data(); } }, [pool, accountId]); + useEffect(() => { + if (dclPoolPointsDone && chartDataListDone) { + const combineList = combine_data(dclPoolPoints, chartDataList); + setChartDataList(combineList); + } + }, [dclPoolPointsDone, chartDataListDone]); useEffect(() => { if (chartDataList) { init_price_range(); @@ -429,10 +437,11 @@ export default function DclChart({ setPool(p); } async function get_chart_data() { + get_dcl_pool_points(); const list = await get_data_from_back_end(); setChartDataList(list); - setChartDataListDone(true); init_price_range(); + setChartDataListDone(true); } function init_price_range() { const { range } = getConfig(); @@ -447,13 +456,23 @@ export default function DclChart({ set_price_range(range); } } - async function get_data_from_back_end() { - setChartDataListDone(false); - const { token_x_metadata, token_y_metadata, pool_id } = pool; + function get_dcl_pool_points() { + const { pool_id } = pool; const { bin: bin_final, rangeGear } = getConfig(); const [price_l, price_r] = get_price_range_by_percent(rangeGear[0], true); const point_l = get_point_by_price(price_l); const point_r = get_point_by_price(price_r); + getDclPoolPoints(pool_id, bin_final, point_l, point_r).then( + (pointsData_apr) => { + setDclPoolPoints(pointsData_apr?.point_data); + setDclPoolPointsDone(true); + } + ); + } + async function get_data_from_back_end() { + setChartDataListDone(false); + const { token_x_metadata, token_y_metadata, pool_id } = pool; + const { bin: bin_final, rangeGear } = getConfig(); let list: any[] = []; if (chartType == 'USER') { if (accountId) { @@ -475,12 +494,6 @@ export default function DclChart({ }); } } else { - const pointsData_apr = await getDclPoolPoints( - pool_id, - bin_final, - point_l, - point_r - ); const marketdepthData = await get_pool_marketdepth(pool_id); const { liquidities, orders } = marketdepthData; let liquidities_array: ILiquidityInfoPool[] = Object.values(liquidities); @@ -525,7 +538,7 @@ export default function DclChart({ tokenY: token_y_metadata, poolDetail: pool, }); - list = combine_data(pointsData_apr?.point_data, pointsData_l); + list = combine_data([], pointsData_l); } return list; } From 6fdf1848ef5cbf4e08f927d8d8882af3942d7bbb Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 26 Sep 2023 14:37:12 +0800 Subject: [PATCH 147/204] add Recent Transactions for fourTokenPool --- src/pages/stable/FourTokenStablePage.tsx | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/src/pages/stable/FourTokenStablePage.tsx b/src/pages/stable/FourTokenStablePage.tsx index 9e7840dd9..3cd4f9572 100644 --- a/src/pages/stable/FourTokenStablePage.tsx +++ b/src/pages/stable/FourTokenStablePage.tsx @@ -29,6 +29,8 @@ import BigNumber from 'bignumber.js'; import { getStablePoolFromCache, Pool, StablePool } from '../../services/pool'; import { getStableSwapTabKey } from './StableSwapPageUSN'; import { USDTT_USDCC_USDT_USDC_TOKEN_IDS } from '../../services/near'; +import { RecentTransactions } from '../pools/DetailsPage'; +import { useTokens } from '~state/token'; export const DEFAULT_ACTIONS = ['add_liquidity', 'remove_liquidity']; interface LocationTypes { @@ -70,6 +72,8 @@ function FourTokenStablePage({ pool }: { pool: Pool }) { }, []); const allTokens = useWhitelistStableTokens(); + const pool_tokens = useTokens(pool?.tokenIds); + let tokens: TokenMetadata[]; if (allTokens && allTokens.length > 0) { tokens = USDTT_USDCC_USDT_USDC_TOKEN_IDS.map((id) => { @@ -127,6 +131,12 @@ function FourTokenStablePage({ pool }: { pool: Pool }) { {} {renderModule(actionName)} {} +
+ +
); } From 62842ed7f3c81dc07a7d542a5ecebc9eb43007e1 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 26 Sep 2023 16:06:07 +0800 Subject: [PATCH 148/204] ui optimization --- src/pages/pools/DetailsPage.tsx | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index cdddab056..102b0dd27 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1506,11 +1506,11 @@ export function RecentTransactions({ return ( - + {tx.method_name.toLowerCase().indexOf('add') > -1 && 'Add'} @@ -1544,10 +1544,10 @@ export function RecentTransactions({ { openUrl(`${getConfig().explorerUrl}/txns/${tx.tx_id}`); }} From 38b47c8a397114e85c7d371ebdff24a6688da5bb Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 26 Sep 2023 16:21:31 +0800 Subject: [PATCH 149/204] add Recent Transactions for two token stable pool --- src/pages/stable/StableSwapPageUSN.tsx | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/pages/stable/StableSwapPageUSN.tsx b/src/pages/stable/StableSwapPageUSN.tsx index 8094ae355..06a13de1e 100644 --- a/src/pages/stable/StableSwapPageUSN.tsx +++ b/src/pages/stable/StableSwapPageUSN.tsx @@ -24,6 +24,8 @@ import { Pool, StablePool, getStablePoolFromCache } from '../../services/pool'; import AddLiquidityComponentUSN from '../../components/stableswap/AddLiquidityUSN'; import { RemoveLiquidityComponentUSN } from '../../components/stableswap/RemoveLiquidityUSN'; import { NEARX_POOL_ID, STABLE_TOKEN_USN_IDS } from '../../services/near'; +import { RecentTransactions } from '../pools/DetailsPage'; +import { useTokens } from '~state/token'; export const DEFAULT_ACTIONS = ['add_liquidity', 'remove_liquidity']; export const getStableSwapTabKey = (id: string | number) => @@ -61,6 +63,7 @@ function StableSwapPageUSN({ pool }: { pool: Pool }) { const { shares } = state?.pool ? state : usePool(id); const allTokens = useWhitelistStableTokens(); + const pool_tokens = useTokens(pool?.tokenIds); const tokens = allTokens ? pool.tokenIds.map((id) => allTokens?.find((token) => token?.id === id)) @@ -124,6 +127,12 @@ function StableSwapPageUSN({ pool }: { pool: Pool }) { {} {renderModule(actionName)} {} +
+ +
); } From 75e871f885f0b9024dbd14c3391bbedd07790913 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 27 Sep 2023 10:23:17 +0800 Subject: [PATCH 150/204] fix bugs --- src/pages/Orderly/components/MyOrder/index.tsx | 4 ++-- src/pages/SwapPage.tsx | 4 ++-- src/state/swapV3.ts | 2 +- 3 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/Orderly/components/MyOrder/index.tsx b/src/pages/Orderly/components/MyOrder/index.tsx index b6da8d76a..f48fb1d57 100644 --- a/src/pages/Orderly/components/MyOrder/index.tsx +++ b/src/pages/Orderly/components/MyOrder/index.tsx @@ -81,7 +81,7 @@ import { HiOutlineExternalLink } from 'react-icons/hi'; import getConfig from '../../../../services/config'; import _ from 'lodash'; import { HistoryOrderSwapInfo } from '../../../../services/indexer'; -import { useDclPoolIdByUrl } from '../../../../state/swapV3'; +import { useDclPoolIdByCondition } from '../../../../state/swapV3'; const ORDER_TYPE_KEY = 'REF_FI_ORDER_TYPE_VALUE'; @@ -2130,7 +2130,7 @@ function OrderCard({ sessionStorage.removeItem(REF_FI_MY_ORDER_SHOW_HISTORY_SWAP_INFO); } }; - const pool_id_by_url = useDclPoolIdByUrl(); + const pool_id_by_url = useDclPoolIdByCondition('all'); const [orderType, setOrderType] = useState<'active' | 'history'>( sessionStorage.getItem(ORDER_TYPE_KEY) || diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index ccd1acf47..79cd14343 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -31,7 +31,7 @@ import MyOrderPage from './MyOrder'; import MyOrderComponent from './Orderly/components/MyOrder'; import { useWalletSelector } from '../context/WalletSelectorContext'; import { useClientMobile } from '../utils/device'; -import { useDclPoolIdByUrl } from '../state/swapV3'; +import { useDclPoolIdByCondition } from '../state/swapV3'; export const SWAP_MODE_KEY = 'SWAP_MODE_VALUE'; @@ -280,7 +280,7 @@ function SwapPage() { const [swapMode, setSwapMode] = useState( storageMode || SWAP_MODE.NORMAL ); - const dcl_pool_id = useDclPoolIdByUrl('all'); + const dcl_pool_id = useDclPoolIdByCondition('all'); useEffect(() => { if (swapMode === SWAP_MODE.LIMIT) { setLimitTokenTrigger(!limitTokenTrigger ? true : false); diff --git a/src/state/swapV3.ts b/src/state/swapV3.ts index ff8689b5c..8cfd00ef9 100644 --- a/src/state/swapV3.ts +++ b/src/state/swapV3.ts @@ -93,7 +93,7 @@ export const useAllPoolsV2 = () => { return allPools; }; -export const useDclPoolIdByUrl = (source?: 'all' | 'url' | 'local') => { +export const useDclPoolIdByCondition = (source?: 'all' | 'url' | 'local') => { const [pool_id, set_pool_id] = useState(); const location = useLocation(); const hash = location.hash; From 9a0ecbc2db5be05fa4af8edba84c5d54fb6d5353 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 27 Sep 2023 11:03:35 +0800 Subject: [PATCH 151/204] add new dcl log --- src/components/icon/Actions.tsx | 10 +++++ src/services/transaction.ts | 76 +++++++++++++++++++++++++++++++++ 2 files changed, 86 insertions(+) diff --git a/src/components/icon/Actions.tsx b/src/components/icon/Actions.tsx index 4bd0a2039..e41b5149a 100644 --- a/src/components/icon/Actions.tsx +++ b/src/components/icon/Actions.tsx @@ -790,6 +790,11 @@ const viewMap = { Supply: AddLiquidity, Claim: Withdraw, 'Settle PnL': Swap, + 'Batch Add Liquidity': AddLiquidity, + 'Batch Remove Liquidity': RemoveLiquidity, + 'Batch Update Liquidity': Swap, + 'Batch Mint Liquidity': Deposit, + 'Batch Burn Liquidity': Withdraw, }; const blackViewMap = { @@ -844,6 +849,11 @@ const blackViewMap = { Supply: AddLiquidityBlack, Claim: WithdrawBlack, 'Settle PnL': SwapBlack, + 'Batch Add Liquidity': AddLiquidityBlack, + 'Batch Remove Liquidity': RemoveLiquidityBlack, + 'Batch Update Liquidity': SwapBlack, + 'Batch Mint Liquidity': DepositBlack, + 'Batch Burn Liquidity': WithdrawBlack, }; export function mapToView(action: string, black = false): JSX.Element { diff --git a/src/services/transaction.ts b/src/services/transaction.ts index af81da3ee..29b829c66 100644 --- a/src/services/transaction.ts +++ b/src/services/transaction.ts @@ -159,6 +159,21 @@ export const parseAction = async ( case 'user_request_settlement': { return await parse_user_request_settlement(params); } + case 'batch_add_liquidity': { + return await parse_batch_add_liquidity(params, tokenId); + } + case 'batch_remove_liquidity': { + return await parse_batch_remove_liquidity(params, tokenId); + } + case 'batch_update_liquidity': { + return await parse_batch_update_liquidity(params, tokenId); + } + case 'batch_mint_v_liquidity': { + return await parse_batch_mint_v_liquidity(params, tokenId); + } + case 'batch_burn_v_liquidity': { + return await parse_batch_burn_v_liquidity(params, tokenId); + } default: { return await parseDefault(); } @@ -1270,6 +1285,67 @@ const parse_user_request_settlement = async (params: any) => { } }; +const parse_batch_add_liquidity = async (params: any, tokenId: string) => { + try { + return { + Action: 'Batch Add Liquidity', + 'Receiver ID': tokenId, + }; + } catch (error) { + return { + Action: 'Batch Add Liquidity', + }; + } +}; +const parse_batch_remove_liquidity = async (params: any, tokenId: string) => { + try { + return { + Action: 'Batch Remove Liquidity', + 'Receiver ID': tokenId, + }; + } catch (error) { + return { + Action: 'Batch Remove Liquidity', + }; + } +}; +const parse_batch_update_liquidity = async (params: any, tokenId: string) => { + try { + return { + Action: 'Batch Update Liquidity', + 'Receiver ID': tokenId, + }; + } catch (error) { + return { + Action: 'Batch Update Liquidity', + }; + } +}; +const parse_batch_mint_v_liquidity = async (params: any, tokenId: string) => { + try { + return { + Action: 'Batch Mint Liquidity', + 'Receiver ID': tokenId, + }; + } catch (error) { + return { + Action: 'Batch Mint Liquidity', + }; + } +}; +const parse_batch_burn_v_liquidity = async (params: any, tokenId: string) => { + try { + return { + Action: 'Batch Burn Liquidity', + 'Receiver ID': tokenId, + }; + } catch (error) { + return { + Action: 'Batch Burn Liquidity', + }; + } +}; + const parseDefault = async () => { return { Action: 'Not Found', From 9316f0f7bc93b494e2f3da0b39eef7e0a06aa4be Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 14:06:07 +0800 Subject: [PATCH 152/204] ui optimization --- src/components/d3Chart/DclChart.tsx | 2 +- src/components/icon/FarmStamp.tsx | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 0932e0087..6855540d3 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1676,7 +1676,7 @@ export default function DclChart({ {/* show hover box then hover on the bin */} -
+
APR(24h) diff --git a/src/components/icon/FarmStamp.tsx b/src/components/icon/FarmStamp.tsx index b2459a8c4..aaee5fa0c 100644 --- a/src/components/icon/FarmStamp.tsx +++ b/src/components/icon/FarmStamp.tsx @@ -28,7 +28,7 @@ export const FarmStampNew = ({ multi }: { multi: boolean }) => { export const FarmStampNewDCL = ({ multi }: { multi: boolean }) => { return ( -
+
diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 70ee6bb2b..8c5e2faaf 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -496,7 +496,7 @@ export default function PoolDetailV3() {
:{' '} - + {poolDetail.fee / 10000}% From bfee56ac87a20afd48d911f6a355cde0a28be3c3 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 15:49:57 +0800 Subject: [PATCH 153/204] add log and update dcl config --- src/components/d3Chart/config.tsx | 6 +++--- src/pages/poolsV3/components/detail/YourLiquidityBox.tsx | 6 ++++++ src/services/commonV3.ts | 9 ++++++++- 3 files changed, 17 insertions(+), 4 deletions(-) diff --git a/src/components/d3Chart/config.tsx b/src/components/d3Chart/config.tsx index 29a767648..3060f40be 100644 --- a/src/components/d3Chart/config.tsx +++ b/src/components/d3Chart/config.tsx @@ -47,7 +47,7 @@ export function get_custom_config_for_chart(): IChartConfig { 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|aurora|2000': // ETH<>USDC.e@2000 { - bin: 4, + bin: 2, range: 50, rangeGear: [130, 110, 90, 70, 50, 30, 20, 10], colors: ['#626CA3', '#2775CA'], @@ -55,7 +55,7 @@ export function get_custom_config_for_chart(): IChartConfig { 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|aaaaaa20d9e0e2461697782ef11675f668207961.factory.bridge.near|2000': // AURORA<>USDC.e@2000 { - bin: 10, + bin: 5, range: 100, rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], colors: ['#79BD84', '#2775CA'], @@ -63,7 +63,7 @@ export function get_custom_config_for_chart(): IChartConfig { 'a0b86991c6218b36c1d19d4a2e9eb0ce3606eb48.factory.bridge.near|wrap.near|2000': // NEAR<>USDC.e@2000 { - bin: 10, + bin: 5, range: 100, rangeGear: [180, 160, 140, 120, 100, 80, 60, 40, 20, 10], colors: ['#707C84', '#2775CA'], diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 4dd588c15..3b056188a 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -160,6 +160,12 @@ export function YourLiquidityBox(props: { unClaimed_amount_x_fee, unClaimed_amount_y_fee, ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); + console.log( + 'unClaimed from contract-unClaimed_amount_x_fee_amount, unClaimed_amount_y_fee_amount, unClaimed_tvl_fee', + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + unClaimed_tvl_fee + ); // 24h profit apr_24 = get_account_24_apr( unClaimed_tvl_fee, diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 548759489..4cf67f91e 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1758,7 +1758,13 @@ export function get_account_24_apr( const total_fee_24_value = fee_x_24_value .plus(fee_y_24_value) .plus(unClaimed_fee$ || 0); - + console.log( + 'Claimed_fee_x_24_value, Claimed_fee_y_24_value, unClaimed_fee$, total_fee_24_value', + fee_x_24_value, + fee_y_24_value, + unClaimed_fee$, + total_fee_24_value + ); // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; const user_token_processed = process_user_token({ @@ -1808,6 +1814,7 @@ export function get_account_24_apr( if (principal.gt(0)) { apr_24 = total_fee_24_value.div(principal).mul(365).mul(100).toFixed(); } + console.log('24小时平均本金', principal); return apr_24; } From 0b185785c9116bbe349e7612eab6457f539a71d6 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 16:20:27 +0800 Subject: [PATCH 154/204] add log --- src/pages/poolsV3/components/detail/YourLiquidityBox.tsx | 6 +++--- src/services/commonV3.ts | 8 ++++---- 2 files changed, 7 insertions(+), 7 deletions(-) diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 3b056188a..9bc441894 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -162,9 +162,9 @@ export function YourLiquidityBox(props: { ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); console.log( 'unClaimed from contract-unClaimed_amount_x_fee_amount, unClaimed_amount_y_fee_amount, unClaimed_tvl_fee', - unClaimed_amount_x_fee, - unClaimed_amount_y_fee, - unClaimed_tvl_fee + unClaimed_amount_x_fee.toFixed(), + unClaimed_amount_y_fee.toFixed(), + unClaimed_tvl_fee.toFixed(), ); // 24h profit apr_24 = get_account_24_apr( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index 4cf67f91e..a64ebd564 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1760,10 +1760,10 @@ export function get_account_24_apr( .plus(unClaimed_fee$ || 0); console.log( 'Claimed_fee_x_24_value, Claimed_fee_y_24_value, unClaimed_fee$, total_fee_24_value', - fee_x_24_value, - fee_y_24_value, + fee_x_24_value.toFixed(), + fee_y_24_value.toFixed(), unClaimed_fee$, - total_fee_24_value + total_fee_24_value.toFixed(), ); // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; @@ -1814,7 +1814,7 @@ export function get_account_24_apr( if (principal.gt(0)) { apr_24 = total_fee_24_value.div(principal).mul(365).mul(100).toFixed(); } - console.log('24小时平均本金', principal); + console.log('24小时平均本金', principal.toFixed()); return apr_24; } From ee339ec6f4404f2b7472573040a7152a9220f49f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 16:33:37 +0800 Subject: [PATCH 155/204] add log --- src/pages/poolsV3/components/detail/YourLiquidityBox.tsx | 6 +++--- src/services/commonV3.ts | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 9bc441894..35d8a045d 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -162,9 +162,9 @@ export function YourLiquidityBox(props: { ] = get_unClaimed_fee_data(liquidities, poolDetail, tokenPriceList); console.log( 'unClaimed from contract-unClaimed_amount_x_fee_amount, unClaimed_amount_y_fee_amount, unClaimed_tvl_fee', - unClaimed_amount_x_fee.toFixed(), - unClaimed_amount_y_fee.toFixed(), - unClaimed_tvl_fee.toFixed(), + unClaimed_amount_x_fee, + unClaimed_amount_y_fee, + '$' + unClaimed_tvl_fee, ); // 24h profit apr_24 = get_account_24_apr( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index a64ebd564..e8c7a2ac0 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1762,8 +1762,8 @@ export function get_account_24_apr( 'Claimed_fee_x_24_value, Claimed_fee_y_24_value, unClaimed_fee$, total_fee_24_value', fee_x_24_value.toFixed(), fee_y_24_value.toFixed(), - unClaimed_fee$, - total_fee_24_value.toFixed(), + '$' + unClaimed_fee$, + '$' + total_fee_24_value.toFixed(), ); // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; From 083069f06c7aa08248fb2f43846cfad64466af48 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 17:34:50 +0800 Subject: [PATCH 156/204] fix tailwind issue --- src/pages/pools/DetailsPage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 5c46f4e85..352e87c89 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1614,7 +1614,7 @@ export function RecentTransactions({
{tab === 'liquidity' && ( From 36be4027ed348ea8ee43424063f4fb12efc91846 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 17:41:17 +0800 Subject: [PATCH 157/204] update --- src/pages/poolsV3/components/detail/TablePool.tsx | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/pages/poolsV3/components/detail/TablePool.tsx b/src/pages/poolsV3/components/detail/TablePool.tsx index d0d8ffbe0..6777b7426 100644 --- a/src/pages/poolsV3/components/detail/TablePool.tsx +++ b/src/pages/poolsV3/components/detail/TablePool.tsx @@ -555,9 +555,7 @@ function RecentTransactions({
{tab === 'liquidity' && ( From 120f9efc693a2490b5d001271a2952cbbbac6779 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 17:45:48 +0800 Subject: [PATCH 158/204] update --- src/pages/poolsV3/components/detail/TablePool.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/poolsV3/components/detail/TablePool.tsx b/src/pages/poolsV3/components/detail/TablePool.tsx index 6777b7426..18049bda5 100644 --- a/src/pages/poolsV3/components/detail/TablePool.tsx +++ b/src/pages/poolsV3/components/detail/TablePool.tsx @@ -551,11 +551,11 @@ function RecentTransactions({ - + {/* ${tab === 'swap' ? 'grid-cols-3': 'grid-cols-5'} */}
{tab === 'liquidity' && ( From 20dcd3ce0fed3dc6b206c284c10000b1b160e890 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 17:49:52 +0800 Subject: [PATCH 159/204] update --- src/pages/poolsV3/components/detail/TablePool.tsx | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/pages/poolsV3/components/detail/TablePool.tsx b/src/pages/poolsV3/components/detail/TablePool.tsx index 18049bda5..515828023 100644 --- a/src/pages/poolsV3/components/detail/TablePool.tsx +++ b/src/pages/poolsV3/components/detail/TablePool.tsx @@ -551,11 +551,10 @@ function RecentTransactions({ - {/* ${tab === 'swap' ? 'grid-cols-3': 'grid-cols-5'} */}
{tab === 'liquidity' && ( From 6e9058f4ca51e742b0ef940e3e5a5457d365ba57 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 28 Sep 2023 19:21:26 +0800 Subject: [PATCH 160/204] fix ui issue --- src/pages/Orderly/components/Common/Icons.tsx | 2 +- src/pages/SwapPage.tsx | 2 +- src/pages/poolsV3/components/detail/TablePool.tsx | 4 +++- src/pages/poolsV3/components/detail/YourLiquidityBox.tsx | 2 +- src/services/commonV3.ts | 2 +- 5 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/pages/Orderly/components/Common/Icons.tsx b/src/pages/Orderly/components/Common/Icons.tsx index 09f571aca..37bb188d2 100644 --- a/src/pages/Orderly/components/Common/Icons.tsx +++ b/src/pages/Orderly/components/Common/Icons.tsx @@ -1375,4 +1375,4 @@ export function BidAskShape() { ); -} \ No newline at end of file +} diff --git a/src/pages/SwapPage.tsx b/src/pages/SwapPage.tsx index a7cfc0bc6..eefb22568 100644 --- a/src/pages/SwapPage.tsx +++ b/src/pages/SwapPage.tsx @@ -347,7 +347,7 @@ function SwapPage() {
{swapType === SWAP_TYPE.Pro && (
{tab === 'liquidity' && ( diff --git a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx index 35d8a045d..c3b801c08 100644 --- a/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx +++ b/src/pages/poolsV3/components/detail/YourLiquidityBox.tsx @@ -164,7 +164,7 @@ export function YourLiquidityBox(props: { 'unClaimed from contract-unClaimed_amount_x_fee_amount, unClaimed_amount_y_fee_amount, unClaimed_tvl_fee', unClaimed_amount_x_fee, unClaimed_amount_y_fee, - '$' + unClaimed_tvl_fee, + '$' + unClaimed_tvl_fee ); // 24h profit apr_24 = get_account_24_apr( diff --git a/src/services/commonV3.ts b/src/services/commonV3.ts index e8c7a2ac0..6d88621c1 100644 --- a/src/services/commonV3.ts +++ b/src/services/commonV3.ts @@ -1763,7 +1763,7 @@ export function get_account_24_apr( fee_x_24_value.toFixed(), fee_y_24_value.toFixed(), '$' + unClaimed_fee$, - '$' + total_fee_24_value.toFixed(), + '$' + total_fee_24_value.toFixed() ); // 24小时平均本金 const processed_change_log: IProcessedLogData[] = []; From 3d00c52d903320df174e9b6f9e7b3d09674179ed Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Mon, 2 Oct 2023 23:56:23 +0800 Subject: [PATCH 161/204] fix bugs --- src/components/farm/FarmsDclDetail.tsx | 142 ++---------------- src/components/pool/YourLiquidityV2.tsx | 36 ++--- src/pages/pools/LiquidityPage.tsx | 33 ++-- src/pages/poolsV3/PoolDetailV3.tsx | 2 +- src/pages/poolsV3/components/detail/Chart.tsx | 10 +- .../poolsV3/components/detail/UserTabBox.tsx | 19 ++- .../components/detail/YourLiquidityBox.tsx | 34 +++-- src/state/pool.ts | 14 +- 8 files changed, 92 insertions(+), 198 deletions(-) diff --git a/src/components/farm/FarmsDclDetail.tsx b/src/components/farm/FarmsDclDetail.tsx index 84e4fda92..a7fb4d375 100644 --- a/src/components/farm/FarmsDclDetail.tsx +++ b/src/components/farm/FarmsDclDetail.tsx @@ -1433,7 +1433,17 @@ export default function FarmsDclDetail(props: { unFarimg={listLiquidities_unFarimg} unavailable={listLiquidities_unavailable} > -
+
{/* Your Farming Position */}
- {/* unClaimed Rewards for PC */} - {/* {user_seeds_map[detailData.seed_id] ? ( */} + {/* unClaimed Rewards*/} - {/* ) : null} */}
- - {/* unClaimed Rewards for Mobile */} - {/*
-
-
- -
- - -
-
- -
- - {unclaimedRewardsData.worth} - - -
-
- {unclaimedRewardsData.showClaimButton ? ( -
-
- - -
- { - e.stopPropagation(); - claimReward(); - }} - > - } - /> - -
- ) : null} - -
- {unclaimedRewardsData.list.map( - ( - { - token, - amount, - preAmount, - }: { token: TokenMetadata; amount: string; preAmount: string }, - index: number - ) => { - return ( -
-
- - - {toRealSymbol(token.symbol)} - -
-
- {preAmount ? ( - <> - {preAmount} - - - - {amount} - - ) : ( - {amount} - )} -
-
- ); - } - )} -
-
*/} {/* caculator */} {seedDclCalcVisible ? ( -
+
{ + goDetailV2(); + }} + > {/* head */} -
{ - e.preventDefault(); - e.stopPropagation(); - goDetailV2(); - }} - > +
- {value_of_investment} in{' '} + {value_of_investment} in { e.stopPropagation(); openUrl(go_farm_url_link); @@ -1421,9 +1419,9 @@ function UserLiquidityLineStyleGroupStyle1Mobile() { } else { return ( ) : tip_seed ? (
- 0% in{' '} + 0% in { e.stopPropagation(); openUrl(tip_seed.go_farm_url_link); @@ -1465,7 +1463,10 @@ function UserLiquidityLineStyleGroupStyle1Mobile() { ? 'bg-deepBlue text-white opacity-30 cursor-not-allowed' : 'bg-deepBlue text-white hover:bg-lightBlue cursor-pointer' }`} - onClick={claimRewards} + onClick={(e) => { + e.stopPropagation(); + claimRewards(); + }} > { + onClick={(e) => { if (is_mobile) { + e.stopPropagation(); setRemoveButtonTip(!removeButtonTip); } }} diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 82e9abdd2..1300b963e 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -563,7 +563,7 @@ function MobilePoolRowV2({ else if (sortBy === 'fee') return `${calculateFeePercent(value / 100)}%`; else if (sortBy === 'volume_24h') { return geth24volume(); - } else if (sortBy === 'top_bin_apr') { + } else if (sortBy === 'top_bin_apr' || (sortBy == 'apr' && mark)) { return value?.toString() == '-' ? '-' : formatPercentage(value); } else return '/'; }; @@ -643,12 +643,19 @@ function MobilePoolRowV2({
-
{showSortedValue({ sortBy, value: pool[sortBy] })}
+
+ {showSortedValue({ + sortBy, + value: + sortBy == 'apr' && mark ? pool['top_bin_apr'] : pool[sortBy], + })} +
); } + function MobileWatchListCard({ watchPools, poolTokenMetas, @@ -758,7 +765,7 @@ function MobileWatchListCard({ {watchAllPools.map((pool: any, i: number) => { if (pool.id?.toString()) { return ( -
+
@@ -1783,7 +1790,6 @@ function PoolRowV2({ }) { const curRowTokens = useTokens([pool.token_x, pool.token_y], tokens); const history = useHistory(); - const displayOfTopBinApr = useDCLTopBinFee({ pool, }); @@ -1878,23 +1884,6 @@ function PoolRowV2({ ? '-' : '$' + toInternationalCurrencySystem(pool.tvl.toString())}
- {/* {!mark && ( -
- -
- )} */}
); diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 8c5e2faaf..3a5ea647d 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -378,7 +378,7 @@ export default function PoolDetailV3() { >
{chartDisplay === 'volume' ? ( diff --git a/src/pages/poolsV3/components/detail/UserTabBox.tsx b/src/pages/poolsV3/components/detail/UserTabBox.tsx index bbb88561d..57312f3ed 100644 --- a/src/pages/poolsV3/components/detail/UserTabBox.tsx +++ b/src/pages/poolsV3/components/detail/UserTabBox.tsx @@ -32,6 +32,7 @@ export function UserButtonBox(props: { matched_seeds={matched_seeds} show={show} tab={tab} + setTab={setTab} setShow={setShow} />
{ - setTabActive(tab); - }, [tab]); function switchTab(tabIndex: number) { - setTabActive(tabIndex); + setTab(tabIndex); } function onRequestClose() { setShow(false); @@ -132,14 +131,14 @@ function UserTabBox(props: { >
@@ -151,14 +150,14 @@ function UserTabBox(props: { >
@@ -167,7 +166,7 @@ function UserTabBox(props: { ) : ( <> - {tabActive == 1 ? ( + {tab == 1 ? ( )} - {earned_fee || '-'} + + {earned_fee || '-'} + @@ -592,19 +598,21 @@ export function YourLiquidityBox(props: {
diff --git a/src/state/pool.ts b/src/state/pool.ts index 3cec3b65c..cd096a9ef 100644 --- a/src/state/pool.ts +++ b/src/state/pool.ts @@ -698,17 +698,25 @@ export const useWatchPools = () => { const poolListDealt = await Promise.all(poolListPromise); return poolListDealt; } - function getV2Poolsfinal() { watchV2Pools.forEach((pool: PoolInfo) => { const { token_x, token_y } = pool; const pricex = tokenPriceList[token_x]?.price || 0; const pricey = tokenPriceList[token_y]?.price || 0; + const { total_x, total_y, total_fee_x_charged, total_fee_y_charged } = + pool; + const totalX = new BigNumber(total_x) + .minus(total_fee_x_charged) + .toFixed(); + const totalY = new BigNumber(total_y) + .minus(total_fee_y_charged) + .toFixed(); + const tvlx = - Number(toReadableNumber(pool.token_x_metadata.decimals, pool.total_x)) * + Number(toReadableNumber(pool.token_x_metadata.decimals, totalX)) * Number(pricex); const tvly = - Number(toReadableNumber(pool.token_y_metadata.decimals, pool.total_y)) * + Number(toReadableNumber(pool.token_y_metadata.decimals, totalY)) * Number(pricey); pool.tvl = tvlx + tvly; }); From ec03614bb7e6fba76329770a623fec353a6a3a9d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Tue, 3 Oct 2023 12:33:00 +0800 Subject: [PATCH 162/204] optimization --- src/components/d3Chart/DclChart.tsx | 2 +- src/components/pool/utils.ts | 2 +- src/pages/poolsV3/PoolDetailV3.tsx | 4 ++-- src/pages/poolsV3/components/add/PointsComponent.tsx | 9 +++++++-- src/pages/poolsV3/components/detail/Chart.tsx | 4 ++-- 5 files changed, 13 insertions(+), 8 deletions(-) diff --git a/src/components/d3Chart/DclChart.tsx b/src/components/d3Chart/DclChart.tsx index 6855540d3..6e25ece3e 100644 --- a/src/components/d3Chart/DclChart.tsx +++ b/src/components/d3Chart/DclChart.tsx @@ -1794,7 +1794,7 @@ export default function DclChart({ ) : null} -
+
Your Liquidity diff --git a/src/components/pool/utils.ts b/src/components/pool/utils.ts index 3118c8832..9a28a058d 100644 --- a/src/components/pool/utils.ts +++ b/src/components/pool/utils.ts @@ -38,7 +38,7 @@ export const formatPercentage = (v: string | number) => { } else if (big.lt(0.01)) { return '<0.01%'; } else { - return big.toFixed(2, 0) + '%'; + return big.toFixed(2, 1) + '%'; } }; export const formatNumber = (v: string | number) => { diff --git a/src/pages/poolsV3/PoolDetailV3.tsx b/src/pages/poolsV3/PoolDetailV3.tsx index 3a5ea647d..ed2c20bc5 100644 --- a/src/pages/poolsV3/PoolDetailV3.tsx +++ b/src/pages/poolsV3/PoolDetailV3.tsx @@ -231,7 +231,7 @@ export default function PoolDetailV3() { ]); return ( <> -
+
-
+
{/* title for pc */}
diff --git a/src/pages/poolsV3/components/add/PointsComponent.tsx b/src/pages/poolsV3/components/add/PointsComponent.tsx index 1e9c063e0..9b1466e06 100644 --- a/src/pages/poolsV3/components/add/PointsComponent.tsx +++ b/src/pages/poolsV3/components/add/PointsComponent.tsx @@ -315,6 +315,7 @@ export function PointsComponent() { } } const is_mobile = isMobile(); + return (
{/* chart area */} @@ -372,7 +373,9 @@ export function PointsComponent() { radius={radius} config={{ radiusMode: priceRangeMode == 'by_radius', - svgWidth: is_mobile ? '330' : '', + svgWidth: is_mobile + ? window.screen.width - 32 || '330' + : '', }} reverse={pair_is_reverse} > @@ -387,7 +390,9 @@ export function PointsComponent() { pool_id={currentSelectedPool?.pool_id} config={{ controlHidden: true, - svgWidth: is_mobile ? '330' : '', + svgWidth: is_mobile + ? window.screen.width - 32 || '330' + : '', }} chartType="USER" reverse={pair_is_reverse} diff --git a/src/pages/poolsV3/components/detail/Chart.tsx b/src/pages/poolsV3/components/detail/Chart.tsx index 5efd7b41a..dd4c6dc03 100644 --- a/src/pages/poolsV3/components/detail/Chart.tsx +++ b/src/pages/poolsV3/components/detail/Chart.tsx @@ -40,7 +40,7 @@ export function Chart(props: any) { return ( (true); const [noData, setNoData] = useState(true); const [rateDirection, setRateDirection] = useState(true); From 879b8873ef73578d7def9292f7b7571e9ecbce65 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 7 Oct 2023 16:12:05 +0800 Subject: [PATCH 163/204] add sort for top bin apr --- src/pages/pools/LiquidityPage.tsx | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 1300b963e..4f9561dd0 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -1790,9 +1790,13 @@ function PoolRowV2({ }) { const curRowTokens = useTokens([pool.token_x, pool.token_y], tokens); const history = useHistory(); - const displayOfTopBinApr = useDCLTopBinFee({ + const topBinApr = useDCLTopBinFee({ pool, + way: 'value', }); + const displayOfTopBinApr = + topBinApr == '-' ? '-' : formatPercentage(topBinApr); + pool.top_bin_apr = topBinApr; if (!curRowTokens) return <>; tokens = sort_tokens_by_base(tokens); @@ -2220,7 +2224,6 @@ function LiquidityPage_({ const top_bin_apr1 = p1.top_bin_apr; const top_bin_apr2 = p2.top_bin_apr; - if (v2Order === 'desc') { if (v2SortBy === 'tvl') { return tvl2 - tvl1; @@ -3277,6 +3280,7 @@ export function LiquidityPage() { indexFail: Object.keys(tokenPriceList).length == 0, }} > + {/* todo */} {!clientMobileDevice && ( Date: Wed, 11 Oct 2023 00:46:58 +0700 Subject: [PATCH 164/204] split batch function for some wallet --- .../components/add/AddLiquidityButton.tsx | 3 ++ src/services/ft-contract.ts | 1 - src/services/pool.ts | 3 -- src/services/swap.ts | 2 +- src/services/swapV3.ts | 40 ++++++++++++++++--- 5 files changed, 39 insertions(+), 10 deletions(-) diff --git a/src/pages/poolsV3/components/add/AddLiquidityButton.tsx b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx index 5aedb1bd7..5afc3f86e 100644 --- a/src/pages/poolsV3/components/add/AddLiquidityButton.tsx +++ b/src/pages/poolsV3/components/add/AddLiquidityButton.tsx @@ -17,6 +17,7 @@ import { ButtonTextWrapper, ConnectToNearBtn, } from '~components/button/Button'; +import { useWalletSelector } from '../../../../context/WalletSelectorContext'; /** * 双边 最小token数量不满足 提示 @@ -43,6 +44,7 @@ export function AddLiquidityButton() { useState(false); const { globalState } = useContext(WalletContext); const isSignedIn = globalState.isSignedIn; + const { selector } = useWalletSelector(); function addLiquiditySpot() { setAddLiquidityButtonLoading(true); @@ -86,6 +88,7 @@ export function AddLiquidityButton() { token_y: tokenY, amount_x: last_total_needed_token_x_amount.toFixed(), amount_y: last_total_needed_token_y_amount.toFixed(), + selectedWalletId: selector.store.getState().selectedWalletId, }); } function getMax(token: TokenMetadata, balance: string) { diff --git a/src/services/ft-contract.ts b/src/services/ft-contract.ts index 7da77dfac..a064cda51 100644 --- a/src/services/ft-contract.ts +++ b/src/services/ft-contract.ts @@ -89,7 +89,6 @@ export const ftGetStorageBalance = ( }); }; -// todo usdc export const check_registration = ( tokenId: string, accountId = getCurrentWallet()?.wallet?.getAccountId() diff --git a/src/services/pool.ts b/src/services/pool.ts index 130d3ab8c..4e237ac66 100644 --- a/src/services/pool.ts +++ b/src/services/pool.ts @@ -889,7 +889,6 @@ export const removeLiquidityFromPool = async ({ const ftBalance = await ftGetStorageBalance(tokenId); if (ftBalance === null) { - // todo usdc if (NO_REQUIRED_REGISTRATION_TOKEN_IDS.includes(tokenId)) { withDrawTransactions.unshift({ receiverId: tokenId, @@ -1012,7 +1011,6 @@ export const removeLiquidityFromStablePool = async ({ const withDrawTransactions: Transaction[] = []; - // todo usdc for (let i = 0; i < tokenIds.length; i++) { const tokenId = tokenIds[i]; const ftBalance = await ftGetStorageBalance(tokenId); @@ -1147,7 +1145,6 @@ export const removeLiquidityByTokensFromStablePool = async ({ const ftBalance = await ftGetStorageBalance(tokenId); if (ftBalance === null) { - // todo usdc if (NO_REQUIRED_REGISTRATION_TOKEN_IDS.includes(tokenId)) { withDrawTransactions.unshift({ receiverId: tokenId, diff --git a/src/services/swap.ts b/src/services/swap.ts index 05a4365bd..e82a1be85 100644 --- a/src/services/swap.ts +++ b/src/services/swap.ts @@ -1123,7 +1123,7 @@ SwapOptions) => { const tokenRegistered = await ftGetStorageBalance(token.id).catch(() => { throw new Error(`${token.id} doesn't exist.`); }); - // todo usdc + if (tokenRegistered === null) { if (NO_REQUIRED_REGISTRATION_TOKEN_IDS.includes(token.id)) { tokenOutActions.push({ diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 2a9cdf2bb..6a0f2a9e6 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -909,27 +909,57 @@ export const batch_add_liquidity = async ({ token_y, amount_x, amount_y, + selectedWalletId = window.selector?.store?.getState()?.selectedWalletId, }: { liquidityInfos: IAddLiquidityInfo[]; token_x: TokenMetadata; token_y: TokenMetadata; amount_x: string; amount_y: string; + selectedWalletId: string; }) => { - const transactions: Transaction[] = [ - { + // todo test + let split_num = 10; + if ( + selectedWalletId == 'ledger' || + selectedWalletId == 'neth' || + selectedWalletId == 'nightly' + ) { + split_num = 2; + } + const transactions: Transaction[] = []; + const n = Math.ceil(liquidityInfos.length / split_num); + for (let i = 0; i < n; i++) { + const start_index = i * split_num; + const end_index = start_index + split_num; + const arr_i = liquidityInfos.slice(start_index, end_index); + transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, functionCalls: [ { methodName: 'batch_add_liquidity', args: { - add_liquidity_infos: liquidityInfos, + add_liquidity_infos: arr_i, }, gas: '300000000000000', }, ], - }, - ]; + }); + } + // const transactions: Transaction[] = [ + // { + // receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + // functionCalls: [ + // { + // methodName: 'batch_add_liquidity', + // args: { + // add_liquidity_infos: liquidityInfos, + // }, + // gas: '300000000000000', + // }, + // ], + // }, + // ]; if (+amount_x > 0) { transactions.unshift({ From ac6bcbdbc751c240bb52eb808723353ae53e24b9 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 11 Oct 2023 19:54:04 +0700 Subject: [PATCH 165/204] modify the wallet selector versions --- package.json | 26 +- yarn.lock | 669 +++++++++++++++++++++++++++++++++------------------ 2 files changed, 450 insertions(+), 245 deletions(-) diff --git a/package.json b/package.json index 3b7af39d1..c4de48626 100644 --- a/package.json +++ b/package.json @@ -64,18 +64,18 @@ "@babel/plugin-transform-modules-commonjs": "^7.17.9", "@ledgerhq/devices": "^7.0.0", "@metamask/detect-provider": "^2.0.0", - "@near-wallet-selector/core": "^7.0.0", - "@near-wallet-selector/here-wallet": "^8.1.0", - "@near-wallet-selector/ledger": "^7.0.0", - "@near-wallet-selector/math-wallet": "^7.0.2", - "@near-wallet-selector/meteor-wallet": "^7.9.0", - "@near-wallet-selector/modal-ui": "^7.0.2", - "@near-wallet-selector/my-near-wallet": "^7.0.0", - "@near-wallet-selector/near-wallet": "^7.0.0", - "@near-wallet-selector/neth": "^7.0.2", - "@near-wallet-selector/nightly": "^7.0.2", - "@near-wallet-selector/sender": "^7.0.0", - "@near-wallet-selector/wallet-connect": "^7.0.2", + "@near-wallet-selector/core": "8.5.3", + "@near-wallet-selector/here-wallet": "^8.5.3", + "@near-wallet-selector/ledger": "^8.5.3", + "@near-wallet-selector/math-wallet": "8.5.3", + "@near-wallet-selector/meteor-wallet": "8.5.3", + "@near-wallet-selector/modal-ui": "8.5.3", + "@near-wallet-selector/my-near-wallet": "8.5.3", + "@near-wallet-selector/near-wallet": "8.5.3", + "@near-wallet-selector/neth": "8.5.3", + "@near-wallet-selector/nightly": "8.5.3", + "@near-wallet-selector/sender": "8.5.3", + "@near-wallet-selector/wallet-connect": "8.5.3", "@react-icons/all-files": "https://github.com/react-icons/react-icons/releases/download/v4.7.1/react-icons-all-files-4.7.1.tgz", "@svgr/parcel-plugin-svgr": "^5.5.0", "@transak/transak-sdk": "1.2.2", @@ -171,4 +171,4 @@ "staticPath": "public", "watcherGlob": "**" } -} \ No newline at end of file +} diff --git a/yarn.lock b/yarn.lock index 9eabcaed2..796220c0b 100644 --- a/yarn.lock +++ b/yarn.lock @@ -1754,10 +1754,10 @@ intl-messageformat "9.13.0" tslib "^2.1.0" -"@here-wallet/core@1.4.3": - version "1.4.3" - resolved "https://registry.yarnpkg.com/@here-wallet/core/-/core-1.4.3.tgz#371fcc2fe4140dd3e93106f367fb6715e487c431" - integrity sha512-HtiAd1gMKxFzbnSualrzAw9CuoGWdY9z8aCY5fkpst+z7Fa5yVvBIg+f/6BWn2PFdxIWEnKk8V051FHEX/iYxA== +"@here-wallet/core@1.5.1": + version "1.5.1" + resolved "https://registry.npmjs.org/@here-wallet/core/-/core-1.5.1.tgz#618db9de547bfaed5229100002357663fcee5d77" + integrity sha512-gCzB27k0QfviyJQUhxqX3kAH3g3mRHb6B5RJdUhX9tTsLlPW6AMV/PJiYBudPCt0EeyeyU4i8kEh223ACHXOjw== dependencies: sha1 "^1.1.1" uuid4 "2.0.3" @@ -2312,19 +2312,100 @@ resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" integrity sha512-Hcv+nVC0kZnQ3tD9GVu5xSMR4VVYOteQIr/hwFPVEvPdlXqgGEuRjiheChHgdM+JyqdgNcmzZOX/tnl0JOiI7A== -"@metamask/detect-provider@^2.0.0": +"@lit-labs/ssr-dom-shim@^1.0.0", "@lit-labs/ssr-dom-shim@^1.1.0": + version "1.1.2" + resolved "https://registry.npmjs.org/@lit-labs/ssr-dom-shim/-/ssr-dom-shim-1.1.2.tgz#d693d972974a354034454ec1317eb6afd0b00312" + integrity sha512-jnOD+/+dSrfTWYfSXBXlo5l5f0q1UuJo3tkbMDCYA2lKUYq79jaxqtGEvnRoh049nt1vdo1+45RinipU6FGY2g== + +"@lit/reactive-element@^1.3.0", "@lit/reactive-element@^1.6.0": + version "1.6.3" + resolved "https://registry.npmjs.org/@lit/reactive-element/-/reactive-element-1.6.3.tgz#25b4eece2592132845d303e091bad9b04cdcfe03" + integrity sha512-QuTgnG52Poic7uM1AN5yJ09QMe0O28e10XzSvWDz02TJiiKee4stsiownEIadWm8nYzyDAyT+gKzUoZmiWQtsQ== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.0.0" + +"@metamask/detect-provider@2.0.0", "@metamask/detect-provider@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-2.0.0.tgz#4bc2795e5e6f7d8b84b2e845058d2f222c99917d" integrity sha512-sFpN+TX13E9fdBDh9lvQeZdJn4qYoRb/6QF2oZZK/Pn559IhCFacPMU1rMuqyXoFQF3JSJfii2l98B87QDPeCQ== -"@meteorwallet/sdk@^0.6.0": - version "0.6.0" - resolved "https://registry.yarnpkg.com/@meteorwallet/sdk/-/sdk-0.6.0.tgz#006b77410a7874f0c9bfc0d693f199b561f39d19" - integrity sha512-oriaQ1gk1hpQx6V6BxsvUthDd0Bpmv3ho5Ap5pm9P0euEosWtFUVF1dTYndJE10qBG8yLW+EOOX1LZ8taXCiRA== +"@meteorwallet/sdk@0.8.0": + version "0.8.0" + resolved "https://registry.npmjs.org/@meteorwallet/sdk/-/sdk-0.8.0.tgz#913732c1c5210a05e008a3aedc664b16c728f682" + integrity sha512-C0x9/20t+lCMUlKSPSAbPUWjz8Ls59NwWnz/qSJM5B4qCXN3OSCcrqmpbJpSVa2wCHFhK/HuOqaKlMCr0XsQ0w== dependencies: nanoid "3.3.4" query-string "^7.1.1" +"@motionone/animation@^10.15.1", "@motionone/animation@^10.16.3": + version "10.16.3" + resolved "https://registry.npmjs.org/@motionone/animation/-/animation-10.16.3.tgz#f5b71e27fd8b88b61f983adb0ed6c8e3e89281f9" + integrity sha512-QUGWpLbMFLhyqKlngjZhjtxM8IqiJQjLK0DF+XOF6od9nhSvlaeEpOY/UMCRVcZn/9Tr2rZO22EkuCIjYdI74g== + dependencies: + "@motionone/easing" "^10.16.3" + "@motionone/types" "^10.16.3" + "@motionone/utils" "^10.16.3" + tslib "^2.3.1" + +"@motionone/dom@^10.16.2", "@motionone/dom@^10.16.4": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/dom/-/dom-10.16.4.tgz#9385716928cc2d5b3208a7dcaf504b69b47fd1ae" + integrity sha512-HPHlVo/030qpRj9R8fgY50KTN4Ko30moWRTA3L3imrsRBmob93cTYmodln49HYFbQm01lFF7X523OkKY0DX6UA== + dependencies: + "@motionone/animation" "^10.16.3" + "@motionone/generators" "^10.16.4" + "@motionone/types" "^10.16.3" + "@motionone/utils" "^10.16.3" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/easing@^10.16.3": + version "10.16.3" + resolved "https://registry.npmjs.org/@motionone/easing/-/easing-10.16.3.tgz#a62abe0ba2841861f167f286782e287eab8d7466" + integrity sha512-HWTMZbTmZojzwEuKT/xCdvoMPXjYSyQvuVM6jmM0yoGU6BWzsmYMeB4bn38UFf618fJCNtP9XeC/zxtKWfbr0w== + dependencies: + "@motionone/utils" "^10.16.3" + tslib "^2.3.1" + +"@motionone/generators@^10.16.4": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/generators/-/generators-10.16.4.tgz#4a38708244bce733bfcebd4a26d19f4bbabd36af" + integrity sha512-geFZ3w0Rm0ZXXpctWsSf3REGywmLLujEjxPYpBR0j+ymYwof0xbV6S5kGqqsDKgyWKVWpUInqQYvQfL6fRbXeg== + dependencies: + "@motionone/types" "^10.16.3" + "@motionone/utils" "^10.16.3" + tslib "^2.3.1" + +"@motionone/svelte@^10.16.2": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/svelte/-/svelte-10.16.4.tgz#5daf117cf5b2576fc6dd487c5e0500938a742470" + integrity sha512-zRVqk20lD1xqe+yEDZhMYgftsuHc25+9JSo+r0a0OWUJFocjSV9D/+UGhX4xgJsuwB9acPzXLr20w40VnY2PQA== + dependencies: + "@motionone/dom" "^10.16.4" + tslib "^2.3.1" + +"@motionone/types@^10.15.1", "@motionone/types@^10.16.3": + version "10.16.3" + resolved "https://registry.npmjs.org/@motionone/types/-/types-10.16.3.tgz#9284ea8a52f6b32c51c54b617214f20e43ac6c59" + integrity sha512-W4jkEGFifDq73DlaZs3HUfamV2t1wM35zN/zX7Q79LfZ2sc6C0R1baUHZmqc/K5F3vSw3PavgQ6HyHLd/MXcWg== + +"@motionone/utils@^10.15.1", "@motionone/utils@^10.16.3": + version "10.16.3" + resolved "https://registry.npmjs.org/@motionone/utils/-/utils-10.16.3.tgz#ddf07ab6cf3000d89e3bcbdc9a8c3e1fd64f8520" + integrity sha512-WNWDksJIxQkaI9p9Z9z0+K27xdqISGNFy1SsWVGaiedTHq0iaT6iZujby8fT/ZnZxj1EOaxJtSfUPCFNU5CRoA== + dependencies: + "@motionone/types" "^10.16.3" + hey-listen "^1.0.8" + tslib "^2.3.1" + +"@motionone/vue@^10.16.2": + version "10.16.4" + resolved "https://registry.npmjs.org/@motionone/vue/-/vue-10.16.4.tgz#07d09e3aa5115ca0bcc0076cb9e5322775277c09" + integrity sha512-z10PF9JV6SbjFq+/rYabM+8CVlMokgl8RFGvieSGNTmrkQanfHn+15XBrhG3BgUfvmTeSeyShfOHpG0i9zEdcg== + dependencies: + "@motionone/dom" "^10.16.4" + tslib "^2.3.1" + "@mrmlnc/readdir-enhanced@^2.2.1": version "2.2.1" resolved "https://registry.yarnpkg.com/@mrmlnc/readdir-enhanced/-/readdir-enhanced-2.2.1.tgz#524af240d1a360527b730475ecfa1344aa540dde" @@ -2333,163 +2414,130 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@near-wallet-selector/core@7.9.0", "@near-wallet-selector/core@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/core/-/core-7.9.0.tgz#33dcfa589983d5aa10f0590e181e2f1e538c044d" - integrity sha512-VbHNdO1yRILsgjrX3Qxg5C3oJZpC3f1PjHLY0h37FXRBTIyVEycMl+8qVaBHck5j8FIOJMfyS9d6tC+TKRp7Og== - dependencies: - rxjs "^7.8.0" - -"@near-wallet-selector/core@7.9.1": - version "7.9.1" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/core/-/core-7.9.1.tgz#d13d20724bd105e85b141bb2c7f35f4c07a37cad" - integrity sha512-BJGsTK0snZAU1Je+W9TFJG5KW+jk+Ots9LuxsALZ/wZpqSeGT+w4xw36TZt3QsEm+I84im1+L+PIIW/bM2A9rQ== - dependencies: - rxjs "^7.8.0" - -"@near-wallet-selector/core@7.9.3": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/core/-/core-7.9.3.tgz#5a51b72064c9e018c0f0eb97db06b70cd58b8358" - integrity sha512-SNIgLnI/LeU1mwBHc5wcyOrVAqhWmFXJfVIfB1P16ziH3EKMsbs/gxcKUSPuvDagm9dZm11k+FA7bxSspavibA== - dependencies: - rxjs "^7.8.0" - -"@near-wallet-selector/core@8.2.1": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/core/-/core-8.2.1.tgz#078e9621c37dfbab3dce107abf48efbf72ca120e" - integrity sha512-/0101VcoiFnptQZieZ+qLEWOmOhAauklZF3fE28m2s3e8RxSYKoB84RVnZETPa6U4nX8awLVY5dUuVw0pTg+VQ== +"@near-wallet-selector/core@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-8.5.3.tgz#1cb6df1a3705f88baab4edef23b5155cf0e52a6a" + integrity sha512-KN0fgrkUz9jpbSImPI4RDYM/S9ARSIcjEP49SVEJp5AQlULrpazJoMRnAWgXRjzvAncSeDOVjb8fAkqxKl3YRQ== dependencies: + borsh "0.7.0" events "3.3.0" + js-sha256 "0.9.0" rxjs "7.8.1" -"@near-wallet-selector/here-wallet@^8.1.0": - version "8.2.1" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/here-wallet/-/here-wallet-8.2.1.tgz#cb2b78aba7f2d37d329a234e2935e9ca244a9d73" - integrity sha512-cKW1PQfimteinWJ+WFk3U1kuI3b1GtPeh4zpq/1wi4ZVkyAbgbeGyYTCKAK4lDnnZYhIc4u9nTJIBgIk6heYXQ== +"@near-wallet-selector/here-wallet@^8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/here-wallet/-/here-wallet-8.5.3.tgz#99e06a76b304316220fd6a709d5b53cf1476c00d" + integrity sha512-ZYmLo4t7S8EwYH2QLVOQs9Zg9AbJEkhvRHsz+TYIAzwDGHxYxNJ7BmohbtE7oICOSd5us4iYmLxn6PETmuICiA== dependencies: - "@here-wallet/core" "1.4.3" - "@near-wallet-selector/core" "8.2.1" + "@here-wallet/core" "1.5.1" + "@near-wallet-selector/core" "8.5.3" bn.js "5.2.1" -"@near-wallet-selector/ledger@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/ledger/-/ledger-7.9.0.tgz#f0f3fa8255309506bdf96983971cd106426cfa97" - integrity sha512-j7nX5NA7UzqI+1voyKU6U4Twrkg297Lo4O3BPpaz/+p0Yzx3qpDEbphob4MqxVeCvKjsy3GaWwmfvA5KVJf7tw== +"@near-wallet-selector/ledger@^8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/ledger/-/ledger-8.5.3.tgz#698965bd6dc106854c6ce4eac70b7a26f0b6287e" + integrity sha512-X8fOpENrGoyqxoqQ2B4qrXnUeE2TujmrKG4IUQybMgBfV1vX+nWmwIZDdgy1AQ49h+tArgcWQ54VfaExjAvzoA== dependencies: "@ledgerhq/hw-transport" "6.27.1" "@ledgerhq/hw-transport-webhid" "6.27.1" - "@near-wallet-selector/core" "7.9.0" - "@near-wallet-selector/wallet-utils" "7.9.0" - bn.js "^5.2.0" - is-mobile "^3.1.1" - -"@near-wallet-selector/math-wallet@^7.0.2": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/math-wallet/-/math-wallet-7.9.0.tgz#86548474e1e0b8fd0771d7741f16c687e00ac7bf" - integrity sha512-MIfEiM7B8OiZyDZlnnnbd7cL/Jidd6dUNC6ioprcfD7zczPZ5UBbpA80Y1w1AW33jbZaD9/4QEXwrrN/gBVlMg== - dependencies: - "@near-wallet-selector/core" "7.9.0" - "@near-wallet-selector/wallet-utils" "7.9.0" - is-mobile "^3.1.1" - -"@near-wallet-selector/meteor-wallet@^7.9.0": - version "7.9.1" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/meteor-wallet/-/meteor-wallet-7.9.1.tgz#a3c462a87b097c27977a546327354bf6ce2e4f89" - integrity sha512-oDP1K65XkSi/2zXuSMhFPcMIuTSYRKX7fsEAV4e6MrW42QFoXgsA64Z8y/JYoITzP6WMRHn6TbdTyw2WZOHgoQ== - dependencies: - "@meteorwallet/sdk" "^0.6.0" - "@near-wallet-selector/core" "7.9.1" - "@near-wallet-selector/wallet-utils" "7.9.1" - -"@near-wallet-selector/modal-ui@^7.0.2": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/modal-ui/-/modal-ui-7.9.0.tgz#4f38ba0d97a45a2e5870cc98977805e0bf185430" - integrity sha512-GT7nQHcaR5xoRYAcWDdehDdGQ72n6ELiDJew8/e2f4OBgGXRQNKf6HnTzKGByy6+BrNjd2QoUPdZXUFcGpZYAw== - dependencies: - "@near-wallet-selector/core" "7.9.0" - copy-to-clipboard "^3.3.3" - qrcode "^1.5.1" + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" + bn.js "5.2.1" + is-mobile "4.0.0" + ts-essentials "7.0.3" + +"@near-wallet-selector/math-wallet@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/math-wallet/-/math-wallet-8.5.3.tgz#ce556f9883418ded6b7a27da18d17055704ad476" + integrity sha512-Bkl/K1fFsvjlZcWZEbqk9sQcx6JdNJgcNxK/BE1ptu7csLPqZM9kV8N8Yx1/CzucWSgc7QKz1P6ygYG108EJaw== + dependencies: + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" + is-mobile "4.0.0" + +"@near-wallet-selector/meteor-wallet@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/meteor-wallet/-/meteor-wallet-8.5.3.tgz#4f493616813013978716e3e46242477484c49e12" + integrity sha512-OryLwNuPBO9dPSA/j43k4+ebSoqqhogIOIayoBJPt2AwVp8rSLlkoQDbuL3wwuMiA5di6A2HCo5pqA7b0NXj5A== + dependencies: + "@meteorwallet/sdk" "0.8.0" + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" + +"@near-wallet-selector/modal-ui@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/modal-ui/-/modal-ui-8.5.3.tgz#9fba8e769ad3c113584e8fdf3b3650f9be08b811" + integrity sha512-GkPgjnjU1+JcYQ1uFnQVYkMGhWvf0XZF9ZSYYa6dakm3J6ZEdH3RmPqKnPAx5Q6dJCQHC/WrqCfAZzZ/LclS5Q== + dependencies: + "@near-wallet-selector/core" "8.5.3" + copy-to-clipboard "3.3.3" + qrcode "1.5.3" react "18.2.0" react-dom "18.2.0" -"@near-wallet-selector/my-near-wallet@7.9.0", "@near-wallet-selector/my-near-wallet@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/my-near-wallet/-/my-near-wallet-7.9.0.tgz#dc6ffbb310a894c1c67ff1bce4feb05c909155ee" - integrity sha512-JZk8SCaig9HQ/gvTOPSVaMVUilfrmMHD8dPrXruZr/gN1pLKyZGJDY35MVuF1weXiECnweJkl5zjumGnPgV2wQ== +"@near-wallet-selector/my-near-wallet@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/my-near-wallet/-/my-near-wallet-8.5.3.tgz#b83907ab7e314c110be944361028df2db5413ff5" + integrity sha512-3fxmYYlRl2iG9b/rAMdA9p8Tz5rgQwFupzzTSF4UaRKYpxsXBeWKmWgZeUg5xvr08Z9cGjEflnVqQ7nW6cNDmQ== dependencies: - "@near-wallet-selector/core" "7.9.0" - "@near-wallet-selector/wallet-utils" "7.9.0" + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" -"@near-wallet-selector/near-wallet@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/near-wallet/-/near-wallet-7.9.0.tgz#ad018a73a7a3658191fdec210a5231de4108fb79" - integrity sha512-P4/q2MOYQCCu9AjxUgCAoM/+mtuq4GiBPfK9MphOw2b850MtgIcPviLMRkMjqMozHsQa2gKV1ZKkkvj2SypXMQ== +"@near-wallet-selector/near-wallet@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/near-wallet/-/near-wallet-8.5.3.tgz#86535114e3e06ae9d90f35fa7fdf95b9801e2a09" + integrity sha512-1VAZUuaMMqHSRRruI/wzyfmvFo/n3L/CNiUwq3MbwGgmIgSvaSP6tBRibumw2oHkBycjcYTQSed/dLLs034zSQ== dependencies: - "@near-wallet-selector/core" "7.9.0" - "@near-wallet-selector/my-near-wallet" "7.9.0" + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/my-near-wallet" "8.5.3" -"@near-wallet-selector/neth@^7.0.2": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/neth/-/neth-7.9.0.tgz#f567e255a3026cd56b0ebc99bc05df3517ef7251" - integrity sha512-7MYLPAKRkswAIsmkhtXSWxE9ICcRQluPNCCo0e55JLYRivg7QiAAc32put2agACsveOHGX7JwfKPywdHnElrKA== +"@near-wallet-selector/neth@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-8.5.3.tgz#fdf2f7fb05df0225e3453316b3a5576871c0db28" + integrity sha512-/K8NvpaEMpyhE1lxg8LQVNotws0hQHOiP5kShEdyqwUONpSz2cE+bs+srbElAsZXmTcLP2UhUrqHUV3MKT62gA== dependencies: - "@metamask/detect-provider" "^2.0.0" - "@near-wallet-selector/core" "7.9.0" - bn.js "^5.2.0" - ethers "^5.7.2" - is-mobile "^3.1.1" - near-seed-phrase "^0.2.0" - -"@near-wallet-selector/nightly@^7.0.2": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/nightly/-/nightly-7.9.3.tgz#f4a42d750213f3fc2cee4843bd4bc9b4b7a03d91" - integrity sha512-ssIjArHISadq51ivQYz+AEXvsRYS2nUrwqWKDzj2UH8p6DssFMCLZjmuhE2c6RgLgVYgIZj5jAbMh8RLs6OOqA== - dependencies: - "@near-wallet-selector/core" "7.9.3" - "@near-wallet-selector/wallet-utils" "7.9.3" - is-mobile "^3.1.1" - -"@near-wallet-selector/sender@^7.0.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/sender/-/sender-7.9.0.tgz#aa7fadf84f7d7f4cf05477b8e31eaed27981f648" - integrity sha512-xSEvGluB/SRLFrYGVmfzWqGrCg5412RiJ+21ysFZJh93V0Vfy/5fryBEQuHQmjjTDE//6E1402Eshd3QIhUEtQ== - dependencies: - "@near-wallet-selector/core" "7.9.0" - is-mobile "^3.1.1" - -"@near-wallet-selector/wallet-connect@^7.0.2": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/wallet-connect/-/wallet-connect-7.9.0.tgz#61f4471d8483c2180c5cc6d21287d1d6a95c10a2" - integrity sha512-47m+He4daFLH9HwJGGVW82mWlZP2Q2nuckVG7nwAvv2gMpQ5pku9ipvPsO9hW60VK4FXGbzHf9GOvbf6PW4kzQ== - dependencies: - "@near-wallet-selector/core" "7.9.0" - "@near-wallet-selector/wallet-utils" "7.9.0" - "@walletconnect/qrcode-modal" "2.0.0-alpha.20" - "@walletconnect/sign-client" "2.1.4" - -"@near-wallet-selector/wallet-utils@7.9.0": - version "7.9.0" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/wallet-utils/-/wallet-utils-7.9.0.tgz#a2f8f3ce8c942cecda68e8b1a73d997794d088a1" - integrity sha512-vJW2TzkyRmgVRbT6U7FWzJOboNT1r9EqmKgXVsjpTYu0JgMhAyFwGAI51+jOYY/mjmkUW+tdrdJV/tGz9j91wg== - dependencies: - "@near-wallet-selector/core" "7.9.0" - bn.js "^5.2.0" - -"@near-wallet-selector/wallet-utils@7.9.1": - version "7.9.1" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/wallet-utils/-/wallet-utils-7.9.1.tgz#f6cb831ec9db0b25c9ee2a031eafb27e1b46f99e" - integrity sha512-8bO0z7VQH26a5suS0g7XiBUsEZEBduJBY+WWybPJNtYbQUDWkIT9/Jz0rCAIgqqrtJxNufFKYimqY0Upt5Um8Q== - dependencies: - "@near-wallet-selector/core" "7.9.1" - bn.js "^5.2.0" - -"@near-wallet-selector/wallet-utils@7.9.3": - version "7.9.3" - resolved "https://registry.yarnpkg.com/@near-wallet-selector/wallet-utils/-/wallet-utils-7.9.3.tgz#35a098f29080041fc83daa6b48a7e005fa441fa1" - integrity sha512-uH99A7K0eibZeWVHb+fFgP/rpaDskEfHQXZiAOuIzaDmWnm8KSG4VxA+l5GAR/X+OTpaYwNSd+AB/v3PdeH0Ug== - dependencies: - "@near-wallet-selector/core" "7.9.3" - bn.js "^5.2.0" + "@metamask/detect-provider" "2.0.0" + "@near-wallet-selector/core" "8.5.3" + bn.js "5.2.1" + ethers "5.7.2" + is-mobile "4.0.0" + near-seed-phrase "0.2.0" + +"@near-wallet-selector/nightly@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/nightly/-/nightly-8.5.3.tgz#2d50f6063f85d2b46a2b2db9f1cf1dfe0886c813" + integrity sha512-tEhx55RrAzOqtsIJM/mh4OA19h+URckefR2E6RSnScAnc/4UnC3K1zXQqZaO9SYWOttbM9w5EbLQQtrNgkFT9A== + dependencies: + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" + is-mobile "4.0.0" + +"@near-wallet-selector/sender@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/sender/-/sender-8.5.3.tgz#bfee3b1bba69b660f5fc12cc3cdfe5eedc34fc25" + integrity sha512-Z3i0mzLx8MOWsIFgXTVu+1j6a3RN29wq3JSqY6WC/dlfpBj3qHPTJCVqccCEBMbqkNJlOrYgYraPxQLd3x/2IQ== + dependencies: + "@near-wallet-selector/core" "8.5.3" + is-mobile "4.0.0" + +"@near-wallet-selector/wallet-connect@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-connect/-/wallet-connect-8.5.3.tgz#6e6a293bea64b50edb80cb3beb306c234f8c0e17" + integrity sha512-hqAWbwUh/lEqN871xhABsiEwst88wWh3P17HfHEHKehOK2LL+HaN4UZrF2Mv8/Sy4K2uCIMLBTIZL51sH2/DzQ== + dependencies: + "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/wallet-utils" "8.5.3" + "@walletconnect/modal" "2.6.0" + "@walletconnect/sign-client" "2.9.1" + "@walletconnect/types" "2.9.1" + +"@near-wallet-selector/wallet-utils@8.5.3": + version "8.5.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-utils/-/wallet-utils-8.5.3.tgz#add49dfeeeebbfb0f8ec2eb3b0ed09296acf755c" + integrity sha512-Ag5bRtQwkPciMSeTrd6TOQyAV3tUWSQTRsZxFqy+aYsQA6vBSI3qsXLSlbjvIQGA8vRgyQdzkkilHWu3gC/Mrg== + dependencies: + "@near-wallet-selector/core" "8.5.3" + bn.js "5.2.1" "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": version "5.1.1-v1" @@ -3846,27 +3894,27 @@ "@walletconnect/utils" "^2.0.0-beta.55" ws "^8.3.0" -"@walletconnect/core@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@walletconnect/core/-/core-2.1.4.tgz#871a1e6b80b14fc3a755763b2cdce0c334e41536" - integrity sha512-pEGTbysRwxJJhOYoRmBy8WQ2z7iYrNYsKvdGN/BNoXGuOT9PqKNqiRtVs2E4W79Nwq7ZoM1zohh5VLgbKguSUQ== +"@walletconnect/core@2.9.1": + version "2.9.1" + resolved "https://registry.npmjs.org/@walletconnect/core/-/core-2.9.1.tgz#1a333933750f5f933d9b7788a8dae44ce1173063" + integrity sha512-xyWeP0eLhEEDQAVJSmqs4n/AClKUM+8os2ZFe7BTuw1tFYjeLNVDtKCHziVOSTh8wEChMsKSGKA4zerQoH8mAQ== dependencies: - "@walletconnect/heartbeat" "^1.0.1" - "@walletconnect/jsonrpc-provider" "^1.0.6" - "@walletconnect/jsonrpc-utils" "^1.0.4" - "@walletconnect/jsonrpc-ws-connection" "^1.0.5" + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-provider" "1.0.13" + "@walletconnect/jsonrpc-types" "1.0.3" + "@walletconnect/jsonrpc-utils" "1.0.8" + "@walletconnect/jsonrpc-ws-connection" "1.0.13" "@walletconnect/keyvaluestorage" "^1.0.2" "@walletconnect/logger" "^2.0.1" - "@walletconnect/relay-api" "^1.0.7" + "@walletconnect/relay-api" "^1.0.9" "@walletconnect/relay-auth" "^1.0.4" - "@walletconnect/safe-json" "^1.0.1" + "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.1.4" - "@walletconnect/utils" "2.1.4" + "@walletconnect/types" "2.9.1" + "@walletconnect/utils" "2.9.1" events "^3.3.0" lodash.isequal "4.5.0" - pino "7.11.0" - uint8arrays "3.1.0" + uint8arrays "^3.1.0" "@walletconnect/core@^2.0.0-beta.102-6a30ee7.0", "@walletconnect/core@^2.0.0-beta.55": version "2.4.9" @@ -3914,7 +3962,7 @@ keyvaluestorage-interface "^1.0.0" tslib "1.14.1" -"@walletconnect/heartbeat@1.2.0", "@walletconnect/heartbeat@^1.0.0", "@walletconnect/heartbeat@^1.0.1": +"@walletconnect/heartbeat@1.2.0", "@walletconnect/heartbeat@^1.0.0": version "1.2.0" resolved "https://registry.yarnpkg.com/@walletconnect/heartbeat/-/heartbeat-1.2.0.tgz#1e87dd234cb72b0587b84f95c4f942f2b4bd0c79" integrity sha512-0vbzTa/ARrpmMmOD+bQMxPvFYKtOLQZObgZakrYr0aODiMOO71CmPVNV2eAqXnw9rMmcP+z91OybLeIFlwTjjA== @@ -3926,6 +3974,24 @@ ts-node "^10.9.1" tslib "1.14.1" +"@walletconnect/heartbeat@1.2.1": + version "1.2.1" + resolved "https://registry.npmjs.org/@walletconnect/heartbeat/-/heartbeat-1.2.1.tgz#afaa3a53232ae182d7c9cff41c1084472d8f32e9" + integrity sha512-yVzws616xsDLJxuG/28FqtZ5rzrTA4gUjdEMTbWB5Y8V1XHRmqq4efAxCw5ie7WjbXFSUyBHaWlMR+2/CpQC5Q== + dependencies: + "@walletconnect/events" "^1.0.1" + "@walletconnect/time" "^1.0.2" + tslib "1.14.1" + +"@walletconnect/jsonrpc-provider@1.0.13": + version "1.0.13" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.13.tgz#9a74da648d015e1fffc745f0c7d629457f53648b" + integrity sha512-K73EpThqHnSR26gOyNEL+acEex3P7VWZe6KE12ZwKzAt2H4e5gldZHbjsu2QR9cLeJ8AXuO7kEMOIcRv1QEc7g== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.8" + "@walletconnect/safe-json" "^1.0.2" + tslib "1.14.1" + "@walletconnect/jsonrpc-provider@^1.0.3", "@walletconnect/jsonrpc-provider@^1.0.6": version "1.0.9" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-provider/-/jsonrpc-provider-1.0.9.tgz#ce5ab64dce6a739110aef204ffeedd668ad343d8" @@ -3935,6 +4001,14 @@ "@walletconnect/safe-json" "^1.0.1" tslib "1.14.1" +"@walletconnect/jsonrpc-types@1.0.3", "@walletconnect/jsonrpc-types@^1.0.3": + version "1.0.3" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.3.tgz#65e3b77046f1a7fa8347ae02bc1b841abe6f290c" + integrity sha512-iIQ8hboBl3o5ufmJ8cuduGad0CQm3ZlsHtujv9Eu16xq89q+BG7Nh5VLxxUgmtpnrePgFkTwXirCTkwJH1v+Yw== + dependencies: + keyvaluestorage-interface "^1.0.0" + tslib "1.14.1" + "@walletconnect/jsonrpc-types@^1.0.1", "@walletconnect/jsonrpc-types@^1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-types/-/jsonrpc-types-1.0.2.tgz#b79519f679cd6a5fa4a1bea888f27c1916689a20" @@ -3951,6 +4025,15 @@ "@walletconnect/environment" "^1.0.0" "@walletconnect/jsonrpc-types" "^1.0.1" +"@walletconnect/jsonrpc-utils@1.0.8", "@walletconnect/jsonrpc-utils@^1.0.8": + version "1.0.8" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.8.tgz#82d0cc6a5d6ff0ecc277cb35f71402c91ad48d72" + integrity sha512-vdeb03bD8VzJUL6ZtzRYsFMq1eZQcM3EAzT0a3st59dyLfJ0wq+tKMpmGH7HlB7waD858UWgfIcudbPFsbzVdw== + dependencies: + "@walletconnect/environment" "^1.0.1" + "@walletconnect/jsonrpc-types" "^1.0.3" + tslib "1.14.1" + "@walletconnect/jsonrpc-utils@^1.0.0", "@walletconnect/jsonrpc-utils@^1.0.4", "@walletconnect/jsonrpc-utils@^1.0.6": version "1.0.6" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-utils/-/jsonrpc-utils-1.0.6.tgz#7fa58e6671247e64e189828103282e6258f5330f" @@ -3960,7 +4043,18 @@ "@walletconnect/jsonrpc-types" "^1.0.2" tslib "1.14.1" -"@walletconnect/jsonrpc-ws-connection@^1.0.0", "@walletconnect/jsonrpc-ws-connection@^1.0.5", "@walletconnect/jsonrpc-ws-connection@^1.0.7": +"@walletconnect/jsonrpc-ws-connection@1.0.13": + version "1.0.13" + resolved "https://registry.npmjs.org/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.13.tgz#23b0cdd899801bfbb44a6556936ec2b93ef2adf4" + integrity sha512-mfOM7uFH4lGtQxG+XklYuFBj6dwVvseTt5/ahOkkmpcAEgz2umuzu7fTR+h5EmjQBdrmYyEBOWADbeaFNxdySg== + dependencies: + "@walletconnect/jsonrpc-utils" "^1.0.6" + "@walletconnect/safe-json" "^1.0.2" + events "^3.3.0" + tslib "1.14.1" + ws "^7.5.1" + +"@walletconnect/jsonrpc-ws-connection@^1.0.0", "@walletconnect/jsonrpc-ws-connection@^1.0.7": version "1.0.10" resolved "https://registry.yarnpkg.com/@walletconnect/jsonrpc-ws-connection/-/jsonrpc-ws-connection-1.0.10.tgz#04e04a7d8c70b27c386a1bdd9ff6511045da3c81" integrity sha512-/tidvjfCXZuYugjF5fOswsNDPoMo9QRML3DFQ0dfNUarL4f5HGqu8NDGerr2n0+4MOX23GsT6Vv2POSwFbvgGw== @@ -3999,6 +4093,31 @@ resolved "https://registry.yarnpkg.com/@walletconnect/mobile-registry/-/mobile-registry-2.0.0-alpha.20.tgz#5f6c6299ec149effaf2a278250198e7ecf33d6b7" integrity sha512-Qunuhp6dnjkIQgVWRRktLE3KGRbNw7AgZnLAoTsOakOLLVL+l9zOIUuowRcbCOpPzDK8Vb5Y2gWSPR1tce0hAg== +"@walletconnect/modal-core@2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@walletconnect/modal-core/-/modal-core-2.6.0.tgz#5e77559b8a7718db355b6cad81412137ca5ce7a9" + integrity sha512-95315iaiVlz72W8IWd0gvBGHenS9cbLXwURjbN6wm12KSc6zbQA6u2RO0SRlcwc+dQcNzhwB/ce7TZYLQUVMfw== + dependencies: + valtio "1.10.7" + +"@walletconnect/modal-ui@2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@walletconnect/modal-ui/-/modal-ui-2.6.0.tgz#f62b2d3d29d2cf83851f5302e03ed02062eaf9e6" + integrity sha512-A9Eohricm+VYTiUELVfum7AjyhxquWqy8ZhyVWXfm2ucpHnG0lhiY2mdLrqoE9EEW/ql4tmfYltX2ZD9wzTu9A== + dependencies: + "@walletconnect/modal-core" "2.6.0" + lit "2.7.6" + motion "10.16.2" + qrcode "1.5.3" + +"@walletconnect/modal@2.6.0": + version "2.6.0" + resolved "https://registry.npmjs.org/@walletconnect/modal/-/modal-2.6.0.tgz#c29b5b6da93e0f5e699a9fd6bb385001a8e4aa4b" + integrity sha512-hV8pfWvUDjanxfXZ7DouyboM+LOcSsJBX3cMsDyNwe0f19W/yIZTUQz9k4DkuIGjEQSUO9zfEX+BAYAeP27uag== + dependencies: + "@walletconnect/modal-core" "2.6.0" + "@walletconnect/modal-ui" "2.6.0" + "@walletconnect/qrcode-modal@2.0.0-alpha.20": version "2.0.0-alpha.20" resolved "https://registry.yarnpkg.com/@walletconnect/qrcode-modal/-/qrcode-modal-2.0.0-alpha.20.tgz#b80da728183b5a9340d99ff507bb40d14727d87f" @@ -4013,7 +4132,7 @@ safe-json-utils "^1.1.1" window-getters "^1.0.1" -"@walletconnect/relay-api@^1.0.2", "@walletconnect/relay-api@^1.0.5", "@walletconnect/relay-api@^1.0.7", "@walletconnect/relay-api@^1.0.9": +"@walletconnect/relay-api@^1.0.2", "@walletconnect/relay-api@^1.0.5", "@walletconnect/relay-api@^1.0.9": version "1.0.9" resolved "https://registry.yarnpkg.com/@walletconnect/relay-api/-/relay-api-1.0.9.tgz#f8c2c3993dddaa9f33ed42197fc9bfebd790ecaf" integrity sha512-Q3+rylJOqRkO1D9Su0DPE3mmznbAalYapJ9qmzDgK28mYF9alcP3UwG/og5V7l7CFOqzCLi7B8BvcBUrpDj0Rg== @@ -4040,6 +4159,13 @@ dependencies: tslib "1.14.1" +"@walletconnect/safe-json@^1.0.2": + version "1.0.2" + resolved "https://registry.npmjs.org/@walletconnect/safe-json/-/safe-json-1.0.2.tgz#7237e5ca48046e4476154e503c6d3c914126fa77" + integrity sha512-Ogb7I27kZ3LPC3ibn8ldyUr5544t3/STow9+lzz7Sfo808YD7SBWk7SAsdBFlYgP2zDRy2hS3sKRcuSRM0OTmA== + dependencies: + tslib "1.14.1" + "@walletconnect/sign-client@2.0.0-beta.102-6a30ee7.0": version "2.0.0-beta.102-6a30ee7.0" resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.0.0-beta.102-6a30ee7.0.tgz#21602a0f322f7fa9c289d54d589f1a43945b54a6" @@ -4060,22 +4186,20 @@ "@walletconnect/utils" "^2.0.0-beta.102-6a30ee7.0" ws "^8.3.0" -"@walletconnect/sign-client@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@walletconnect/sign-client/-/sign-client-2.1.4.tgz#678d35f29967bbedcc03d91cd5d47f872baba917" - integrity sha512-40fnyRUVNT+6AYQElQA/rUPHH9fdlbAd4hacpBFny2TbjGeGD/P+n4enhF5m0AQWwuF8zS8c7/qERvbWnaYnEg== +"@walletconnect/sign-client@2.9.1": + version "2.9.1" + resolved "https://registry.npmjs.org/@walletconnect/sign-client/-/sign-client-2.9.1.tgz#e4aa9c7b15849f450fdd1b03754a7517cb5c8811" + integrity sha512-Z7tFRrJ9btA1vU427vsjUS6cPlHQVcTWdKH90khEc2lv3dB6mU8FNO0VJsw+I2D7CW7WaMWF3nnj6Z1FfotbDg== dependencies: - "@walletconnect/core" "2.1.4" + "@walletconnect/core" "2.9.1" "@walletconnect/events" "^1.0.1" - "@walletconnect/heartbeat" "^1.0.1" - "@walletconnect/jsonrpc-provider" "^1.0.6" - "@walletconnect/jsonrpc-utils" "^1.0.4" + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-utils" "1.0.8" "@walletconnect/logger" "^2.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.1.4" - "@walletconnect/utils" "2.1.4" + "@walletconnect/types" "2.9.1" + "@walletconnect/utils" "2.9.1" events "^3.3.0" - pino "7.11.0" "@walletconnect/time@^1.0.1", "@walletconnect/time@^1.0.2": version "1.0.2" @@ -4084,34 +4208,34 @@ dependencies: tslib "1.14.1" -"@walletconnect/types@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.1.4.tgz#0db77adbaffd3a9bdf7e9d710d7e7352de542b16" - integrity sha512-n9fIk0z2fiGFlPgQcpurQ8WH7XF/blYaDIpr2ai3qdoo1N10yCsX1vj65L8t3zJAmSeHFoVJI9YVrSDj5eWjWw== +"@walletconnect/types@2.4.9", "@walletconnect/types@^2.0.0-alpha.20", "@walletconnect/types@^2.0.0-beta.102", "@walletconnect/types@^2.0.0-beta.102-6a30ee7.0", "@walletconnect/types@^2.0.0-beta.55": + version "2.4.9" + resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.4.9.tgz#db37d233dc79a4bf55d8a5a88cd088aebb766686" + integrity sha512-Mv1yH8MI52JPBi1Qj/iDjMewjBVhy4TrWPiPrSU0zcUSpUzCmai/BxGMjEGOJ8gsEIFwfEkFkZJQoza2fVtFLA== dependencies: "@walletconnect/events" "^1.0.1" - "@walletconnect/heartbeat" "^1.0.1" + "@walletconnect/heartbeat" "1.2.0" "@walletconnect/jsonrpc-types" "^1.0.2" "@walletconnect/keyvaluestorage" "^1.0.2" "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/types@2.4.9", "@walletconnect/types@^2.0.0-alpha.20", "@walletconnect/types@^2.0.0-beta.102", "@walletconnect/types@^2.0.0-beta.102-6a30ee7.0", "@walletconnect/types@^2.0.0-beta.55": - version "2.4.9" - resolved "https://registry.yarnpkg.com/@walletconnect/types/-/types-2.4.9.tgz#db37d233dc79a4bf55d8a5a88cd088aebb766686" - integrity sha512-Mv1yH8MI52JPBi1Qj/iDjMewjBVhy4TrWPiPrSU0zcUSpUzCmai/BxGMjEGOJ8gsEIFwfEkFkZJQoza2fVtFLA== +"@walletconnect/types@2.9.1": + version "2.9.1" + resolved "https://registry.npmjs.org/@walletconnect/types/-/types-2.9.1.tgz#cb32ff396cc8880a7395f28716d1e82f407e1372" + integrity sha512-xbGgTPuD6xsb7YMvCESBIH55cjB86QAnnVL50a/ED42YkQzDsOdJ0VGTbrm0tG5cxUOF933rpxZQjxGdP+ovww== dependencies: "@walletconnect/events" "^1.0.1" - "@walletconnect/heartbeat" "1.2.0" - "@walletconnect/jsonrpc-types" "^1.0.2" + "@walletconnect/heartbeat" "1.2.1" + "@walletconnect/jsonrpc-types" "1.0.3" "@walletconnect/keyvaluestorage" "^1.0.2" "@walletconnect/logger" "^2.0.1" events "^3.3.0" -"@walletconnect/utils@2.1.4": - version "2.1.4" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.1.4.tgz#992442d175f71cf7a685c5568d12bf25d719600e" - integrity sha512-g3XRGAgETQbwbZleQS1f4ZFl8X6jb+CaWVNM+vtVO9EvIUJAzE8TLq3H70v+KawRMaLXjgRc0z0W7Wg6bvct7g== +"@walletconnect/utils@2.4.9", "@walletconnect/utils@^2.0.0-alpha.20", "@walletconnect/utils@^2.0.0-beta.102-6a30ee7.0", "@walletconnect/utils@^2.0.0-beta.55": + version "2.4.9" + resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.4.9.tgz#d5237d89aa7e552a1779ec8d7b0bdb3bc12b141d" + integrity sha512-Mq5ZgXAn/SJZJYkINkl07YprnuvFRi+A7c3wapgvKjFB6msFMllBa4UFukON5+5ZCScSwPMwxoUX4EBjWnbaWA== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" @@ -4119,35 +4243,34 @@ "@stablelib/sha256" "1.0.1" "@stablelib/x25519" "^1.0.3" "@walletconnect/jsonrpc-utils" "^1.0.4" - "@walletconnect/relay-api" "^1.0.7" + "@walletconnect/relay-api" "^1.0.9" "@walletconnect/safe-json" "^1.0.1" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.1.4" + "@walletconnect/types" "2.4.9" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" query-string "7.1.1" - uint8arrays "3.1.0" + uint8arrays "^3.1.0" -"@walletconnect/utils@2.4.9", "@walletconnect/utils@^2.0.0-alpha.20", "@walletconnect/utils@^2.0.0-beta.102-6a30ee7.0", "@walletconnect/utils@^2.0.0-beta.55": - version "2.4.9" - resolved "https://registry.yarnpkg.com/@walletconnect/utils/-/utils-2.4.9.tgz#d5237d89aa7e552a1779ec8d7b0bdb3bc12b141d" - integrity sha512-Mq5ZgXAn/SJZJYkINkl07YprnuvFRi+A7c3wapgvKjFB6msFMllBa4UFukON5+5ZCScSwPMwxoUX4EBjWnbaWA== +"@walletconnect/utils@2.9.1": + version "2.9.1" + resolved "https://registry.npmjs.org/@walletconnect/utils/-/utils-2.9.1.tgz#92abc24b3af3ead42a3864e019dbf2f651ab2e47" + integrity sha512-tXeQVebF5oPBvhdmuUyVSkSIBYx/egIi4czav1QrnUpwrUS1LsrFhyWBxSbhN7TXY287ULWkEf6aFpWOHdp5EA== dependencies: "@stablelib/chacha20poly1305" "1.0.1" "@stablelib/hkdf" "1.0.1" "@stablelib/random" "^1.0.2" "@stablelib/sha256" "1.0.1" "@stablelib/x25519" "^1.0.3" - "@walletconnect/jsonrpc-utils" "^1.0.4" "@walletconnect/relay-api" "^1.0.9" - "@walletconnect/safe-json" "^1.0.1" + "@walletconnect/safe-json" "^1.0.2" "@walletconnect/time" "^1.0.2" - "@walletconnect/types" "2.4.9" + "@walletconnect/types" "2.9.1" "@walletconnect/window-getters" "^1.0.1" "@walletconnect/window-metadata" "^1.0.1" detect-browser "5.3.0" - query-string "7.1.1" + query-string "7.1.3" uint8arrays "^3.1.0" "@walletconnect/window-getters@^1.0.1": @@ -5226,6 +5349,15 @@ boolbase@^1.0.0, boolbase@~1.0.0: resolved "https://registry.yarnpkg.com/boolbase/-/boolbase-1.0.0.tgz#68dff5fbe60c51eb37725ea9e3ed310dcc1e776e" integrity sha512-JZOSA7Mo9sNGB8+UjSgzdLtokWAky1zbztM3WRLCbZ70/3cTANmQmOdR7y2g+J0e2WXywy1yS468tY+IruqEww== +borsh@0.7.0: + version "0.7.0" + resolved "https://registry.npmjs.org/borsh/-/borsh-0.7.0.tgz#6e9560d719d86d90dc589bca60ffc8a6c51fec2a" + integrity sha512-CLCsZGIBCFnPtkNnieW/a8wmreDmfUtjU2m9yHrzPXIlNbqVs0AQrSatSG6vdNYUqdc83tkQi2eHfF98ubzQLA== + dependencies: + bn.js "^5.2.0" + bs58 "^4.0.0" + text-encoding-utf-8 "^1.0.2" + borsh@^0.3.1: version "0.3.1" resolved "https://registry.yarnpkg.com/borsh/-/borsh-0.3.1.tgz#c31c3a149610e37913deada80e89073fb15cf55b" @@ -6034,9 +6166,9 @@ copy-descriptor@^0.1.0: resolved "https://registry.yarnpkg.com/copy-descriptor/-/copy-descriptor-0.1.1.tgz#676f6eb3c39997c2ee1ac3a924fd6124748f578d" integrity sha512-XgZ0pFcakEUlbwQEVNg3+QAis1FyTL3Qel9FYy8pSkQqoG3PNoT0bOCQtOXcOkur21r2Eq2kI+IE+gsmAEVlYw== -copy-to-clipboard@^3.3.1, copy-to-clipboard@^3.3.3: +copy-to-clipboard@3.3.3, copy-to-clipboard@^3.3.1: version "3.3.3" - resolved "https://registry.yarnpkg.com/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" + resolved "https://registry.npmjs.org/copy-to-clipboard/-/copy-to-clipboard-3.3.3.tgz#55ac43a1db8ae639a4bd99511c148cdd1b83a1b0" integrity sha512-2KV8NhB5JqC3ky0r9PMCAZKbUHSwtEo4CwCs0KXgruG43gX5PMqDEBbVU4OUzw2MuAWUfsuFmWvEKG5QRfSnJA== dependencies: toggle-selection "^1.0.6" @@ -7900,9 +8032,9 @@ ethereumjs-util@^7.1.0: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@^5.7.2: +ethers@5.7.2, ethers@^5.7.2: version "5.7.2" - resolved "https://registry.yarnpkg.com/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" + resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== dependencies: "@ethersproject/abi" "5.7.0" @@ -8895,6 +9027,11 @@ hex-color-regex@^1.1.0: resolved "https://registry.yarnpkg.com/hex-color-regex/-/hex-color-regex-1.1.0.tgz#4c06fccb4602fe2602b3c93df82d7e7dbf1a8a8e" integrity sha512-l9sfDFsuqtOqKDsQdqrMRk0U85RZc0RtOR9yPI7mRVOa4FsR/BVnZ0shmQRM96Ji99kYZP/7hn1cedc1+ApsTQ== +hey-listen@^1.0.8: + version "1.0.8" + resolved "https://registry.npmjs.org/hey-listen/-/hey-listen-1.0.8.tgz#8e59561ff724908de1aa924ed6ecc84a56a9aa68" + integrity sha512-COpmrF2NOg4TBWUJ5UVyaCU2A88wEMkUPK4hNqyCkqHbxT92BbvfjoSozkAIIm6XhicGlJHhFdullInrdhwU8Q== + history@^4.9.0: version "4.10.1" resolved "https://registry.yarnpkg.com/history/-/history-4.10.1.tgz#33371a65e3a83b267434e2b3f3b1b4c58aad4cf3" @@ -9554,10 +9691,10 @@ is-map@^2.0.1, is-map@^2.0.2: resolved "https://registry.yarnpkg.com/is-map/-/is-map-2.0.2.tgz#00922db8c9bf73e81b7a335827bc2a43f2b91127" integrity sha512-cOZFQQozTha1f4MxLFzlgKYPTyj26picdZTx82hbc/Xf4K/tZOOXSCkMvU4pKioRXGDLJRn0GM7Upe7kR721yg== -is-mobile@^3.1.1: - version "3.1.1" - resolved "https://registry.yarnpkg.com/is-mobile/-/is-mobile-3.1.1.tgz#3b9e48f40068e4ea2da411f5009779844ce8d6aa" - integrity sha512-RRoXXR2HNFxNkUnxtaBdGBXtFlUMFa06S0NUKf/LCF+MuGLu13gi9iBCkoEmc6+rpXuwi5Mso5V8Zf7mNynMBQ== +is-mobile@4.0.0: + version "4.0.0" + resolved "https://registry.npmjs.org/is-mobile/-/is-mobile-4.0.0.tgz#bba396eb9656e2739afde3053d7191da310fc758" + integrity sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew== is-module@^1.0.0: version "1.0.0" @@ -10722,7 +10859,7 @@ js-sdsl@^4.1.4: resolved "https://registry.yarnpkg.com/js-sdsl/-/js-sdsl-4.3.0.tgz#aeefe32a451f7af88425b11fdb5f58c90ae1d711" integrity sha512-mifzlm2+5nZ+lEcLJMoBK0/IH/bDg8XnJfd/Wq6IP+xoCjLZsTOnV2QpxlVbX9bMnkl5PdEjNtBJ9Cj1NjifhQ== -js-sha256@^0.9.0: +js-sha256@0.9.0, js-sha256@^0.9.0: version "0.9.0" resolved "https://registry.yarnpkg.com/js-sha256/-/js-sha256-0.9.0.tgz#0b89ac166583e91ef9123644bd3c5334ce9d0966" integrity sha512-sga3MHh9sgQN2+pJ9VYZ+1LPwXOxuBJBA5nrR5/ofPfuiJBE2hnjsaN8se8JznOmGLN2p49Pe5U/ttafcs/apA== @@ -11011,6 +11148,31 @@ lines-and-columns@^1.1.6: resolved "https://registry.yarnpkg.com/lines-and-columns/-/lines-and-columns-1.2.4.tgz#eca284f75d2965079309dc0ad9255abb2ebc1632" integrity sha512-7ylylesZQ/PV29jhEDl3Ufjo6ZX7gCqJr5F7PKrqc93v7fzSymt1BpwEU8nAUXs8qzzvqhbjhK5QZg6Mt/HkBg== +lit-element@^3.3.0: + version "3.3.3" + resolved "https://registry.npmjs.org/lit-element/-/lit-element-3.3.3.tgz#10bc19702b96ef5416cf7a70177255bfb17b3209" + integrity sha512-XbeRxmTHubXENkV4h8RIPyr8lXc+Ff28rkcQzw3G6up2xg5E8Zu1IgOWIwBLEQsu3cOVFqdYwiVi0hv0SlpqUA== + dependencies: + "@lit-labs/ssr-dom-shim" "^1.1.0" + "@lit/reactive-element" "^1.3.0" + lit-html "^2.8.0" + +lit-html@^2.7.0, lit-html@^2.8.0: + version "2.8.0" + resolved "https://registry.npmjs.org/lit-html/-/lit-html-2.8.0.tgz#96456a4bb4ee717b9a7d2f94562a16509d39bffa" + integrity sha512-o9t+MQM3P4y7M7yNzqAyjp7z+mQGa4NS4CxiyLqFPyFWyc4O+nodLrkrxSaCTrla6M5YOLaT3RpbbqjszB5g3Q== + dependencies: + "@types/trusted-types" "^2.0.2" + +lit@2.7.6: + version "2.7.6" + resolved "https://registry.npmjs.org/lit/-/lit-2.7.6.tgz#810007b876ed43e0c70124de91831921598b1665" + integrity sha512-1amFHA7t4VaaDe+vdQejSVBklwtH9svGoG6/dZi9JhxtJBBlqY5D1RV7iLUYY0trCqQc4NfhYYZilZiVHt7Hxg== + dependencies: + "@lit/reactive-element" "^1.6.0" + lit-element "^3.3.0" + lit-html "^2.7.0" + loader-runner@^4.2.0: version "4.3.0" resolved "https://registry.yarnpkg.com/loader-runner/-/loader-runner-4.3.0.tgz#c1b4a163b99f614830353b16755e7149ac2314e1" @@ -11459,6 +11621,18 @@ moment@^2.29.1: resolved "https://registry.yarnpkg.com/moment/-/moment-2.29.4.tgz#3dbe052889fe7c1b2ed966fcb3a77328964ef108" integrity sha512-5LC9SOxjSc2HF6vO2CyuTDNivEdoz2IvyJJGj6X8DJ0eFyfszE0QiEd+iXmBvUP3WHxSjFH/vIsA0EN00cgr8w== +motion@10.16.2: + version "10.16.2" + resolved "https://registry.npmjs.org/motion/-/motion-10.16.2.tgz#7dc173c6ad62210a7e9916caeeaf22c51e598d21" + integrity sha512-p+PurYqfUdcJZvtnmAqu5fJgV2kR0uLFQuBKtLeFVTrYEVllI99tiOTSefVNYuip9ELTEkepIIDftNdze76NAQ== + dependencies: + "@motionone/animation" "^10.15.1" + "@motionone/dom" "^10.16.2" + "@motionone/svelte" "^10.16.2" + "@motionone/types" "^10.15.1" + "@motionone/utils" "^10.15.1" + "@motionone/vue" "^10.16.2" + move-file@^2.0.0: version "2.1.0" resolved "https://registry.yarnpkg.com/move-file/-/move-file-2.1.0.tgz#3bec9d34fbe4832df6865f112cda4492b56e8507" @@ -11585,9 +11759,9 @@ near-hd-key@^1.2.1: create-hmac "1.1.7" tweetnacl "1.0.3" -near-seed-phrase@^0.2.0: +near-seed-phrase@0.2.0, near-seed-phrase@^0.2.0: version "0.2.0" - resolved "https://registry.yarnpkg.com/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz#fb7cf89682112b1160ab68abb50dc821f49be18a" + resolved "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz#fb7cf89682112b1160ab68abb50dc821f49be18a" integrity sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ== dependencies: bip39-light "^1.0.7" @@ -13530,6 +13704,11 @@ proxy-addr@~2.0.7: forwarded "0.2.0" ipaddr.js "1.9.1" +proxy-compare@2.5.1: + version "2.5.1" + resolved "https://registry.npmjs.org/proxy-compare/-/proxy-compare-2.5.1.tgz#17818e33d1653fbac8c2ec31406bce8a2966f600" + integrity sha512-oyfc0Tx87Cpwva5ZXezSp5V9vht1c7dZBhvuV/y3ctkgMVUmiAGDVeeB0dKhGSyT0v1ZTEQYpe/RXlBVBNuCLA== + psl@^1.1.28, psl@^1.1.33: version "1.9.0" resolved "https://registry.yarnpkg.com/psl/-/psl-1.9.0.tgz#d0df2a137f00794565fcaf3b2c00cd09f8d5a5a7" @@ -13595,7 +13774,17 @@ q@^1.1.2: resolved "https://registry.yarnpkg.com/q/-/q-1.5.1.tgz#7e32f75b41381291d04611f1bf14109ac00651d7" integrity sha512-kV/CThkXo6xyFEZUugw/+pIOywXcDbFYgSct5cT3gqlbkBE1SJdwy6UQoZvodiWF/ckQLZyDE/Bu1M6gVu5lVw== -qrcode@^1.4.4, qrcode@^1.5.1: +qrcode@1.5.3: + version "1.5.3" + resolved "https://registry.npmjs.org/qrcode/-/qrcode-1.5.3.tgz#03afa80912c0dccf12bc93f615a535aad1066170" + integrity sha512-puyri6ApkEHYiVl4CFzo1tDkAZ+ATcnbJrJ6RiBM1Fhctdn/ix9MTE3hRph33omisEbC/2fcfemsseiKgBPKZg== + dependencies: + dijkstrajs "^1.0.1" + encode-utf8 "^1.0.3" + pngjs "^5.0.0" + yargs "^15.3.1" + +qrcode@^1.4.4: version "1.5.1" resolved "https://registry.yarnpkg.com/qrcode/-/qrcode-1.5.1.tgz#0103f97317409f7bc91772ef30793a54cd59f0cb" integrity sha512-nS8NJ1Z3md8uTjKtP+SGGhfqmTCs5flU/xR623oI0JX+Wepz9R8UrRVCTBTJm3qGw3rH6jJ6MUHjkDx15cxSSg== @@ -13627,22 +13816,22 @@ query-string@7.1.1: split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -query-string@^6.12.1: - version "6.14.1" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" - integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== +query-string@7.1.3, query-string@^7.1.1: + version "7.1.3" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" + integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== dependencies: - decode-uri-component "^0.2.0" + decode-uri-component "^0.2.2" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" -query-string@^7.1.1: - version "7.1.3" - resolved "https://registry.yarnpkg.com/query-string/-/query-string-7.1.3.tgz#a1cf90e994abb113a325804a972d98276fe02328" - integrity sha512-hh2WYhq4fi8+b+/2Kg9CEge4fDPvHS534aOOvOZeQ3+Vf2mCFsaFBYj0i+iXcAq6I9Vzp5fjMFBlONvayDC1qg== +query-string@^6.12.1: + version "6.14.1" + resolved "https://registry.yarnpkg.com/query-string/-/query-string-6.14.1.tgz#7ac2dca46da7f309449ba0f86b1fd28255b0c86a" + integrity sha512-XDxAeVmpfu1/6IjyT/gXHOl+S0vQ9owggJ30hhWKdHAsNPOcasn5o9BW0eejZqL2e4vMjhAxoW3jVHcD6mbcYw== dependencies: - decode-uri-component "^0.2.2" + decode-uri-component "^0.2.0" filter-obj "^1.1.0" split-on-first "^1.0.0" strict-uri-encode "^2.0.0" @@ -14597,7 +14786,7 @@ rxjs@7.8.1: dependencies: tslib "^2.1.0" -rxjs@^7.5.5, rxjs@^7.8.0: +rxjs@^7.5.5: version "7.8.0" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== @@ -15951,6 +16140,11 @@ tryer@^1.0.1: resolved "https://registry.yarnpkg.com/tryer/-/tryer-1.0.1.tgz#f2c85406800b9b0f74c9f7465b81eaad241252f8" integrity sha512-c3zayb8/kWWpycWYg87P71E1S1ZL6b6IJxfb5fvsUgsf0S2MVGaDhDXXjDMpdCpfWXqptc+4mXwmiy1ypXqRAA== +ts-essentials@7.0.3: + version "7.0.3" + resolved "https://registry.npmjs.org/ts-essentials/-/ts-essentials-7.0.3.tgz#686fd155a02133eedcc5362dc8b5056cde3e5a38" + integrity sha512-8+gr5+lqO3G84KdiTSMRLtuyJ+nTBVRKuCrK4lidMPdVeEp0uqC875uE5NMcaA7YYMN7XsNiFQuMvasF8HT/xQ== + ts-jest@^26.5.4: version "26.5.6" resolved "https://registry.yarnpkg.com/ts-jest/-/ts-jest-26.5.6.tgz#c32e0746425274e1dfe333f43cd3c800e014ec35" @@ -16011,6 +16205,11 @@ tslib@^2.0.0, tslib@^2.0.3, tslib@^2.1.0: resolved "https://registry.yarnpkg.com/tslib/-/tslib-2.5.0.tgz#42bfed86f5787aeb41d031866c8f402429e0fddf" integrity sha512-336iVw3rtn2BUK7ORdIAHTyxHGRIHVReokCR3XjbckJMK7ms8FysBfhLR8IXnAgy7T0PTPNBWKiH514FOW/WSg== +tslib@^2.3.1: + version "2.6.2" + resolved "https://registry.npmjs.org/tslib/-/tslib-2.6.2.tgz#703ac29425e7b37cd6fd456e92404d46d1f3e4ae" + integrity sha512-AEYxH93jGFPn/a2iVAwW87VuUIkR1FVUKB77NwMF7nBTDkDrrT/Hpt/IrCJ0QXhW27jTBDcf5ZY7w6RiqTMw2Q== + tsutils@^3.21.0: version "3.21.0" resolved "https://registry.yarnpkg.com/tsutils/-/tsutils-3.21.0.tgz#b48717d394cea6c1e096983eed58e9d61715b623" @@ -16128,13 +16327,6 @@ u3@^0.1.1: resolved "https://registry.yarnpkg.com/u3/-/u3-0.1.1.tgz#5f52044f42ee76cd8de33148829e14528494b73b" integrity sha512-+J5D5ir763y+Am/QY6hXNRlwljIeRMZMGs0cT6qqZVVzzT3X3nFPXVyPOFRMOR4kupB0T8JnCdpWdp6Q/iXn3w== -uint8arrays@3.1.0: - version "3.1.0" - resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.0.tgz#8186b8eafce68f28bd29bd29d683a311778901e2" - integrity sha512-ei5rfKtoRO8OyOIor2Rz5fhzjThwIHJZ3uyDPnDHTXbP0aMQ1RN/6AI5B5d9dBxJOU+BvOAk7ZQ1xphsX8Lrog== - dependencies: - multiformats "^9.4.2" - uint8arrays@^3.0.0, uint8arrays@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/uint8arrays/-/uint8arrays-3.1.1.tgz#2d8762acce159ccd9936057572dade9459f65ae0" @@ -16294,6 +16486,11 @@ url@^0.11.0: punycode "1.3.2" querystring "0.2.0" +use-sync-external-store@1.2.0: + version "1.2.0" + resolved "https://registry.npmjs.org/use-sync-external-store/-/use-sync-external-store-1.2.0.tgz#7dbefd6ef3fe4e767a0cf5d7287aacfb5846928a" + integrity sha512-eEgnFxGQ1Ife9bzYs6VLi8/4X6CObHMw9Qr9tPY43iKwsPw8xE8+EFsf/2cFZ5S3esXgpWgtSCtLNS41F+sKPA== + use@^3.1.0: version "3.1.1" resolved "https://registry.yarnpkg.com/use/-/use-3.1.1.tgz#d50c8cac79a19fbc20f2911f56eb973f4e10070f" @@ -16406,6 +16603,14 @@ validate-npm-package-license@^3.0.1: spdx-correct "^3.0.0" spdx-expression-parse "^3.0.0" +valtio@1.10.7: + version "1.10.7" + resolved "https://registry.npmjs.org/valtio/-/valtio-1.10.7.tgz#0843b8bc8382060b3e6a0d31be645c3ef9a7f7dc" + integrity sha512-XUwXJ9twXqwfVhXlcbYlCcRUUSOmp8RcJODVAxPrUZc2jh3com13B49MOL/cbgcTOX+5yf7wdMqbFGR7ba2sPg== + dependencies: + proxy-compare "2.5.1" + use-sync-external-store "1.2.0" + value-equal@^1.0.1: version "1.0.1" resolved "https://registry.yarnpkg.com/value-equal/-/value-equal-1.0.1.tgz#1e0b794c734c5c0cade179c437d356d931a34d6c" From 1285a5e2154cb7f1e747a7c76e7fe48663cb1db5 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 18 Oct 2023 17:14:09 +0800 Subject: [PATCH 166/204] upgrated near-wallet-selector --- package.json | 24 ++++----- yarn.lock | 142 +++++++++++++++++++++++++-------------------------- 2 files changed, 83 insertions(+), 83 deletions(-) diff --git a/package.json b/package.json index c4de48626..2e6e1ad6f 100644 --- a/package.json +++ b/package.json @@ -64,18 +64,18 @@ "@babel/plugin-transform-modules-commonjs": "^7.17.9", "@ledgerhq/devices": "^7.0.0", "@metamask/detect-provider": "^2.0.0", - "@near-wallet-selector/core": "8.5.3", - "@near-wallet-selector/here-wallet": "^8.5.3", - "@near-wallet-selector/ledger": "^8.5.3", - "@near-wallet-selector/math-wallet": "8.5.3", - "@near-wallet-selector/meteor-wallet": "8.5.3", - "@near-wallet-selector/modal-ui": "8.5.3", - "@near-wallet-selector/my-near-wallet": "8.5.3", - "@near-wallet-selector/near-wallet": "8.5.3", - "@near-wallet-selector/neth": "8.5.3", - "@near-wallet-selector/nightly": "8.5.3", - "@near-wallet-selector/sender": "8.5.3", - "@near-wallet-selector/wallet-connect": "8.5.3", + "@near-wallet-selector/core": "^8.5.4", + "@near-wallet-selector/here-wallet": "^8.5.4", + "@near-wallet-selector/ledger": "^8.5.4", + "@near-wallet-selector/math-wallet": "^8.5.4", + "@near-wallet-selector/meteor-wallet": "^8.5.4", + "@near-wallet-selector/modal-ui": "^8.5.4", + "@near-wallet-selector/my-near-wallet": "^8.5.4", + "@near-wallet-selector/near-wallet": "^8.5.4", + "@near-wallet-selector/neth": "^8.5.4", + "@near-wallet-selector/nightly": "^8.5.4", + "@near-wallet-selector/sender": "^8.5.4", + "@near-wallet-selector/wallet-connect": "^8.5.4", "@react-icons/all-files": "https://github.com/react-icons/react-icons/releases/download/v4.7.1/react-icons-all-files-4.7.1.tgz", "@svgr/parcel-plugin-svgr": "^5.5.0", "@transak/transak-sdk": "1.2.2", diff --git a/yarn.lock b/yarn.lock index 796220c0b..82acc55d6 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2414,129 +2414,129 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@near-wallet-selector/core@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-8.5.3.tgz#1cb6df1a3705f88baab4edef23b5155cf0e52a6a" - integrity sha512-KN0fgrkUz9jpbSImPI4RDYM/S9ARSIcjEP49SVEJp5AQlULrpazJoMRnAWgXRjzvAncSeDOVjb8fAkqxKl3YRQ== +"@near-wallet-selector/core@8.5.4", "@near-wallet-selector/core@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-8.5.4.tgz#5783a835ea7422709e5643775fee61a2b81500e6" + integrity sha512-GxoRU7epI4/7JVzJzROgFxwLlbEN/snrc2qGQ2ROYBojvq6YoNq/KIyLXiM8WlAPw3gbKnuLJj4uHquyo/NOJw== dependencies: borsh "0.7.0" events "3.3.0" js-sha256 "0.9.0" rxjs "7.8.1" -"@near-wallet-selector/here-wallet@^8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/here-wallet/-/here-wallet-8.5.3.tgz#99e06a76b304316220fd6a709d5b53cf1476c00d" - integrity sha512-ZYmLo4t7S8EwYH2QLVOQs9Zg9AbJEkhvRHsz+TYIAzwDGHxYxNJ7BmohbtE7oICOSd5us4iYmLxn6PETmuICiA== +"@near-wallet-selector/here-wallet@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/here-wallet/-/here-wallet-8.5.4.tgz#8372dd398475561aba12d1f05d724c7670d2cdd7" + integrity sha512-d/erBeUYuTYRqv2LpXZwLSvOgjcn5hTJps47BVW7mc6fGB5kJ5jO8gxk++tHaZMb0ONQnIZholu1qkziURXxnQ== dependencies: "@here-wallet/core" "1.5.1" - "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/core" "8.5.4" bn.js "5.2.1" -"@near-wallet-selector/ledger@^8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/ledger/-/ledger-8.5.3.tgz#698965bd6dc106854c6ce4eac70b7a26f0b6287e" - integrity sha512-X8fOpENrGoyqxoqQ2B4qrXnUeE2TujmrKG4IUQybMgBfV1vX+nWmwIZDdgy1AQ49h+tArgcWQ54VfaExjAvzoA== +"@near-wallet-selector/ledger@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/ledger/-/ledger-8.5.4.tgz#e5528000baa11e960236f9ce6ca570f0a7a3dde2" + integrity sha512-+MPgwsWyQ2kOwRtkq72Y6/IrccyNVsOcdx+tjstKAjUmrrt5KNYty7sE4tdb9wdDTM9ARPzMCan6/LTspo+MDw== dependencies: "@ledgerhq/hw-transport" "6.27.1" "@ledgerhq/hw-transport-webhid" "6.27.1" - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" bn.js "5.2.1" is-mobile "4.0.0" ts-essentials "7.0.3" -"@near-wallet-selector/math-wallet@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/math-wallet/-/math-wallet-8.5.3.tgz#ce556f9883418ded6b7a27da18d17055704ad476" - integrity sha512-Bkl/K1fFsvjlZcWZEbqk9sQcx6JdNJgcNxK/BE1ptu7csLPqZM9kV8N8Yx1/CzucWSgc7QKz1P6ygYG108EJaw== +"@near-wallet-selector/math-wallet@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/math-wallet/-/math-wallet-8.5.4.tgz#c7caeb390c98cb3976538e0af122c4a27d38219d" + integrity sha512-/av/tk+rkKEpfvV2vad+8jSh8m1AiZI1meODfv51a8Ll6QduVVuD+JeJMniSCmEF1c9ZwaZYvRGT1/eOVFGWIQ== dependencies: - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" is-mobile "4.0.0" -"@near-wallet-selector/meteor-wallet@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/meteor-wallet/-/meteor-wallet-8.5.3.tgz#4f493616813013978716e3e46242477484c49e12" - integrity sha512-OryLwNuPBO9dPSA/j43k4+ebSoqqhogIOIayoBJPt2AwVp8rSLlkoQDbuL3wwuMiA5di6A2HCo5pqA7b0NXj5A== +"@near-wallet-selector/meteor-wallet@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/meteor-wallet/-/meteor-wallet-8.5.4.tgz#426026022352750bcefaa2aeef5388d4b7b7c623" + integrity sha512-f387DVX2e77U6bezpJc9mhWUjqxHS5CdqAN0aWGvmBC4RrtQBWc2BRX11EyfBEcq6uo9rFvoi1G7LE6+HNhUiA== dependencies: "@meteorwallet/sdk" "0.8.0" - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" -"@near-wallet-selector/modal-ui@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/modal-ui/-/modal-ui-8.5.3.tgz#9fba8e769ad3c113584e8fdf3b3650f9be08b811" - integrity sha512-GkPgjnjU1+JcYQ1uFnQVYkMGhWvf0XZF9ZSYYa6dakm3J6ZEdH3RmPqKnPAx5Q6dJCQHC/WrqCfAZzZ/LclS5Q== +"@near-wallet-selector/modal-ui@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/modal-ui/-/modal-ui-8.5.4.tgz#aed98fbaa1e86503a1987ba74b1515986a623991" + integrity sha512-0AYlKIyCze5kvBXbt2b3awwkhfy7GL0C9SUTyR/C8UQn8KPYWUplt4KPmCTlkBRGOLNcyIKwjsW2ZYxZP77m5g== dependencies: - "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/core" "8.5.4" copy-to-clipboard "3.3.3" qrcode "1.5.3" react "18.2.0" react-dom "18.2.0" -"@near-wallet-selector/my-near-wallet@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/my-near-wallet/-/my-near-wallet-8.5.3.tgz#b83907ab7e314c110be944361028df2db5413ff5" - integrity sha512-3fxmYYlRl2iG9b/rAMdA9p8Tz5rgQwFupzzTSF4UaRKYpxsXBeWKmWgZeUg5xvr08Z9cGjEflnVqQ7nW6cNDmQ== +"@near-wallet-selector/my-near-wallet@8.5.4", "@near-wallet-selector/my-near-wallet@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/my-near-wallet/-/my-near-wallet-8.5.4.tgz#129d9b421ce0a2a8bd361eccb3122fa97d3180f9" + integrity sha512-3ltVyinDEcIzmaTAWW5hyfosV8lGNDJPHiiRiF5tr8j7Y9/j4kABw9TUsKCf2VE8ubIolZIKoDU7MWTmpDR8uw== dependencies: - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" -"@near-wallet-selector/near-wallet@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/near-wallet/-/near-wallet-8.5.3.tgz#86535114e3e06ae9d90f35fa7fdf95b9801e2a09" - integrity sha512-1VAZUuaMMqHSRRruI/wzyfmvFo/n3L/CNiUwq3MbwGgmIgSvaSP6tBRibumw2oHkBycjcYTQSed/dLLs034zSQ== +"@near-wallet-selector/near-wallet@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/near-wallet/-/near-wallet-8.5.4.tgz#03523b67fff860cee10b2f92fe8ba258aa5a82a5" + integrity sha512-RuH5B8YyRVCOUAxFuL2illfdrhw9b5H/EPry4IuYh+dfdCFyeIrNwmZ3krtnr5S4bgFL0dfeJFANNpRyt9OJpg== dependencies: - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/my-near-wallet" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/my-near-wallet" "8.5.4" -"@near-wallet-selector/neth@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-8.5.3.tgz#fdf2f7fb05df0225e3453316b3a5576871c0db28" - integrity sha512-/K8NvpaEMpyhE1lxg8LQVNotws0hQHOiP5kShEdyqwUONpSz2cE+bs+srbElAsZXmTcLP2UhUrqHUV3MKT62gA== +"@near-wallet-selector/neth@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-8.5.4.tgz#08002bc148331d0ebd3dc4dc16ba81a9469adf04" + integrity sha512-zmmXGKC4u8MNs4RDhURa4xntlxLv0Ih8VcnFW0oe++hfuqFxp3dASbO1o37Mfz1A6Fc14ed0da/FhPM3T+jAfw== dependencies: "@metamask/detect-provider" "2.0.0" - "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/core" "8.5.4" bn.js "5.2.1" ethers "5.7.2" is-mobile "4.0.0" near-seed-phrase "0.2.0" -"@near-wallet-selector/nightly@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/nightly/-/nightly-8.5.3.tgz#2d50f6063f85d2b46a2b2db9f1cf1dfe0886c813" - integrity sha512-tEhx55RrAzOqtsIJM/mh4OA19h+URckefR2E6RSnScAnc/4UnC3K1zXQqZaO9SYWOttbM9w5EbLQQtrNgkFT9A== +"@near-wallet-selector/nightly@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/nightly/-/nightly-8.5.4.tgz#3a6dd274cccb841ceb53bf100a8ac5dd8f415d6b" + integrity sha512-NallFyNER5RPN1BAkWEYFX8iFqPZbT05K9sPd6n7xD0gHDPmKADqzcybqv021pXXhyZOVdu7oZy1oNDfVTrMVQ== dependencies: - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" is-mobile "4.0.0" -"@near-wallet-selector/sender@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/sender/-/sender-8.5.3.tgz#bfee3b1bba69b660f5fc12cc3cdfe5eedc34fc25" - integrity sha512-Z3i0mzLx8MOWsIFgXTVu+1j6a3RN29wq3JSqY6WC/dlfpBj3qHPTJCVqccCEBMbqkNJlOrYgYraPxQLd3x/2IQ== +"@near-wallet-selector/sender@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/sender/-/sender-8.5.4.tgz#0bec2baa4d9ca0c139a691149107567d637f9d7a" + integrity sha512-9LdPQRxilPUtiMK5XRdrtNsJpC1a9uYQirJq+IO3NJ3p++Zfwuua20IXVPSSeaxH1LLB66StOoYQj3V9jiGW4w== dependencies: - "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/core" "8.5.4" is-mobile "4.0.0" -"@near-wallet-selector/wallet-connect@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-connect/-/wallet-connect-8.5.3.tgz#6e6a293bea64b50edb80cb3beb306c234f8c0e17" - integrity sha512-hqAWbwUh/lEqN871xhABsiEwst88wWh3P17HfHEHKehOK2LL+HaN4UZrF2Mv8/Sy4K2uCIMLBTIZL51sH2/DzQ== +"@near-wallet-selector/wallet-connect@^8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-connect/-/wallet-connect-8.5.4.tgz#a97e9b86420a5e6edfea9b26994d8f2db7283151" + integrity sha512-c+6AMiwShrRb6qVX9Xd0elp1MlFy8onE9pUyryv1HQx4CnhMBo8mRTYaia7ixBlmO5nPU1t3H/SYx+0yW0OAmg== dependencies: - "@near-wallet-selector/core" "8.5.3" - "@near-wallet-selector/wallet-utils" "8.5.3" + "@near-wallet-selector/core" "8.5.4" + "@near-wallet-selector/wallet-utils" "8.5.4" "@walletconnect/modal" "2.6.0" "@walletconnect/sign-client" "2.9.1" "@walletconnect/types" "2.9.1" -"@near-wallet-selector/wallet-utils@8.5.3": - version "8.5.3" - resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-utils/-/wallet-utils-8.5.3.tgz#add49dfeeeebbfb0f8ec2eb3b0ed09296acf755c" - integrity sha512-Ag5bRtQwkPciMSeTrd6TOQyAV3tUWSQTRsZxFqy+aYsQA6vBSI3qsXLSlbjvIQGA8vRgyQdzkkilHWu3gC/Mrg== +"@near-wallet-selector/wallet-utils@8.5.4": + version "8.5.4" + resolved "https://registry.npmjs.org/@near-wallet-selector/wallet-utils/-/wallet-utils-8.5.4.tgz#4348d957a09802b22ece5206668df13998f37d26" + integrity sha512-mxod9Vf8z9COiBAlKBVVAB2C6fOKKYjatuTPTnHX1h4tIkTjzGTKcqjAz3LK2NEFSFeIiKxjjFwcvSgD//8c1g== dependencies: - "@near-wallet-selector/core" "8.5.3" + "@near-wallet-selector/core" "8.5.4" bn.js "5.2.1" "@nicolo-ribaudo/eslint-scope-5-internals@5.1.1-v1": From 62eafe59d140b0fec5537d668d9b5febb404ced0 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 18 Oct 2023 17:58:56 +0800 Subject: [PATCH 167/204] update neth wallet version --- package.json | 2 +- yarn.lock | 40 ++++++++++++++++++++++++++-------------- 2 files changed, 27 insertions(+), 15 deletions(-) diff --git a/package.json b/package.json index 2e6e1ad6f..8f35fd19d 100644 --- a/package.json +++ b/package.json @@ -72,7 +72,7 @@ "@near-wallet-selector/modal-ui": "^8.5.4", "@near-wallet-selector/my-near-wallet": "^8.5.4", "@near-wallet-selector/near-wallet": "^8.5.4", - "@near-wallet-selector/neth": "^8.5.4", + "@near-wallet-selector/neth": "^7.9.1", "@near-wallet-selector/nightly": "^8.5.4", "@near-wallet-selector/sender": "^8.5.4", "@near-wallet-selector/wallet-connect": "^8.5.4", diff --git a/yarn.lock b/yarn.lock index 82acc55d6..0aa84938e 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2324,7 +2324,7 @@ dependencies: "@lit-labs/ssr-dom-shim" "^1.0.0" -"@metamask/detect-provider@2.0.0", "@metamask/detect-provider@^2.0.0": +"@metamask/detect-provider@^2.0.0": version "2.0.0" resolved "https://registry.yarnpkg.com/@metamask/detect-provider/-/detect-provider-2.0.0.tgz#4bc2795e5e6f7d8b84b2e845058d2f222c99917d" integrity sha512-sFpN+TX13E9fdBDh9lvQeZdJn4qYoRb/6QF2oZZK/Pn559IhCFacPMU1rMuqyXoFQF3JSJfii2l98B87QDPeCQ== @@ -2414,6 +2414,13 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" +"@near-wallet-selector/core@7.9.1": + version "7.9.1" + resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-7.9.1.tgz#d13d20724bd105e85b141bb2c7f35f4c07a37cad" + integrity sha512-BJGsTK0snZAU1Je+W9TFJG5KW+jk+Ots9LuxsALZ/wZpqSeGT+w4xw36TZt3QsEm+I84im1+L+PIIW/bM2A9rQ== + dependencies: + rxjs "^7.8.0" + "@near-wallet-selector/core@8.5.4", "@near-wallet-selector/core@^8.5.4": version "8.5.4" resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-8.5.4.tgz#5783a835ea7422709e5643775fee61a2b81500e6" @@ -2491,17 +2498,17 @@ "@near-wallet-selector/core" "8.5.4" "@near-wallet-selector/my-near-wallet" "8.5.4" -"@near-wallet-selector/neth@^8.5.4": - version "8.5.4" - resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-8.5.4.tgz#08002bc148331d0ebd3dc4dc16ba81a9469adf04" - integrity sha512-zmmXGKC4u8MNs4RDhURa4xntlxLv0Ih8VcnFW0oe++hfuqFxp3dASbO1o37Mfz1A6Fc14ed0da/FhPM3T+jAfw== +"@near-wallet-selector/neth@7.9.1": + version "7.9.1" + resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-7.9.1.tgz#e67718a004e990ddbb85ee3c1ac3bf113123792f" + integrity sha512-1QW5Fifg0iASobZKf/fyLKeIJCwEve1UnUqWqQPdIJHjMll7PsvXXPoRadIGozQ1iM1Pk4qKAnC6llOeFuij5w== dependencies: - "@metamask/detect-provider" "2.0.0" - "@near-wallet-selector/core" "8.5.4" - bn.js "5.2.1" - ethers "5.7.2" - is-mobile "4.0.0" - near-seed-phrase "0.2.0" + "@metamask/detect-provider" "^2.0.0" + "@near-wallet-selector/core" "7.9.1" + bn.js "^5.2.0" + ethers "^5.7.2" + is-mobile "^3.1.1" + near-seed-phrase "^0.2.0" "@near-wallet-selector/nightly@^8.5.4": version "8.5.4" @@ -8032,7 +8039,7 @@ ethereumjs-util@^7.1.0: ethereum-cryptography "^0.1.3" rlp "^2.2.4" -ethers@5.7.2, ethers@^5.7.2: +ethers@^5.7.2: version "5.7.2" resolved "https://registry.npmjs.org/ethers/-/ethers-5.7.2.tgz#3a7deeabbb8c030d4126b24f84e525466145872e" integrity sha512-wswUsmWo1aOK8rR7DIKiWSw9DbLWe6x98Jrn8wcTflTVvaXhAMaB5zGAXy0GYQEQp9iO1iSHWVyARQm11zUtyg== @@ -9696,6 +9703,11 @@ is-mobile@4.0.0: resolved "https://registry.npmjs.org/is-mobile/-/is-mobile-4.0.0.tgz#bba396eb9656e2739afde3053d7191da310fc758" integrity sha512-mlcHZA84t1qLSuWkt2v0I2l61PYdyQDt4aG1mLIXF5FDMm4+haBCxCPYSr/uwqQNRk1MiTizn0ypEuRAOLRAew== +is-mobile@^3.1.1: + version "3.1.1" + resolved "https://registry.npmjs.org/is-mobile/-/is-mobile-3.1.1.tgz#3b9e48f40068e4ea2da411f5009779844ce8d6aa" + integrity sha512-RRoXXR2HNFxNkUnxtaBdGBXtFlUMFa06S0NUKf/LCF+MuGLu13gi9iBCkoEmc6+rpXuwi5Mso5V8Zf7mNynMBQ== + is-module@^1.0.0: version "1.0.0" resolved "https://registry.yarnpkg.com/is-module/-/is-module-1.0.0.tgz#3258fb69f78c14d5b815d664336b4cffb6441591" @@ -11759,7 +11771,7 @@ near-hd-key@^1.2.1: create-hmac "1.1.7" tweetnacl "1.0.3" -near-seed-phrase@0.2.0, near-seed-phrase@^0.2.0: +near-seed-phrase@^0.2.0: version "0.2.0" resolved "https://registry.npmjs.org/near-seed-phrase/-/near-seed-phrase-0.2.0.tgz#fb7cf89682112b1160ab68abb50dc821f49be18a" integrity sha512-NpmrnejpY1AdlRpDZ0schJQJtfBaoUheRfiYtQpcq9TkwPgqKZCRULV5L3hHmLc0ep7KRtikbPQ9R2ztN/3cyQ== @@ -14779,7 +14791,7 @@ rxjs@6: dependencies: tslib "^1.9.0" -rxjs@7.8.1: +rxjs@7.8.1, rxjs@^7.8.0: version "7.8.1" resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.1.tgz#6f6f3d99ea8044291efd92e7c7fcf562c4057543" integrity sha512-AA3TVj+0A2iuIoQkWEK/tqFjBq2j+6PO6Y0zJcvzLAFhEFIO3HL0vls9hWLncZbAAbK0mar7oZ4V079I/qPMxg== From 7f149d306752ed31a2022677c42e85af3d676aa2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 18 Oct 2023 23:07:05 +0800 Subject: [PATCH 168/204] handle neth wallet issue --- src/components/pool/RemovePoolV3.tsx | 3 +++ src/services/swapV3.ts | 37 +++++++++------------------- 2 files changed, 14 insertions(+), 26 deletions(-) diff --git a/src/components/pool/RemovePoolV3.tsx b/src/components/pool/RemovePoolV3.tsx index 2bad62aa5..4d1d914db 100644 --- a/src/components/pool/RemovePoolV3.tsx +++ b/src/components/pool/RemovePoolV3.tsx @@ -56,6 +56,7 @@ import { isMobile } from '~utils/device'; import { WarningIcon } from '~components/icon/V3'; import QuestionMark from '~components/farm/QuestionMark'; import ReactTooltip from 'react-tooltip'; +import { useWalletSelector } from '../../context/WalletSelectorContext'; export type RemoveType = 'left' | 'right' | 'all'; @@ -101,6 +102,7 @@ export const RemovePoolV3 = (props: any) => { const [show_boundary_tip, set_show_boundary_tip] = useState(false); const [boundary_is_diff, set_boundary_is_diff] = useState(false); + const { selector } = useWalletSelector(); useEffect(() => { // init if (tokens && poolDetail && listLiquidities) { @@ -547,6 +549,7 @@ export const RemovePoolV3 = (props: any) => { batch_remove_liquidity, batch_update_liquidity, mint_liquidities, + selectedWalletId: selector.store.getState().selectedWalletId, }).then(() => { sessionStorage.setItem('REMOVE_POOL_ID', pool_id); }); diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 6a0f2a9e6..3607fee49 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -918,13 +918,8 @@ export const batch_add_liquidity = async ({ amount_y: string; selectedWalletId: string; }) => { - // todo test let split_num = 10; - if ( - selectedWalletId == 'ledger' || - selectedWalletId == 'neth' || - selectedWalletId == 'nightly' - ) { + if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { split_num = 2; } const transactions: Transaction[] = []; @@ -941,26 +936,11 @@ export const batch_add_liquidity = async ({ args: { add_liquidity_infos: arr_i, }, - gas: '300000000000000', + gas: split_num == 2 ? '200000000000000' : '300000000000000', }, ], }); } - // const transactions: Transaction[] = [ - // { - // receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - // functionCalls: [ - // { - // methodName: 'batch_add_liquidity', - // args: { - // add_liquidity_infos: liquidityInfos, - // }, - // gas: '300000000000000', - // }, - // ], - // }, - // ]; - if (+amount_x > 0) { transactions.unshift({ receiverId: token_x.id, @@ -1291,14 +1271,19 @@ export const batch_remove_liquidity_contract = async ({ batch_remove_liquidity, batch_update_liquidity, mint_liquidities, + selectedWalletId = window.selector?.store?.getState()?.selectedWalletId, }: { token_x: TokenMetadata; token_y: TokenMetadata; batch_remove_liquidity: IRemoveLiquidityInfo[]; batch_update_liquidity: IBatchUpdateiquidityInfo; mint_liquidities: UserLiquidityInfo[]; + selectedWalletId: string; }) => { - const max_number = 10; + let max_number = 10; + if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + max_number = 5; + } const transactions: Transaction[] = []; if (mint_liquidities.length) { const lpt_ids: any[] = []; @@ -1313,7 +1298,7 @@ export const batch_remove_liquidity_contract = async ({ args: { lpt_ids, }, - gas: '300000000000000', + gas: '250000000000000', }, ], }); @@ -1337,7 +1322,7 @@ export const batch_remove_liquidity_contract = async ({ args: { remove_liquidity_infos: batch_remove_liquidity_i, }, - gas: '300000000000000', + gas: max_number == 5 ? '250000000000000' : '300000000000000', }, ], }); @@ -1365,7 +1350,7 @@ export const batch_remove_liquidity_contract = async ({ { methodName: 'batch_update_liquidity', args: batch_update_liquidity_i, - gas: '300000000000000', + gas: max_number == 5 ? '250000000000000' : '300000000000000', }, ], }); From c1826b6098dd9c687ed3a84a09912b4adbaa8ebf Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 19 Oct 2023 00:03:32 +0800 Subject: [PATCH 169/204] fix ui issue --- src/components/portfolio/Orders.tsx | 4 ++-- src/global.css | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/components/portfolio/Orders.tsx b/src/components/portfolio/Orders.tsx index ec598ac1a..d9cfcb266 100644 --- a/src/components/portfolio/Orders.tsx +++ b/src/components/portfolio/Orders.tsx @@ -202,7 +202,8 @@ function OrderCard({ .div(p) .toFixed(tokensMap[order.buy_token].decimals); - return scientificNotationToString(sell_amount); + // return scientificNotationToString(sell_amount); + return display_amount(sell_amount); }; function display_amount(amount: string) { if (new Big(amount).eq(0)) { @@ -434,7 +435,6 @@ function OrderCard({ displayPercents[1] == '100' ? display_amount(orderIn) : buyAmountToSellAmount(order.unclaimed_amount || '0', order, price); - const sellTokenAmount = (
diff --git a/src/global.css b/src/global.css index a75052b2b..d6625096f 100644 --- a/src/global.css +++ b/src/global.css @@ -518,7 +518,7 @@ input[type='range']::-webkit-slider-runnable-track { } .swiper-container { width: 100%; - height: 98px; + /* height: 98px; */ } .swiper-wrapper { position: relative; From ef76510e1acf67a9443778ccd39772d4eadcd4e0 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 19 Oct 2023 14:35:13 +0800 Subject: [PATCH 170/204] update for here-wallet --- src/services/swapV3.ts | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 3607fee49..a887a965f 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1280,8 +1280,13 @@ export const batch_remove_liquidity_contract = async ({ mint_liquidities: UserLiquidityInfo[]; selectedWalletId: string; }) => { + console.log('9999999-selectedWalletId', selectedWalletId); let max_number = 10; - if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + if ( + selectedWalletId == 'ledger' || + selectedWalletId == 'neth' || + selectedWalletId == 'here-wallet' + ) { max_number = 5; } const transactions: Transaction[] = []; From 949f673a245f3f16ed3b12fb8345e7e869e8ee30 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Thu, 19 Oct 2023 14:50:26 +0800 Subject: [PATCH 171/204] fix here wallet issue --- src/services/swapV3.ts | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index a887a965f..8e4aa2ccd 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -919,7 +919,11 @@ export const batch_add_liquidity = async ({ selectedWalletId: string; }) => { let split_num = 10; - if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + if ( + selectedWalletId == 'ledger' || + selectedWalletId == 'neth' || + selectedWalletId == 'here-wallet' + ) { split_num = 2; } const transactions: Transaction[] = []; @@ -1280,7 +1284,6 @@ export const batch_remove_liquidity_contract = async ({ mint_liquidities: UserLiquidityInfo[]; selectedWalletId: string; }) => { - console.log('9999999-selectedWalletId', selectedWalletId); let max_number = 10; if ( selectedWalletId == 'ledger' || From 564c7c8c6b64f1419df120e28120d13c60aab82e Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 15:19:10 +0800 Subject: [PATCH 172/204] update --- src/services/swapV3.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 8e4aa2ccd..c73669ce3 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1290,7 +1290,7 @@ export const batch_remove_liquidity_contract = async ({ selectedWalletId == 'neth' || selectedWalletId == 'here-wallet' ) { - max_number = 5; + max_number = 2; } const transactions: Transaction[] = []; if (mint_liquidities.length) { From ca5c84100be2050880c7ed1e29c90de5fcb789bc Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 16:01:59 +0800 Subject: [PATCH 173/204] fix issue --- src/services/swapV3.ts | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index c73669ce3..09f0a150c 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1285,12 +1285,14 @@ export const batch_remove_liquidity_contract = async ({ selectedWalletId: string; }) => { let max_number = 10; + let need_split = false; if ( selectedWalletId == 'ledger' || selectedWalletId == 'neth' || selectedWalletId == 'here-wallet' ) { max_number = 2; + need_split = true; } const transactions: Transaction[] = []; if (mint_liquidities.length) { @@ -1330,7 +1332,7 @@ export const batch_remove_liquidity_contract = async ({ args: { remove_liquidity_infos: batch_remove_liquidity_i, }, - gas: max_number == 5 ? '250000000000000' : '300000000000000', + gas: need_split ? '250000000000000' : '300000000000000', }, ], }); @@ -1358,7 +1360,7 @@ export const batch_remove_liquidity_contract = async ({ { methodName: 'batch_update_liquidity', args: batch_update_liquidity_i, - gas: max_number == 5 ? '250000000000000' : '300000000000000', + gas: need_split ? '250000000000000' : '300000000000000', }, ], }); From 35e8409b4d827356c212253aa566e068fd5516d2 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 16:46:08 +0800 Subject: [PATCH 174/204] add test code --- src/components/icon/Risk.tsx | 2 +- src/components/swap/SwapLimitOrderChart.tsx | 4 +- src/pages/pools/DetailsPage.tsx | 53 +++++++++------------ src/pages/stable/StableSwapPage.tsx | 6 +-- src/pages/stable/StableSwapPageUSN.tsx | 5 +- src/services/swapV3.ts | 3 ++ 6 files changed, 30 insertions(+), 43 deletions(-) diff --git a/src/components/icon/Risk.tsx b/src/components/icon/Risk.tsx index 0d8c99b8b..e288dc794 100644 --- a/src/components/icon/Risk.tsx +++ b/src/components/icon/Risk.tsx @@ -1,5 +1,5 @@ import React from 'react'; -import { isMobile } from '~utils/device'; +import { isMobile } from '../../utils/device'; const RiskLogo = (props: any) => { const { width, height, className, ...rest } = props; return ( diff --git a/src/components/swap/SwapLimitOrderChart.tsx b/src/components/swap/SwapLimitOrderChart.tsx index 3b64fa1ed..337aedad4 100644 --- a/src/components/swap/SwapLimitOrderChart.tsx +++ b/src/components/swap/SwapLimitOrderChart.tsx @@ -15,12 +15,12 @@ import { toReadableNumber, toInternationalCurrencySystem, formatWithCommas, -} from '~utils/numbers'; +} from '../../utils/numbers'; import { toRealSymbol } from '../../utils/token'; import { SwapProContext } from '../../pages/SwapPage'; import Big from 'big.js'; import * as d3 from 'd3'; -import { isMobile } from '~utils/device'; +import { isMobile } from '../../utils/device'; const LimitOrderChartData = createContext(null); export default function SwapLimitOrderChart() { // CONST start diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index 352e87c89..da6a87718 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -1,8 +1,7 @@ import React, { useEffect, useState, useContext, useMemo } from 'react'; import { useLocation, useParams } from 'react-router-dom'; import Modal from 'react-modal'; -import { Card } from '~components/card/Card'; -import { ActionModel } from '~pages/AccountPage'; +import { Card } from '../../components/card/Card'; import { useMonthTVL, useMonthVolume, @@ -15,7 +14,7 @@ import { useDayVolume, useClassicPoolTransaction, useIndexerStatus, -} from '~state/pool'; +} from '../../state/pool'; import { addLiquidityToPool, addPoolToWatchList, @@ -23,19 +22,17 @@ import { Pool, PoolDetails, removePoolFromWatchList, -} from '~services/pool'; +} from '../../services/pool'; import { useTokenBalances, useTokens, getDepositableBalance, -} from '~state/token'; -import Loading from '~components/layout/Loading'; -import { FarmMiningIcon } from '~components/icon/FarmMining'; -import { FarmStamp, FarmStampNew } from '~components/icon/FarmStamp'; -import { ChartLoading } from '~components/icon/Loading'; -import { PoolSlippageSelector } from '~components/forms/SlippageSelector'; +} from '../../state/token'; +import Loading from '../../components/layout/Loading'; +import { FarmStamp, FarmStampNew } from '../../components/icon/FarmStamp'; +import { ChartLoading } from '../../components/icon/Loading'; +import { PoolSlippageSelector } from '../../components/forms/SlippageSelector'; import { Link } from 'react-router-dom'; -import { canFarm } from '~services/pool'; import { calculateFairShare, calculateFeePercent, @@ -47,35 +44,34 @@ import { toRoundedReadableNumber, percentOf, } from '../../utils/numbers'; -import { ftGetTokenMetadata, TokenMetadata } from '~services/ft-contract'; -import Alert from '~components/alert/Alert'; -import InputAmount from '~components/forms/InputAmount'; -import { isMobile } from '~utils/device'; +import { ftGetTokenMetadata, TokenMetadata } from '../../services/ft-contract'; +import Alert from '../../components/alert/Alert'; +import InputAmount from '../../components/forms/InputAmount'; +import { isMobile } from '../../utils/device'; import ReactModal from 'react-modal'; -import { toRealSymbol } from '~utils/token'; +import { toRealSymbol } from '../../utils/token'; import { BackArrowWhite, BackArrowGray, ModalClose, Near, -} from '~components/icon'; +} from '../../components/icon'; import { useHistory } from 'react-router'; -import { getPool } from '~services/indexer'; +import { getPool } from '../../services/indexer'; import { BigNumber } from 'bignumber.js'; import { FormattedMessage, useIntl, FormattedRelativeTime } from 'react-intl'; import { WatchListStartFull, WatchListStartFullMobile, -} from '~components/icon/WatchListStar'; +} from '../../components/icon/WatchListStar'; import { OutlineButton, SolidButton, FarmButton, ButtonTextWrapper, ConnectToNearBtn, -} from '~components/button/Button'; -import { wallet } from '~services/near'; -import { BreadCrumb } from '~components/layout/BreadCrumb'; +} from '../../components/button/Button'; +import { BreadCrumb } from '../../components/layout/BreadCrumb'; import { LP_TOKEN_DECIMALS, LP_STABLE_TOKEN_DECIMALS, @@ -98,7 +94,7 @@ import { import _ from 'lodash'; import moment from 'moment'; -import { ChartNoData } from '~components/icon/ChartNoData'; +import { ChartNoData } from '../../components/icon/ChartNoData'; import { getCurrentWallet, WalletContext, @@ -108,28 +104,23 @@ import { useWalletTokenBalances, useDepositableBalance, } from '../../state/token'; -import { SmallWallet } from '../../components/icon/SmallWallet'; import { scientificNotationToString, toInternationalCurrencySystemLongString, } from '../../utils/numbers'; import { isNotStablePool, canFarmV2, canFarmV1 } from '../../services/pool'; import { isStablePool, BLACKLIST_POOL_IDS } from '../../services/near'; -import { - getURLInfo, - checkAccountTip, -} from '../../components/layout/transactionTipPopUp'; export const REF_FI_PRE_LIQUIDITY_ID_KEY = 'REF_FI_PRE_LIQUIDITY_ID_VALUE'; import ReactTooltip from 'react-tooltip'; import { useWalletSelector } from '../../context/WalletSelectorContext'; -import { WRAP_NEAR_CONTRACT_ID } from '~services/wrap-near'; +import { WRAP_NEAR_CONTRACT_ID } from '../../services/wrap-near'; import { useAccountInfo, LOVE_TOKEN_DECIMAL } from '../../state/referendum'; import { getVEPoolId } from '../ReferendumPage'; import getConfig from '../../services/config'; import { BoostInputAmount } from '../../components/forms/InputAmount'; -import { ExternalLinkIcon } from '~components/icon/Risk'; +import { ExternalLinkIcon } from '../../components/icon/Risk'; import { FaAngleDown, FaAngleUp } from '../../components/reactIcons'; import { useClientMobile, isClientMobie } from '../../utils/device'; import { @@ -162,7 +153,7 @@ import { NoLiquidityDetailPageIcon } from '../../components/icon/Pool'; import { useFarmStake } from '../../state/farm'; import { VEARROW } from '../../components/icon/Referendum'; import Big from 'big.js'; -import { getEffectiveFarmList, sort_tokens_by_base } from '~services/commonV3'; +import { getEffectiveFarmList, sort_tokens_by_base } from '../../services/commonV3'; import { openUrl } from '../../services/commonV3'; import { numberWithCommas } from '../Orderly/utiles'; import { HiOutlineExternalLink, HiOutlineLink } from 'react-icons/hi'; diff --git a/src/pages/stable/StableSwapPage.tsx b/src/pages/stable/StableSwapPage.tsx index eeaf102df..201e825aa 100644 --- a/src/pages/stable/StableSwapPage.tsx +++ b/src/pages/stable/StableSwapPage.tsx @@ -7,7 +7,6 @@ import { } from '../../state/token'; import AddLiquidityComponent from '../../components/stableswap/AddLiquidity'; import { usePool, useStablePool } from '../../state/pool'; -import { isMobile } from '~utils/device'; import { RemoveLiquidityComponent } from '../../components/stableswap/RemoveLiquidity'; import TokenReserves from '../../components/stableswap/TokenReserves'; import { useWalletTokenBalances } from '../../state/token'; @@ -16,18 +15,15 @@ import { SharesCard, StableTokens, } from '../../components/stableswap/CommonComp'; -import { TokenMetadata } from '../../services/ft-contract'; -import { useFarmStake } from '../../state/farm'; import { BackToStablePoolList, Images, } from '../../components/stableswap/CommonComp'; -import BigNumber from 'bignumber.js'; import { getStablePoolFromCache, Pool, StablePool } from '../../services/pool'; import { getStableSwapTabKey } from './StableSwapPageUSN'; import { STABLE_TOKEN_IDS } from '../../services/near'; import { RecentTransactions } from '../pools/DetailsPage'; -import { useTokens } from '~state/token'; +import { useTokens } from '../../state/token'; export const DEFAULT_ACTIONS = ['add_liquidity', 'remove_liquidity']; const STABLE_TOKENS = ['USDT.e', 'USDC', 'DAI']; diff --git a/src/pages/stable/StableSwapPageUSN.tsx b/src/pages/stable/StableSwapPageUSN.tsx index 06a13de1e..6a03b0fc8 100644 --- a/src/pages/stable/StableSwapPageUSN.tsx +++ b/src/pages/stable/StableSwapPageUSN.tsx @@ -7,25 +7,22 @@ import { } from '../../state/token'; import { usePool, useStablePool } from '../../state/pool'; import TokenReserves from '../../components/stableswap/TokenReserves'; -import getConfig from '~services/config'; import { useWalletTokenBalances } from '../../state/token'; import { useLocation, useParams } from 'react-router-dom'; import { SharesCard, StableTokens, } from '../../components/stableswap/CommonComp'; -import { useFarmStake } from '../../state/farm'; import { BackToStablePoolList, Images, } from '../../components/stableswap/CommonComp'; -import BigNumber from 'bignumber.js'; import { Pool, StablePool, getStablePoolFromCache } from '../../services/pool'; import AddLiquidityComponentUSN from '../../components/stableswap/AddLiquidityUSN'; import { RemoveLiquidityComponentUSN } from '../../components/stableswap/RemoveLiquidityUSN'; import { NEARX_POOL_ID, STABLE_TOKEN_USN_IDS } from '../../services/near'; import { RecentTransactions } from '../pools/DetailsPage'; -import { useTokens } from '~state/token'; +import { useTokens } from '../../state/token'; export const DEFAULT_ACTIONS = ['add_liquidity', 'remove_liquidity']; export const getStableSwapTabKey = (id: string | number) => diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 09f0a150c..dbbe7b2ff 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -926,6 +926,7 @@ export const batch_add_liquidity = async ({ ) { split_num = 2; } + debugger; const transactions: Transaction[] = []; const n = Math.ceil(liquidityInfos.length / split_num); for (let i = 0; i < n; i++) { @@ -1041,6 +1042,8 @@ export const batch_add_liquidity = async ({ ], }); } + console.log('888888888888888888-transactions', transactions) + debugger; return executeMultipleTransactions(transactions); }; From 974cadcf154829d33a13e38a72c71b8334124c4a Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 16:54:28 +0800 Subject: [PATCH 175/204] remove test code --- src/services/swapV3.ts | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index dbbe7b2ff..b34fbae81 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1042,8 +1042,6 @@ export const batch_add_liquidity = async ({ ], }); } - console.log('888888888888888888-transactions', transactions) - debugger; return executeMultipleTransactions(transactions); }; From 1141dd512d3887c28d60e55be4f29afc7a06ed80 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 17:51:58 +0800 Subject: [PATCH 176/204] update --- src/services/swapV3.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index b34fbae81..ce550b24a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1418,6 +1418,7 @@ export const batch_remove_liquidity_contract = async ({ ], }); } + console.log('888888888888888-transactions', transactions); return executeMultipleTransactions(transactions); }; From 8cc7d44969eb10ba96c5ea09871c200f38bc9ee4 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 18:08:48 +0800 Subject: [PATCH 177/204] fix batch update --- src/pages/pools/DetailsPage.tsx | 5 ++++- src/services/swapV3.ts | 10 ++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/src/pages/pools/DetailsPage.tsx b/src/pages/pools/DetailsPage.tsx index da6a87718..f83ca9636 100644 --- a/src/pages/pools/DetailsPage.tsx +++ b/src/pages/pools/DetailsPage.tsx @@ -153,7 +153,10 @@ import { NoLiquidityDetailPageIcon } from '../../components/icon/Pool'; import { useFarmStake } from '../../state/farm'; import { VEARROW } from '../../components/icon/Referendum'; import Big from 'big.js'; -import { getEffectiveFarmList, sort_tokens_by_base } from '../../services/commonV3'; +import { + getEffectiveFarmList, + sort_tokens_by_base, +} from '../../services/commonV3'; import { openUrl } from '../../services/commonV3'; import { numberWithCommas } from '../Orderly/utiles'; import { HiOutlineExternalLink, HiOutlineLink } from 'react-icons/hi'; diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index ce550b24a..444449a30 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1286,6 +1286,7 @@ export const batch_remove_liquidity_contract = async ({ selectedWalletId: string; }) => { let max_number = 10; + let max_batch_update_number = 10; let need_split = false; if ( selectedWalletId == 'ledger' || @@ -1293,6 +1294,7 @@ export const batch_remove_liquidity_contract = async ({ selectedWalletId == 'here-wallet' ) { max_number = 2; + max_batch_update_number = 1; need_split = true; } const transactions: Transaction[] = []; @@ -1343,11 +1345,11 @@ export const batch_remove_liquidity_contract = async ({ const { add_liquidity_infos, remove_liquidity_infos } = batch_update_liquidity; const length = add_liquidity_infos.length; - const ts_length = Math.ceil(length / max_number); + const ts_length = Math.ceil(length / max_batch_update_number); for (let i = 0; i < ts_length; i++) { let batch_update_liquidity_i; - const startIndex = i * max_number; - const endIndex = startIndex + max_number; + const startIndex = i * max_batch_update_number; + const endIndex = startIndex + max_batch_update_number; batch_update_liquidity_i = { add_liquidity_infos: add_liquidity_infos.slice(startIndex, endIndex), remove_liquidity_infos: remove_liquidity_infos.slice( @@ -1418,7 +1420,7 @@ export const batch_remove_liquidity_contract = async ({ ], }); } - console.log('888888888888888-transactions', transactions); + console.log('8888888888888888-transactions', transactions); return executeMultipleTransactions(transactions); }; From f98a5a4d09de0bf9ae53d465cc40c9398b96cd9f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 21:11:10 +0800 Subject: [PATCH 178/204] fix ledger issue for batch opration in farm page --- src/services/farm.ts | 94 ++++++++++++++++++++++++++++++++++---------- 1 file changed, 73 insertions(+), 21 deletions(-) diff --git a/src/services/farm.ts b/src/services/farm.ts index f3bf575db..0b429721c 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -1342,6 +1342,12 @@ export const batch_unStake_boost_nft = async ({ withdraw_amount, liquidities, }: IStakeInfo) => { + let need_split = false; + const max_length = 2; + const selectedWalletId = window.selector?.store?.getState()?.selectedWalletId; + if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + need_split = true; + } const transactions: Transaction[] = []; if (new BigNumber(withdraw_amount).isGreaterThan('0')) { transactions.push({ @@ -1365,18 +1371,39 @@ export const batch_unStake_boost_nft = async ({ lpt_ids.push(l.lpt_id); }); if (lpt_ids.length) { - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - { - methodName: 'batch_burn_v_liquidity', - args: { - lpt_ids, + if (need_split) { + const num = Math.ceil(lpt_ids.length / max_length); + for (let i = 0; i < num; i++) { + const startIndex = i * max_length; + const endIndex = startIndex + max_length; + const lpt_ids_i = lpt_ids.slice(startIndex, endIndex); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_burn_v_liquidity', + args: { + lpt_ids: lpt_ids_i, + }, + gas: '250000000000000', + }, + ], + }); + } + } else { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_burn_v_liquidity', + args: { + lpt_ids, + }, + gas: '250000000000000', }, - gas: '250000000000000', - }, - ], - }); + ], + }); + } } const neededStorage = await checkTokenNeedsStorageDeposit_boost(); if (neededStorage) { @@ -1394,6 +1421,12 @@ export const batch_stake_boost_nft = async ({ withdraw_amount, seed_id, }: IStakeInfo) => { + let need_split = false; + const selectedWalletId = window.selector?.store?.getState()?.selectedWalletId; + if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + need_split = true; + } + const max_length = 2; const [contractId, temp_pool_id] = seed_id.split('@'); const [fixRange, dcl_pool_id, left_point, right_point] = temp_pool_id.split('&'); @@ -1447,16 +1480,35 @@ export const batch_stake_boost_nft = async ({ } }); if (mint_infos.length) { - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: [ - { - methodName: 'batch_mint_v_liquidity', - args: { mint_infos }, - gas: '200000000000000', - }, - ], - }); + if (need_split) { + const num = Math.ceil(mint_infos.length / max_length); + for (let i = 0; i < num; i++) { + const startIndex = i * max_length; + const endIndex = startIndex + max_length; + const mint_infos_i = mint_infos.slice(startIndex, endIndex); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_mint_v_liquidity', + args: { mint_infos: mint_infos_i }, + gas: '200000000000000', + }, + ], + }); + } + } else { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'batch_mint_v_liquidity', + args: { mint_infos }, + gas: '200000000000000', + }, + ], + }); + } } transactions.push({ receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, From bb49b0126bab033aff985c548c099d259d26d84b Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 22:47:24 +0800 Subject: [PATCH 179/204] add test code --- src/services/swapV3.ts | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 444449a30..7c8ccdbe1 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -926,7 +926,6 @@ export const batch_add_liquidity = async ({ ) { split_num = 2; } - debugger; const transactions: Transaction[] = []; const n = Math.ceil(liquidityInfos.length / split_num); for (let i = 0; i < n; i++) { @@ -1288,15 +1287,16 @@ export const batch_remove_liquidity_contract = async ({ let max_number = 10; let max_batch_update_number = 10; let need_split = false; - if ( - selectedWalletId == 'ledger' || - selectedWalletId == 'neth' || - selectedWalletId == 'here-wallet' - ) { + if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { max_number = 2; max_batch_update_number = 1; need_split = true; } + if (selectedWalletId == 'here-wallet') { + max_number = 10; + max_batch_update_number = 5; + need_split = true; + } const transactions: Transaction[] = []; if (mint_liquidities.length) { const lpt_ids: any[] = []; From 508db79e59fcbb5128cdc2d5ce04a88dd0fa3c08 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 20 Oct 2023 23:24:47 +0800 Subject: [PATCH 180/204] add test code --- src/services/swapV3.ts | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 7c8ccdbe1..4acc2806d 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -1293,10 +1293,11 @@ export const batch_remove_liquidity_contract = async ({ need_split = true; } if (selectedWalletId == 'here-wallet') { - max_number = 10; - max_batch_update_number = 5; + max_number = 4; + max_batch_update_number = 2; need_split = true; } + console.log('666666666666-max_number, max_batch_update_number, need_split', max_number, max_batch_update_number, need_split); const transactions: Transaction[] = []; if (mint_liquidities.length) { const lpt_ids: any[] = []; From 9a1676e9426b689b1093dcba7ba9369605a8d1ce Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 21 Oct 2023 11:11:57 +0800 Subject: [PATCH 181/204] add test code for batch_add --- src/services/farm.ts | 4 ++-- src/services/swapV3.ts | 27 ++++++++++++++++++--------- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/src/services/farm.ts b/src/services/farm.ts index 0b429721c..f81ebe421 100644 --- a/src/services/farm.ts +++ b/src/services/farm.ts @@ -1345,7 +1345,7 @@ export const batch_unStake_boost_nft = async ({ let need_split = false; const max_length = 2; const selectedWalletId = window.selector?.store?.getState()?.selectedWalletId; - if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + if (selectedWalletId == 'ledger') { need_split = true; } const transactions: Transaction[] = []; @@ -1423,7 +1423,7 @@ export const batch_stake_boost_nft = async ({ }: IStakeInfo) => { let need_split = false; const selectedWalletId = window.selector?.store?.getState()?.selectedWalletId; - if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + if (selectedWalletId == 'ledger') { need_split = true; } const max_length = 2; diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 4acc2806d..171ea03ea 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -44,6 +44,7 @@ import { IRemoveLiquidityInfo, } from '../pages/poolsV3/interfaces'; import getConfig from './config'; +import de from 'date-fns/esm/locale/de/index.js'; const LOG_BASE = 1.0001; export const V3_POOL_FEE_LIST = [100, 400, 2000, 10000]; @@ -919,12 +920,15 @@ export const batch_add_liquidity = async ({ selectedWalletId: string; }) => { let split_num = 10; - if ( - selectedWalletId == 'ledger' || - selectedWalletId == 'neth' || - selectedWalletId == 'here-wallet' - ) { + let need_split = false; + if (selectedWalletId == 'ledger') { split_num = 2; + need_split = true; + } else if (selectedWalletId == 'neth') { + split_num = 5; + need_split = true; + } else if (selectedWalletId == 'here-wallet') { + } const transactions: Transaction[] = []; const n = Math.ceil(liquidityInfos.length / split_num); @@ -940,7 +944,7 @@ export const batch_add_liquidity = async ({ args: { add_liquidity_infos: arr_i, }, - gas: split_num == 2 ? '200000000000000' : '300000000000000', + gas: need_split ? '200000000000000' : '300000000000000', }, ], }); @@ -1041,6 +1045,7 @@ export const batch_add_liquidity = async ({ ], }); } + console.log('55555555555555555-transactions', transactions) return executeMultipleTransactions(transactions); }; @@ -1287,16 +1292,20 @@ export const batch_remove_liquidity_contract = async ({ let max_number = 10; let max_batch_update_number = 10; let need_split = false; - if (selectedWalletId == 'ledger' || selectedWalletId == 'neth') { + if (selectedWalletId == 'ledger') { max_number = 2; max_batch_update_number = 1; need_split = true; - } - if (selectedWalletId == 'here-wallet') { + } else if (selectedWalletId == 'neth') { + max_number = 5; + max_batch_update_number = 2; + need_split = true; + } else if (selectedWalletId == 'here-wallet') { max_number = 4; max_batch_update_number = 2; need_split = true; } + debugger; console.log('666666666666-max_number, max_batch_update_number, need_split', max_number, max_batch_update_number, need_split); const transactions: Transaction[] = []; if (mint_liquidities.length) { From 86cc3fb646f3983ab7b07922f716de3ea0f03533 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 21 Oct 2023 11:54:10 +0800 Subject: [PATCH 182/204] update --- src/services/swapV3.ts | 1 + 1 file changed, 1 insertion(+) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index 171ea03ea..e8f32e191 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -930,6 +930,7 @@ export const batch_add_liquidity = async ({ } else if (selectedWalletId == 'here-wallet') { } + debugger; const transactions: Transaction[] = []; const n = Math.ceil(liquidityInfos.length / split_num); for (let i = 0; i < n; i++) { From d48d6946495710ac6a976faa8e63d5f0d5106553 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sat, 21 Oct 2023 15:12:23 +0800 Subject: [PATCH 183/204] fix here wallet batch transtion error --- src/services/swapV3.ts | 69 ++++++++++++++++++++++++++---------------- 1 file changed, 43 insertions(+), 26 deletions(-) diff --git a/src/services/swapV3.ts b/src/services/swapV3.ts index e8f32e191..89476279a 100644 --- a/src/services/swapV3.ts +++ b/src/services/swapV3.ts @@ -924,13 +924,10 @@ export const batch_add_liquidity = async ({ if (selectedWalletId == 'ledger') { split_num = 2; need_split = true; - } else if (selectedWalletId == 'neth') { + } else if (selectedWalletId == 'neth' || selectedWalletId == 'here-wallet') { split_num = 5; need_split = true; - } else if (selectedWalletId == 'here-wallet') { - } - debugger; const transactions: Transaction[] = []; const n = Math.ceil(liquidityInfos.length / split_num); for (let i = 0; i < n; i++) { @@ -945,7 +942,7 @@ export const batch_add_liquidity = async ({ args: { add_liquidity_infos: arr_i, }, - gas: need_split ? '200000000000000' : '300000000000000', + gas: need_split ? '250000000000000' : '300000000000000', }, ], }); @@ -1046,7 +1043,6 @@ export const batch_add_liquidity = async ({ ], }); } - console.log('55555555555555555-transactions', transactions) return executeMultipleTransactions(transactions); }; @@ -1302,12 +1298,10 @@ export const batch_remove_liquidity_contract = async ({ max_batch_update_number = 2; need_split = true; } else if (selectedWalletId == 'here-wallet') { - max_number = 4; - max_batch_update_number = 2; + max_number = 10; + max_batch_update_number = 5; need_split = true; } - debugger; - console.log('666666666666-max_number, max_batch_update_number, need_split', max_number, max_batch_update_number, need_split); const transactions: Transaction[] = []; if (mint_liquidities.length) { const lpt_ids: any[] = []; @@ -1379,21 +1373,45 @@ export const batch_remove_liquidity_contract = async ({ ], }); } - const widthdrawActions: any[] = []; - widthdrawActions.push({ - methodName: 'withdraw_asset', - args: { token_id: token_x.id }, - gas: '55000000000000', - }); - widthdrawActions.push({ - methodName: 'withdraw_asset', - args: { token_id: token_y.id }, - gas: '55000000000000', - }); - transactions.push({ - receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, - functionCalls: widthdrawActions, - }); + + if (need_split) { + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'withdraw_asset', + args: { token_id: token_x.id }, + gas: '250000000000000', + }, + ], + }); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: [ + { + methodName: 'withdraw_asset', + args: { token_id: token_y.id }, + gas: '250000000000000', + }, + ], + }); + } else { + const widthdrawActions: any[] = []; + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_x.id }, + gas: '100000000000000', + }); + widthdrawActions.push({ + methodName: 'withdraw_asset', + args: { token_id: token_y.id }, + gas: '100000000000000', + }); + transactions.push({ + receiverId: REF_UNI_V3_SWAP_CONTRACT_ID, + functionCalls: widthdrawActions, + }); + } } const ftBalance_x = await ftGetStorageBalance(token_x.id); if (!ftBalance_x) { @@ -1431,7 +1449,6 @@ export const batch_remove_liquidity_contract = async ({ ], }); } - console.log('8888888888888888-transactions', transactions); return executeMultipleTransactions(transactions); }; From 35e5245830f7c953b3f0d0dc674983c073431e7f Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 22 Oct 2023 22:00:02 +0800 Subject: [PATCH 184/204] upgrade rxjs versions --- package.json | 2 +- src/context/WalletSelectorContext.tsx | 2 +- yarn.lock | 25 +++++++++---------------- 3 files changed, 11 insertions(+), 18 deletions(-) diff --git a/package.json b/package.json index 8f35fd19d..f15dcca83 100644 --- a/package.json +++ b/package.json @@ -149,7 +149,7 @@ "recharts": "^2.1.6", "reconnecting-websocket": "^4.4.0", "respinner": "^3.0.8", - "rxjs": "^7.5.5", + "rxjs": "7.8.1", "socket.io-client": "^4.5.4", "swiper": "^6.8.4" }, diff --git a/src/context/WalletSelectorContext.tsx b/src/context/WalletSelectorContext.tsx index a3f54f689..8869e6129 100644 --- a/src/context/WalletSelectorContext.tsx +++ b/src/context/WalletSelectorContext.tsx @@ -127,7 +127,7 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { iconUrl: walletIcons['neth'], gas: '300000000000000', bundle: false, - }), + }) as any, // @ts-ignore setupNightly({ iconUrl: walletIcons['nightly'], diff --git a/yarn.lock b/yarn.lock index 0aa84938e..67105b3f7 100644 --- a/yarn.lock +++ b/yarn.lock @@ -2414,10 +2414,10 @@ call-me-maybe "^1.0.1" glob-to-regexp "^0.3.0" -"@near-wallet-selector/core@7.9.1": - version "7.9.1" - resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-7.9.1.tgz#d13d20724bd105e85b141bb2c7f35f4c07a37cad" - integrity sha512-BJGsTK0snZAU1Je+W9TFJG5KW+jk+Ots9LuxsALZ/wZpqSeGT+w4xw36TZt3QsEm+I84im1+L+PIIW/bM2A9rQ== +"@near-wallet-selector/core@7.9.3": + version "7.9.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/core/-/core-7.9.3.tgz#5a51b72064c9e018c0f0eb97db06b70cd58b8358" + integrity sha512-SNIgLnI/LeU1mwBHc5wcyOrVAqhWmFXJfVIfB1P16ziH3EKMsbs/gxcKUSPuvDagm9dZm11k+FA7bxSspavibA== dependencies: rxjs "^7.8.0" @@ -2498,13 +2498,13 @@ "@near-wallet-selector/core" "8.5.4" "@near-wallet-selector/my-near-wallet" "8.5.4" -"@near-wallet-selector/neth@7.9.1": - version "7.9.1" - resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-7.9.1.tgz#e67718a004e990ddbb85ee3c1ac3bf113123792f" - integrity sha512-1QW5Fifg0iASobZKf/fyLKeIJCwEve1UnUqWqQPdIJHjMll7PsvXXPoRadIGozQ1iM1Pk4qKAnC6llOeFuij5w== +"@near-wallet-selector/neth@^7.9.1": + version "7.9.3" + resolved "https://registry.npmjs.org/@near-wallet-selector/neth/-/neth-7.9.3.tgz#d9991b136225e577c569622e1ffb64b34fdd6006" + integrity sha512-v5tJdb8HOCOenW+nd5dyH/aF3ISFB/Wf+PK9Qy9UL4abvLsfBam75vqiI8cMe/15pfuqF7CHC3h9Wt2/BDt4Yg== dependencies: "@metamask/detect-provider" "^2.0.0" - "@near-wallet-selector/core" "7.9.1" + "@near-wallet-selector/core" "7.9.3" bn.js "^5.2.0" ethers "^5.7.2" is-mobile "^3.1.1" @@ -14798,13 +14798,6 @@ rxjs@7.8.1, rxjs@^7.8.0: dependencies: tslib "^2.1.0" -rxjs@^7.5.5: - version "7.8.0" - resolved "https://registry.yarnpkg.com/rxjs/-/rxjs-7.8.0.tgz#90a938862a82888ff4c7359811a595e14e1e09a4" - integrity sha512-F2+gxDshqmIub1KdvZkaEfGDwLNpPvk9Fs6LD/MyQxNgMds/WH9OdDDXOmxUZpME+iSK3rQCctkL0DYyytUqMg== - dependencies: - tslib "^2.1.0" - safe-buffer@5.1.2, safe-buffer@~5.1.0, safe-buffer@~5.1.1: version "5.1.2" resolved "https://registry.yarnpkg.com/safe-buffer/-/safe-buffer-5.1.2.tgz#991ec69d296e0313747d59bdfd2b745c35f8828d" From e6a88e3652a27d7fe9889b69d3b085f46b5a906d Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Wed, 25 Oct 2023 23:20:13 +0800 Subject: [PATCH 185/204] add farm apr for dcl pool list --- src/pages/pools/LiquidityPage.tsx | 121 +++++++++++++++++++++++++----- 1 file changed, 101 insertions(+), 20 deletions(-) diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 4f9561dd0..b840f3995 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -50,7 +50,6 @@ import { toInternationalCurrencySystem, } from '../../utils/numbers'; import { CheckedTick, CheckedEmpty } from '../../components/icon/CheckBox'; -import { toRealSymbol } from '../../utils/token'; import { FormattedMessage, useIntl } from 'react-intl'; import { DownArrowLight, @@ -58,12 +57,7 @@ import { UpArrowDeep, UpArrowLight, } from '../../components/icon'; -import { FarmStamp } from '../../components/icon/FarmStamp'; -import { - SolidButton, - FarmButton, - GradientButton, -} from '../../components/button/Button'; +import { SolidButton } from '../../components/button/Button'; import { NEAR_CLASS_STABLE_POOL_IDS, wallet, @@ -71,17 +65,11 @@ import { USDTT_USDCC_USDT_USDC_POOL_ID, } from '../../services/near'; import { WatchListStartFull } from '../../components/icon/WatchListStar'; -import { PolygonGrayDown } from '../../components/icon/Polygon'; import _, { orderBy, sortBy, filter } from 'lodash'; -import QuestionMark from '../../components/farm/QuestionMark'; import { useInView } from 'react-intersection-observer'; import { QuestionTip } from '../../components/layout/TipWrapper'; import { FilterIcon } from '../../components/icon/PoolFilter'; -import { - TokenMetadata, - REF_META_DATA, - ftGetTokenMetadata, -} from '../../services/ft-contract'; +import { TokenMetadata, REF_META_DATA } from '../../services/ft-contract'; import { scientificNotationToString, percent, @@ -137,6 +125,7 @@ import { FarmStampNew } from '../../components/icon/FarmStamp'; import { ALL_STABLE_POOL_IDS } from '../../services/near'; import { BoostSeeds, WatchList } from '../../store/RefDatabase'; import { REF_FI_CONTRACT_ID } from '../../services/near'; +import { FarmBoost } from '../../services/farm'; import { get_all_seeds, @@ -581,6 +570,27 @@ function MobilePoolRowV2({ return '$' + toInternationalCurrencySystem(v.toString(), 2); } } + function getFarmApr() { + if (relatedSeed) { + const farms = relatedSeed.farmList; + let apr = 0; + const allPendingFarms = isPending(relatedSeed); + farms.forEach(function (item: FarmBoost) { + const pendingFarm = + item.status == 'Created' || item.status == 'Pending'; + if (allPendingFarms || (!allPendingFarms && !pendingFarm)) { + apr = +new BigNumber(apr).plus(item.apr).toFixed(); + } + }); + apr = apr * 100; + if (+apr == 0) { + return '-'; + } else { + return '+' + toPrecision(apr.toString(), 2) + '%'; + } + } + return ''; + } return (
-
+
{showSortedValue({ sortBy, value: sortBy == 'apr' && mark ? pool['top_bin_apr'] : pool[sortBy], })} + + {relatedSeed && + (sortBy == 'top_bin_apr' || (sortBy == 'apr' && mark)) && ( + + {getFarmApr()} + + )}
@@ -1589,6 +1606,25 @@ export const getPoolListFarmAprTip = () => { +
+`; +}; +export const getPoolListV2FarmAprTip = () => { + return ` +
+
+ Top Bin APR +
+ +
+ + + Farm Rewards APR +
+ + +
`; }; @@ -1684,7 +1720,6 @@ function PoolRow({
{calculateFeePercent(pool.fee)}%
-
- {displayOfTopBinApr} +
+ {displayOfTopBinApr} + {relatedSeed && ( + {getFarmApr()} + )} + {relatedSeed && ( + + )} +
- {/* todo */} {!clientMobileDevice && ( Date: Thu, 26 Oct 2023 10:27:09 +0800 Subject: [PATCH 186/204] update text for watchList in mobile site --- src/pages/pools/LiquidityPage.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index b840f3995..3a77151cf 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -774,7 +774,7 @@ function MobileWatchListCard({ {sortBy === 'apr' && (
- *Pool Fee APY + Farm Rewards APR + *Pool Fee APY/Top Bin APR + Farm Rewards APR
)}
@@ -1448,7 +1448,7 @@ function MobileLiquidityPage({ {sortBy === 'apr' && (
- *Pool Fee APY + Farm Rewards APR + *Pool Fee APY/Top Bin APR + Farm Rewards APR
)}
From c921f418055dcd8ed8f23b96ed958bca132aa662 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 27 Oct 2023 11:35:21 +0800 Subject: [PATCH 187/204] modal ui use package --- src/context/WalletSelectorContext.tsx | 38 +++------------------------ 1 file changed, 4 insertions(+), 34 deletions(-) diff --git a/src/context/WalletSelectorContext.tsx b/src/context/WalletSelectorContext.tsx index 8869e6129..3cf3a4aff 100644 --- a/src/context/WalletSelectorContext.tsx +++ b/src/context/WalletSelectorContext.tsx @@ -1,18 +1,16 @@ import React, { useCallback, useContext, useEffect, useState } from 'react'; -import { map, distinctUntilChanged, windowWhen } from 'rxjs'; +import { map, distinctUntilChanged } from 'rxjs'; import { NetworkId, setupWalletSelector, - waitFor, } from '@near-wallet-selector/core'; import type { WalletSelector, AccountState } from '@near-wallet-selector/core'; -import { setupModal } from './modal-ui'; -import type { WalletSelectorModal } from './modal-ui'; +import { setupModal } from '@near-wallet-selector/modal-ui'; +import type { WalletSelectorModal } from '@near-wallet-selector/modal-ui'; import { setupNearWallet } from '@near-wallet-selector/near-wallet'; import { setupMyNearWallet } from '@near-wallet-selector/my-near-wallet'; import { setupSender } from '@near-wallet-selector/sender'; -import { setupMathWallet } from '@near-wallet-selector/math-wallet'; import { setupLedger } from '@near-wallet-selector/ledger'; import { setupHereWallet } from '@near-wallet-selector/here-wallet'; @@ -24,11 +22,8 @@ import { setupNightly } from '@near-wallet-selector/nightly'; import getConfig from '../services/config'; -import './modal-ui/components/styles.css'; +import '@near-wallet-selector/modal-ui/styles.css'; import { - REF_FARM_CONTRACT_ID, - wallet, - REF_FARM_BOOST_CONTRACT_ID, near, } from '../services/near'; import { walletIcons } from './walletIcons'; @@ -37,15 +32,12 @@ import { REF_ORDERLY_ACCOUNT_VALID } from '../pages/Orderly/components/UserBoard import { REF_FI_SENDER_WALLET_ACCESS_KEY, REF_ORDERLY_NORMALIZED_KEY, - generateTradingKeyPair, } from '../pages/Orderly/orderly/utils'; import { get_orderly_private_key_path, get_orderly_public_key_path, } from '../pages/Orderly/orderly/utils'; import { isMobile } from '../utils/device'; -import { AccountView } from 'near-api-js/lib/providers/provider'; -import { Account, providers } from 'near-api-js'; const CONTRACT_ID = getOrderlyConfig().ORDERLY_ASSET_MANAGER; @@ -137,28 +129,6 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { }), // @ts-ignore setupHereWallet(), - // setupNightlyConnect({ - // url: 'wss://ncproxy.nightly.app/app', - // appMetadata: { - // additionalInfo: '', - // application: 'ref fiannce', - // description: 'Example dApp used by NEAR Wallet Selector', - // icon: 'https://near.org/wp-content/uploads/2020/09/cropped-favicon-192x192.png', - // }, - // iconUrl: walletIcons['nightly-connect'], - // }), - // setupWalletConnect({ - // projectId: '423baa464ffaeca9d7165ab4222d534f', - // relayUrl: 'wss://relay.walletconnect.com', - // metadata: { - // name: 'ref_finance', - // description: 'Example dApp used by NEAR Wallet Selector', - // url: 'https://github.com/near/wallet-selector', - // icons: walletIcons['wallet-connect'], - // }, - // chainId: `near:${getConfig().networkId}}`, - // iconUrl: walletIcons['wallet-connect'], - // }), ], }); const _modal = setupModal(_selector, { From 5fdae83bff858e299203f3a78550d7f2ec811842 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 27 Oct 2023 16:28:24 +0800 Subject: [PATCH 188/204] change wallet custom ui to standard Ui --- src/Content.tsx | 42 ++++++------- src/components/button/Button.tsx | 19 +++--- src/context/WalletSelectorContext.tsx | 53 +++------------- src/global.css | 87 +++++++++++++++++++++++++++ 4 files changed, 125 insertions(+), 76 deletions(-) diff --git a/src/Content.tsx b/src/Content.tsx index 10f9aeaed..8d622d0cf 100644 --- a/src/Content.tsx +++ b/src/Content.tsx @@ -190,27 +190,27 @@ export function Content() { }); }, [accountId, getAccount]); - useEffect(() => { - if ( - !window?.near?.isSender || - selector?.store?.getState()?.selectedWalletId !== 'sender' - ) - return; - - window.near.on('accountChanged', async (changedAccountId: string) => { - const senderModule = selector.store - .getState() - .modules.find((m) => m.id === 'sender'); - - const senderWallet = (await senderModule.wallet()) as InjectedWallet; - - await senderWallet.signIn({ - contractId: ORDERLY_ASSET_MANAGER, - }); - - window.location.reload(); - }); - }, [window.near]); + // useEffect(() => { + // if ( + // !window?.near?.isSender || + // selector?.store?.getState()?.selectedWalletId !== 'sender' + // ) + // return; + + // window.near.on('accountChanged', async (changedAccountId: string) => { + // const senderModule = selector.store + // .getState() + // .modules.find((m) => m.id === 'sender'); + + // const senderWallet = (await senderModule.wallet()) as InjectedWallet; + + // await senderWallet.signIn({ + // contractId: ORDERLY_ASSET_MANAGER, + // }); + + // window.location.reload(); + // }); + // }, [window.near]); useGlobalPopUp(globalState); diff --git a/src/components/button/Button.tsx b/src/components/button/Button.tsx index e7eef9454..dc5a33d7b 100644 --- a/src/components/button/Button.tsx +++ b/src/components/button/Button.tsx @@ -154,8 +154,7 @@ export function ConnectToNearBtn() { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); - // setShowWalletSelector(true); + // setButtonLoading(true); modal.show(); }} > @@ -211,7 +210,7 @@ export function ConnectToNearBtnGradient({ onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); + // setButtonLoading(true); modal.show(); }} > @@ -259,7 +258,7 @@ export function ConnectToNearBtnGradientMoible({ onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); + // setButtonLoading(true); modal.show(); }} > @@ -562,8 +561,7 @@ export function ConnectToNearButton(props: any) { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); - // setShowWalletSelector(true); + // setButtonLoading(true); modal.show(); }} > @@ -880,7 +878,7 @@ export function GreenConnectToNearBtn(props: any) { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); + // setButtonLoading(true); modal.show(); }} > @@ -928,8 +926,7 @@ export function BlacklightConnectToNearBtn(props: any) { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); - // setShowWalletSelector(true); + // setButtonLoading(true); modal.show(); }} > @@ -988,7 +985,7 @@ export function ConnectToNearBtnVotingMobile() { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); + // setButtonLoading(true); modal.show(); }} > @@ -1066,7 +1063,7 @@ export function ConnectToNearBtnSwap() { onClick={(e) => { e.preventDefault(); e.stopPropagation(); - setButtonLoading(true); + // setButtonLoading(true); modal.show(); }} onMouseEnter={() => { diff --git a/src/context/WalletSelectorContext.tsx b/src/context/WalletSelectorContext.tsx index 3cf3a4aff..3938c9618 100644 --- a/src/context/WalletSelectorContext.tsx +++ b/src/context/WalletSelectorContext.tsx @@ -1,10 +1,7 @@ import React, { useCallback, useContext, useEffect, useState } from 'react'; import { map, distinctUntilChanged } from 'rxjs'; -import { - NetworkId, - setupWalletSelector, -} from '@near-wallet-selector/core'; +import { NetworkId, setupWalletSelector } from '@near-wallet-selector/core'; import type { WalletSelector, AccountState } from '@near-wallet-selector/core'; import { setupModal } from '@near-wallet-selector/modal-ui'; import type { WalletSelectorModal } from '@near-wallet-selector/modal-ui'; @@ -23,9 +20,7 @@ import { setupNightly } from '@near-wallet-selector/nightly'; import getConfig from '../services/config'; import '@near-wallet-selector/modal-ui/styles.css'; -import { - near, -} from '../services/near'; +import { near } from '../services/near'; import { walletIcons } from './walletIcons'; import { getOrderlyConfig } from '../pages/Orderly/config'; import { REF_ORDERLY_ACCOUNT_VALID } from '../pages/Orderly/components/UserBoard/index'; @@ -103,29 +98,29 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { debug: false, modules: [ setupNearWallet({ - iconUrl: walletIcons['near-wallet'], + // iconUrl: walletIcons['near-wallet'], }), setupMyNearWallet({ - iconUrl: walletIcons['my-near-wallet'], + // iconUrl: walletIcons['my-near-wallet'], }), setupSender({ - iconUrl: walletIcons['sender'], + // iconUrl: walletIcons['sender'], }), // @ts-ignore setupMeteorWallet({ - iconUrl: walletIcons['meteor-wallet'], + // iconUrl: walletIcons['meteor-wallet'], }), setupNeth({ - iconUrl: walletIcons['neth'], + // iconUrl: walletIcons['neth'], gas: '300000000000000', bundle: false, }) as any, // @ts-ignore setupNightly({ - iconUrl: walletIcons['nightly'], + // iconUrl: walletIcons['nightly'], }), setupLedger({ - iconUrl: walletIcons['ledger'], + // iconUrl: walletIcons['ledger'], }), // @ts-ignore setupHereWallet(), @@ -204,36 +199,6 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { getAllKeys(accountId); }, [accountId, selector]); - // const getAccount = useCallback(async (): Promise => { - // if (!accountId) { - // return null; - // } - - // const provider = new providers.JsonRpcProvider({ - // url: getConfig().nodeUrl, - // }); - - // return provider - // .query({ - // request_type: 'view_account', - // finality: 'final', - // account_id: accountId, - // }) - // .then((data: any) => ({ - // ...data, - // account_id: accountId, - // })); - // }, [accountId]); - - // useEffect(() => { - // if (!selector || !accountId) return; - - // getAccount().catch((e) => { - // alert(e?.message); - // selector.wallet().then((wallet) => wallet.signOut()); - // }); - // }, [selector, accountId]); - if (!selector || !modal || (!!accountId && isLedger === undefined)) { return null; } diff --git a/src/global.css b/src/global.css index d6625096f..b54c24108 100644 --- a/src/global.css +++ b/src/global.css @@ -1373,3 +1373,90 @@ input[type='range']::-webkit-slider-runnable-track { animation: spin 1s linear infinite; transform-origin: center; } + +/* */ + +.nws-modal-wrapper { + z-index: 999999; +} +.nws-modal-wrapper .nws-modal-overlay { + backdrop-filter: blur(20px); +} +.nws-modal-wrapper .nws-modal { + border: 1px solid rgba(0, 198, 162, 0.5); +} + +::-webkit-scrollbar { + width: 4px; + border-radius: 2px; +} + +::-webkit-scrollbar-thumb { + background: #626486; + border-radius: 2px; +} + +:root { + --wallet-selector-backdrop-bg: rgba(0, 0, 0, 0.5); + --wallet-selector-content-bg: rgb(29, 41, 50); + --wallet-selector-heading-color: #ffffff; + --wallet-selector-text-color: #c0c4e9; + --gradient-dark-icon: #ffffff; + --wallet-selector-close-button-bg-color: transparent; + --wallet-selector-sidebar-border-color: rgba(79, 81, 120, 0.5); + --wallet-selector-selected-wallet-bg: #3f4162; + --wallet-selector-mobile-bottom-section: #2e304b; +} + +#near-wallet-selector-modal { + --gradient-dark-icon: #ffffff; + --what-wallet-icon-color: #000; + --get-wallet-option-bg-color-hover: rgba(255, 255, 255, 0.9); +} + +/* */ +.nws-modal-wrapper .nws-modal { + border: 1px solid #4f5178; +} + +.nws-modal-wrapper .nws-modal .modal-left::-webkit-scrollbar, +options-list::-webkit-scrollbar { + width: 0px; + border-radius: 2px; +} + +.nws-modal-wrapper .nws-modal .modal-left::-webkit-scrollbar-thumb { + background: transparent; + border-radius: 2px; +} + +.wallet-options-wrapper .options-list::-webkit-scrollbar-thumb { + background: #626486; + border-radius: 2px; +} + +.nws-modal-wrapper .nws-modal .nws-modal-body button.middleButton { + color: #000; + background-color: #d2ff3a; + border: none; +} + +.nws-modal-wrapper .nws-modal .nws-modal-body button.middleButton:hover { + opacity: 0.8; +} + +.nws-modal-wrapper .nws-modal .nws-modal-header .close-button svg { + fill: #fff; +} + +.nws-modal-wrapper + .nws-modal + .wallet-options-wrapper + .options-list + .single-wallet.sidebar:hover { + background-color: #3f4162; +} + +.nws-modal-wrapper .nws-modal .connecting-details span { + color: #fff; +} From c80df964768ec32fb0e3031c00e853b89e51c689 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 27 Oct 2023 17:03:22 +0800 Subject: [PATCH 189/204] update z-index --- src/global.css | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/global.css b/src/global.css index b54c24108..e6c683b1c 100644 --- a/src/global.css +++ b/src/global.css @@ -1377,7 +1377,7 @@ input[type='range']::-webkit-slider-runnable-track { /* */ .nws-modal-wrapper { - z-index: 999999; + z-index: 9999; } .nws-modal-wrapper .nws-modal-overlay { backdrop-filter: blur(20px); From 545b474dcf9760a6d5224bda758508fa00c51632 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Fri, 27 Oct 2023 22:17:13 +0800 Subject: [PATCH 190/204] update wallet select ui --- src/context/WalletSelectorContext.tsx | 10 ++--- src/global.css | 64 ++++++++++++++++++++------- 2 files changed, 52 insertions(+), 22 deletions(-) diff --git a/src/context/WalletSelectorContext.tsx b/src/context/WalletSelectorContext.tsx index 3938c9618..1ab0e0b75 100644 --- a/src/context/WalletSelectorContext.tsx +++ b/src/context/WalletSelectorContext.tsx @@ -97,12 +97,14 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { network: getConfig().networkId as NetworkId, debug: false, modules: [ - setupNearWallet({ - // iconUrl: walletIcons['near-wallet'], - }), setupMyNearWallet({ // iconUrl: walletIcons['my-near-wallet'], }), + // @ts-ignore + setupHereWallet(), + setupNearWallet({ + // iconUrl: walletIcons['near-wallet'], + }), setupSender({ // iconUrl: walletIcons['sender'], }), @@ -122,8 +124,6 @@ export const WalletSelectorContextProvider: React.FC = ({ children }) => { setupLedger({ // iconUrl: walletIcons['ledger'], }), - // @ts-ignore - setupHereWallet(), ], }); const _modal = setupModal(_selector, { diff --git a/src/global.css b/src/global.css index e6c683b1c..6a8980b21 100644 --- a/src/global.css +++ b/src/global.css @@ -1374,8 +1374,7 @@ input[type='range']::-webkit-slider-runnable-track { transform-origin: center; } -/* */ - +/* wallet select modal ui start */ .nws-modal-wrapper { z-index: 9999; } @@ -1383,7 +1382,7 @@ input[type='range']::-webkit-slider-runnable-track { backdrop-filter: blur(20px); } .nws-modal-wrapper .nws-modal { - border: 1px solid rgba(0, 198, 162, 0.5); + border: 1px solid #414d55; } ::-webkit-scrollbar { @@ -1398,27 +1397,48 @@ input[type='range']::-webkit-slider-runnable-track { :root { --wallet-selector-backdrop-bg: rgba(0, 0, 0, 0.5); - --wallet-selector-content-bg: rgb(29, 41, 50); + --wallet-selector-content-bg: #313d46; --wallet-selector-heading-color: #ffffff; - --wallet-selector-text-color: #c0c4e9; - --gradient-dark-icon: #ffffff; + --wallet-selector-text-color: #fff; + --gradient-dark-icon: #42525c; --wallet-selector-close-button-bg-color: transparent; - --wallet-selector-sidebar-border-color: rgba(79, 81, 120, 0.5); - --wallet-selector-selected-wallet-bg: #3f4162; - --wallet-selector-mobile-bottom-section: #2e304b; + --wallet-selector-sidebar-border-color: #414d55; + --wallet-selector-selected-wallet-bg: #42525c; + --wallet-selector-mobile-bottom-section: #42525c; +} +.nws-modal-wrapper + .nws-modal + .modal-right + .wallet-what + .content-side + .nws-modal-wrapper + .nws-modal + .modal-right + .wallet-what + .content-side + p { + font-size: 14px; + color: #7e8a93; +} +.nws-modal-wrapper .nws-modal .modal-right .connecting-message { + color: #fff; +} +.nws-modal-wrapper + .nws-modal + .nws-modal-body + .alert-message + .connection + .error-wrapper + p { + color: #fff; } #near-wallet-selector-modal { - --gradient-dark-icon: #ffffff; - --what-wallet-icon-color: #000; + --gradient-dark-icon: #42525c; + --what-wallet-icon-color: rgba(255, 255, 255, 0.65); --get-wallet-option-bg-color-hover: rgba(255, 255, 255, 0.9); } -/* */ -.nws-modal-wrapper .nws-modal { - border: 1px solid #4f5178; -} - .nws-modal-wrapper .nws-modal .modal-left::-webkit-scrollbar, options-list::-webkit-scrollbar { width: 0px; @@ -1454,9 +1474,19 @@ options-list::-webkit-scrollbar { .wallet-options-wrapper .options-list .single-wallet.sidebar:hover { - background-color: #3f4162; + background-color: #42525c; } .nws-modal-wrapper .nws-modal .connecting-details span { color: #fff; } +.nws-modal-wrapper .nws-modal .nws-modal-body button.middleButton { + background: linear-gradient(180deg, #00c6a2 0%, #008b72 100%); + color: #fff; +} +.options-list-section-header { + font-size: 14px; + color: #7e8a93; +} + +/* wallet select modal ui end */ From ca4b015421714433ea131137b8068d73c3029863 Mon Sep 17 00:00:00 2001 From: "nature.xie" Date: Sun, 29 Oct 2023 19:01:50 +0800 Subject: [PATCH 191/204] add apr for stable pool --- src/pages/pools/LiquidityPage.tsx | 152 +++++++++++++++++++++++++----- tailwind.config.js | 2 + 2 files changed, 133 insertions(+), 21 deletions(-) diff --git a/src/pages/pools/LiquidityPage.tsx b/src/pages/pools/LiquidityPage.tsx index 3a77151cf..40a8c2782 100644 --- a/src/pages/pools/LiquidityPage.tsx +++ b/src/pages/pools/LiquidityPage.tsx @@ -1040,7 +1040,7 @@ function MobileLiquidityPage({ {!!getConfig().REF_VE_CONTRACT_ID ? (
- + )}
@@ -2391,7 +2393,7 @@ function LiquidityPage_({
@@ -3675,7 +3679,7 @@ const RenderDisplayTokensAmounts = ({ setChartActiveToken?: (token: string) => void; }) => { return ( -
+
{tokens.map((token, i) => { return ( (false); @@ -3784,7 +3794,7 @@ function StablePoolCard({ to={`/sauce/${poolData.pool.id}`} className={`${ hover || isMobile ? 'bg-v3HoverDarkBgColor' : 'bg-cardBg' - } relative z-20 rounded-xl xs:rounded-t-xl md:rounded-t-xl xs:rounded-b-none md:rounded-b-none px-8 xs:px-5 md:px-5 w-full h-28 xs:h-20 md:h-20 flex items-center justify-between overflow-hidden`} + } relative z-20 rounded-xl xsm:rounded-t-xl xsm:rounded-b-none w-full h-28 xsm:h-20 lg:grid lg:grid-cols-6 overflow-hidden xsm:flex xsm:items-center xsm:justify-between xsm:pr-3`} onMouseEnter={() => { setHover(true); }} @@ -3792,7 +3802,7 @@ function StablePoolCard({ {is_new_pool ? : null}
{watched && ( -
+
)}
{ e.stopPropagation(); e.preventDefault(); @@ -3838,9 +3848,31 @@ function StablePoolCard({
-
+
+ {!h24volume ? '-' : `${getPoolFeeApr(h24volume, standPool)}%`} + {supportFarm && + !Number.isNaN(farmApr) && + farmApr !== null && + farmApr !== undefined && + farmApr > 0 && + h24volume && ( + + {`+${toPrecision((farmApr * 100).toString(), 2)}%`} + + )} +
+
{!h24volume @@ -3852,9 +3884,9 @@ function StablePoolCard({ : `$${toInternationalCurrencySystem(h24volume)}`}
-
+
+
+ +
+ +
+ {!h24volume ? '-' : `${getPoolFeeApr(h24volume, standPool)}%`}{' '} + {supportFarm} + {supportFarm && + !Number.isNaN(farmApr) && + farmApr !== null && + farmApr !== undefined && + farmApr > 0 && + h24volume && ( + + {`+${toPrecision((farmApr * 100).toString(), 2)}%`} + + )} +
+
+
@@ -3945,7 +3997,7 @@ function StablePoolCard({
-
+
{formattedPool.displayMyShareAmount}
@@ -4033,10 +4085,14 @@ function StablePoolList({ searchBy, volumes, watchPools, + farmCounts, + farmAprById, }: { searchBy: string; volumes: Record; watchPools: Pool[]; + farmCounts: Record; + farmAprById: Record; }) { const [option, setOption] = useState('ALL'); @@ -4074,22 +4130,41 @@ function StablePoolList({ const vol1 = Number(volumes[p1.pool.id.toString()] || '0'); const vol2 = Number(volumes[p2.pool.id.toString()] || '0'); + const standPool1 = p1.pool; + standPool1.tvl = p1.poolTVL; + + const standPool2 = p2.pool; + standPool2.tvl = p2.poolTVL; + + const apr1 = + getPoolFeeAprTitle(vol1.toString(), standPool1) + + (farmAprById?.[p1.pool.id] || 0) * 100; + + const apr2 = + getPoolFeeAprTitle(vol2.toString(), standPool2) + + (farmAprById?.[p2.pool.id] || 0) * 100; + const is_p1_sort_top = p1.pool.id == USDTT_USDCC_USDT_USDC_POOL_ID && !clicked; const is_p2_sort_top = p2.pool.id == USDTT_USDCC_USDT_USDC_POOL_ID && !clicked; + if (is_p1_sort_top) return 1; if (is_p2_sort_top) return 1; if (orderStable === 'desc') { if (sortBy === 'tvl') { return v2 - v1; + } else if (sortBy == 'apr') { + return apr2 - apr1; } else { return vol2 - vol1; } } else { if (sortBy === 'tvl') { return v1 - v2; + } else if (sortBy == 'apr') { + return apr1 - apr2; } else { return vol1 - vol2; } @@ -4098,8 +4173,8 @@ function StablePoolList({ return ( <> -
-
+
+
{['ALL', 'USD', 'BTC', 'NEAR'].map((o) => { return (