From b82c0b1a67f8bb5646517f5ccae055e9c5e50e46 Mon Sep 17 00:00:00 2001 From: denis-orbs Date: Sun, 6 Oct 2024 14:20:19 +0300 Subject: [PATCH] a --- packages/dapp-example/src/SushiSwap.tsx | 10 ++++- .../src/components/CreateOrderModal/Steps.tsx | 17 +++------ packages/lib/src/components/OrderDisplay.tsx | 5 ++- .../OrderHistory/OrderHistoryList.tsx | 38 ++++++++++++++----- .../components/OrderHistory/SelectedOrder.tsx | 6 +-- packages/lib/src/components/base/Loader.tsx | 16 ++++---- packages/lib/src/context/context.tsx | 4 +- packages/lib/src/hooks/orders.ts | 25 +++++++----- packages/lib/src/hooks/query.ts | 22 ++++------- packages/lib/src/store.ts | 4 +- packages/lib/src/types.ts | 15 ++++++-- packages/sushiswap/src/index.tsx | 25 +++++++----- 12 files changed, 111 insertions(+), 76 deletions(-) diff --git a/packages/dapp-example/src/SushiSwap.tsx b/packages/dapp-example/src/SushiSwap.tsx index 0b184629..fd0fa704 100644 --- a/packages/dapp-example/src/SushiSwap.tsx +++ b/packages/dapp-example/src/SushiSwap.tsx @@ -9,10 +9,11 @@ import { SelectorOption, TokenListItem } from "./types"; import { getConfig, mapCollection, size, TooltipProps, Configs } from "@orbs-network/twap-ui"; import { DappProvider } from "./context"; import { baseSwapTokens } from "./BaseSwap"; -import { network } from "@defi.org/web3-candies"; +import { eqIgnoreCase, network } from "@defi.org/web3-candies"; const name = "SushiSwap"; const configs = [Configs.SushiArb, Configs.SushiBase, Configs.SushiEth]; + export const useDappTokens = () => { const config = useConfig(); const isBase = config?.chainId === Configs.SushiBase.chainId; @@ -125,6 +126,12 @@ const Tooltip = (props: TooltipProps) => { ); }; +const useToken = (address?: string) => { + const { data: tokens } = useDappTokens(); + + return useMemo(() => tokens?.find((it: any) => eqIgnoreCase(it.address, address || "")), [tokens, address]); +}; + const TWAPComponent = ({ limit }: { limit?: boolean }) => { const { account, library, chainId } = useWeb3React(); const connect = useConnectWallet(); @@ -183,6 +190,7 @@ const TWAPComponent = ({ limit }: { limit?: boolean }) => { onDstTokenSelected={(it: any) => setToToken(it)} onSwitchTokens={onSwitchTokens} Tooltip={Tooltip} + useToken={useToken} /> ); }; diff --git a/packages/lib/src/components/CreateOrderModal/Steps.tsx b/packages/lib/src/components/CreateOrderModal/Steps.tsx index 586ebb54..832b5b9d 100644 --- a/packages/lib/src/components/CreateOrderModal/Steps.tsx +++ b/packages/lib/src/components/CreateOrderModal/Steps.tsx @@ -59,7 +59,7 @@ const StepStatus = ({ step }: { step: Step }) => { ); } - if (step.status === "pending") { + if (step.status === "loading") { return ; } @@ -77,19 +77,12 @@ const useStep = (step?: SwapStep) => { const nativeToken = useNetwork()?.native; return useMemo((): Step | undefined => { if (!step) return; - const isWrapPending = swapStep === "wrap" && !wrapTxHash && !wrapSuccess; - const isWrapLoading = swapStep === "wrap" && wrapTxHash && !wrapSuccess; - const isApprovePending = swapStep === "approve" && !approveTxHash && !approveSuccess; - const isApproveLoading = swapStep === "approve" && approveTxHash && !approveSuccess; - const isCreatePending = swapStep === "createOrder" && !createOrdertxHash && !createOrderSuccess; - const isCreateLoading = swapStep === "createOrder" && createOrdertxHash && !createOrderSuccess; - if (step === "wrap") { return { title: `Wrap ${nativeToken?.symbol}`, Icon: RiSwapFill, image: nativeToken?.logoUrl, - status: wrapSuccess ? "completed" : isWrapLoading ? "loading" : isWrapPending ? "pending" : "disabled", + status: wrapSuccess ? "completed" : swapStep === "wrap" ? "loading" : "disabled", }; } @@ -97,7 +90,7 @@ const useStep = (step?: SwapStep) => { return { title: `Approve ${srcToken?.symbol}`, Icon: RiCheckboxCircleFill, - status: approveSuccess ? "completed" : isApproveLoading ? "loading" : isApprovePending ? "pending" : "disabled", + status: approveSuccess ? "completed" : swapStep === "approve" ? "loading" : "disabled", }; } @@ -105,10 +98,10 @@ const useStep = (step?: SwapStep) => { return { title: "Create Order", Icon: RiSwapFill, - status: createOrderSuccess ? "completed" : isCreateLoading ? "loading" : isCreatePending ? "pending" : "disabled", + status: createOrderSuccess ? "completed" : swapStep === "createOrder" ? "loading" : "disabled", }; } - }, [step, nativeToken, srcToken, createOrdertxHash, approveTxHash, wrapTxHash, swapStep, createOrderSuccess, wrapSuccess, approveSuccess, swapSteps]); + }, [createOrdertxHash, approveTxHash, wrapTxHash, swapStep, createOrderSuccess, wrapSuccess, approveSuccess, swapSteps, srcToken, nativeToken]); }; const StepContainer = styled(StyledColumnFlex)<{ selected: number }>(({ selected }) => ({ diff --git a/packages/lib/src/components/OrderDisplay.tsx b/packages/lib/src/components/OrderDisplay.tsx index 272bf44b..1e8ba269 100644 --- a/packages/lib/src/components/OrderDisplay.tsx +++ b/packages/lib/src/components/OrderDisplay.tsx @@ -27,7 +27,7 @@ const ChunkSize = ({ srcChunkAmount, srcToken }: { srcChunkAmount?: string; srcT const _srcChunkAmount = useFormatNumberV2({ value: srcChunkAmount, decimalScale: 3 }); return ( - {`${_srcChunkAmount} ${srcToken?.symbol}`} + {`${srcChunkAmount ? _srcChunkAmount : "-"} ${srcToken?.symbol}`} ); }; @@ -45,11 +45,12 @@ const MinDestAmount = ({ }) => { const { translations } = useTwapContext(); const formattedValue = useFormatNumberV2({ value: dstMinAmountOut }); + if (isMarketOrder) return null; return ( - {`${formattedValue} ${dstToken?.symbol}`} + {`${dstMinAmountOut ? formattedValue : "-"} ${dstToken?.symbol}`} ); }; diff --git a/packages/lib/src/components/OrderHistory/OrderHistoryList.tsx b/packages/lib/src/components/OrderHistory/OrderHistoryList.tsx index d92ad30f..3091ae48 100644 --- a/packages/lib/src/components/OrderHistory/OrderHistoryList.tsx +++ b/packages/lib/src/components/OrderHistory/OrderHistoryList.tsx @@ -109,18 +109,26 @@ const ListOrder = React.memo( setSize(index, root.current.getBoundingClientRect().height); }, [index]); - if (!order) return null; + const onClick = useCallback(() => { + if (!order?.isLoading) { + onSelect(parsedOrder?.id); + } + }, [order?.isLoading, onSelect, parsedOrder?.id]); + + if (!order) { + return
; + } return ( - onSelect(parsedOrder?.id)}> + - + - + @@ -184,18 +192,30 @@ const StyledListOrder = styled(StyledColumnFlex)({ }, }); -const TokenDisplay = ({ token, amount }: { token?: Token; amount?: string }) => { +const TokenDisplay = ({ token, amount, isLoading }: { token?: Token; amount?: string; isLoading: boolean }) => { const _amount = useFormatNumberV2({ value: amount, decimalScale: 4 }); return ( - - - {_amount} {token?.symbol} - + {isLoading ? ( + + ) : ( + <> + + + {_amount} {token?.symbol} + + + )} ); }; +const StyledTokenDisplayLoader = styled(Loader)({ + borderRadius: "50%", + width: 20, + height: 20, +}); + const StyledTokenDisplay = styled(StyledRowFlex)({ width: "auto", ".twap-order-token-text": { diff --git a/packages/lib/src/components/OrderHistory/SelectedOrder.tsx b/packages/lib/src/components/OrderHistory/SelectedOrder.tsx index e13121fb..1cbf4c74 100644 --- a/packages/lib/src/components/OrderHistory/SelectedOrder.tsx +++ b/packages/lib/src/components/OrderHistory/SelectedOrder.tsx @@ -149,7 +149,7 @@ const AmountOutFilled = ({ order }: { order: OrderUI }) => { return ( - {amount || 0} {order?.dstToken.symbol} + {amount || 0} {order?.dstToken?.symbol} ); @@ -161,7 +161,7 @@ const AmountIn = ({ order }: { order: OrderUI }) => { return ( - {amount || 0} {order?.srcToken.symbol} + {amount || 0} {order?.srcToken?.symbol} ); @@ -173,7 +173,7 @@ const AmountInFilled = ({ order }: { order: OrderUI }) => { return ( - {amount || 0} {order?.srcToken.symbol} + {amount || 0} {order?.srcToken?.symbol} ); diff --git a/packages/lib/src/components/base/Loader.tsx b/packages/lib/src/components/base/Loader.tsx index c628d6e8..1f498144 100644 --- a/packages/lib/src/components/base/Loader.tsx +++ b/packages/lib/src/components/base/Loader.tsx @@ -1,11 +1,14 @@ import { keyframes, styled } from "styled-components"; -const shimmer = keyframes` +const fade = keyframes` 0% { - background-position: -468px 0; + opacity: 0.5; + } + 50% { + opacity: 1; } 100% { - background-position: 468px 0; + opacity: 0.5; } `; @@ -21,12 +24,9 @@ const SkeletonWrapper = styled.div` display: inline-block; width: ${(props) => props.width || "100%"}; height: ${(props) => props.height || "100%"}; - background: #f6f7f8; - background-image: linear-gradient(to right, #f6f7f8 0%, #edeef1 20%, #f6f7f8 40%, #f6f7f8 100%); - background-repeat: no-repeat; - background-size: 800px 104px; + background-color: #f6f7f8; position: relative; - animation: ${shimmer} 1.2s infinite linear; + animation: ${fade} 1.5s infinite ease-in-out; border-radius: ${(props) => props.borderRadius || "4px"}; margin: ${(props) => props.margin || "0"}; `; diff --git a/packages/lib/src/context/context.tsx b/packages/lib/src/context/context.tsx index eadf477c..c0761259 100644 --- a/packages/lib/src/context/context.tsx +++ b/packages/lib/src/context/context.tsx @@ -163,7 +163,7 @@ export const Content = (props: TwapLibProps) => { onSrcTokenSelected: props.onSrcTokenSelected, onSwitchTokens: props.onSwitchTokens || (() => {}), isLimitPanel: !!props.isLimitPanel, - tokens: props.parsedTokens, + tokens: props.parsedTokens || [], maxFeePerGas: props.maxFeePerGas, priorityFeePerGas: props.priorityFeePerGas, askDataParams: props.askDataParams, @@ -173,6 +173,8 @@ export const Content = (props: TwapLibProps) => { isExactAppoval: props.isExactAppoval, fee: props.fee, nativeUsd: props.nativeUsd, + useDappToken: props.useDappToken, + useParsedToken: props.useParsedToken, }} > diff --git a/packages/lib/src/hooks/orders.ts b/packages/lib/src/hooks/orders.ts index 42feb084..f4e28a2f 100644 --- a/packages/lib/src/hooks/orders.ts +++ b/packages/lib/src/hooks/orders.ts @@ -5,21 +5,25 @@ import { HistoryOrder } from "../types"; import { amountUiV2, flatMap, flatMapObject } from "../utils"; import BN from "bignumber.js"; import { query } from "./query"; -import { useEstimatedDelayBetweenChunksMillis } from "./hooks"; +import { useEstimatedDelayBetweenChunksMillis, useGetTokenFromParsedTokensList } from "./hooks"; export const useParseOrderUi = (order?: HistoryOrder) => { - const config = useTwapContext()?.config; + const { config, useParsedToken } = useTwapContext(); const estimatedDelayBetweenChunksMillis = useEstimatedDelayBetweenChunksMillis(); + const getTokensFromTokensList = useGetTokenFromParsedTokensList(); + const srcTokenFromHook = useParsedToken?.(order?.srcTokenAddress); + const dstTokenFromHook = useParsedToken?.(order?.dstTokenAddress); return useMemo(() => { if (!config || !order) return; - const { srcToken, dstToken, srcAmount, srcBidAmount, dstMinAmount, srcFilledAmount, fillDelay, createdAt, deadline, dollarValueOut, progress } = order; - if (!srcToken || !dstToken) return; + const { srcAmount, srcBidAmount, dstMinAmount, srcFilledAmount, fillDelay, createdAt, deadline, dollarValueOut, progress } = order; + const srcToken = srcTokenFromHook || getTokensFromTokensList(order.srcTokenAddress); + const dstToken = dstTokenFromHook || getTokensFromTokensList(order.dstTokenAddress); const isMarketOrder = BN(dstMinAmount || 0).lte(1); - const srcChunkAmountUi = amountUiV2(srcToken.decimals, srcBidAmount) || "0"; - const dstMinAmountOutUi = amountUiV2(dstToken.decimals, dstMinAmount) || "0"; - const dstAmount = amountUiV2(dstToken.decimals, order.dstAmount) || "0"; - const srcFilledAmountUi = amountUiV2(srcToken.decimals, srcFilledAmount) || "0"; + const srcChunkAmountUi = !srcToken ? "" : amountUiV2(srcToken.decimals, srcBidAmount) || ""; + const dstMinAmountOutUi = !dstToken ? "" : amountUiV2(dstToken.decimals, dstMinAmount) || "0"; + const dstAmount = !dstToken ? "" : amountUiV2(dstToken.decimals, order.dstAmount) || "0"; + const srcFilledAmountUi = !srcToken ? "" : amountUiV2(srcToken.decimals, srcFilledAmount) || "0"; const totalChunks = order.totalChunks || 0; return { @@ -28,7 +32,7 @@ export const useParseOrderUi = (order?: HistoryOrder) => { createdAtUi: moment(createdAt * 1000).format("ll HH:mm"), deadlineUi: moment(deadline * 1000).format("ll HH:mm"), isMarketOrder, - srcAmountUi: amountUiV2(srcToken.decimals, srcAmount) || "0", + srcAmountUi: !srcToken ? "" : amountUiV2(srcToken.decimals, srcAmount) || "0", srcAmountUsdUi: order.dollarValueIn || "0", srcChunkAmountUi, srcFilledAmountUi, @@ -46,8 +50,9 @@ export const useParseOrderUi = (order?: HistoryOrder) => { txHash: order.txHash, limitPrice: isMarketOrder ? undefined : BN(dstMinAmountOutUi).div(srcChunkAmountUi).toString() || "0", excecutionPrice: BN(dstAmount).gt(0) && BN(srcFilledAmountUi).gt(0) ? BN(dstAmount).div(srcFilledAmountUi).toString() : undefined, + isLoading: !srcToken || !dstToken, }; - }, [config, order, estimatedDelayBetweenChunksMillis]); + }, [config, order, estimatedDelayBetweenChunksMillis, getTokensFromTokensList, srcTokenFromHook, dstTokenFromHook]); }; export const useOrderById = (id?: number) => { diff --git a/packages/lib/src/hooks/query.ts b/packages/lib/src/hooks/query.ts index f551b91b..beeff69b 100644 --- a/packages/lib/src/hooks/query.ts +++ b/packages/lib/src/hooks/query.ts @@ -7,7 +7,7 @@ import { useTwapContext } from "../context/context"; import { QueryKeys } from "../enums"; import FEE_ON_TRANSFER_ABI from "../abi/FEE_ON_TRANSFER.json"; import { amountBNV2, compact, getTheGraphUrl, groupBy, logger, orderBy } from "../utils"; -import { HistoryOrder, OrdersData, Status, Token } from "../types"; +import { ExtendsOrderHistory, HistoryOrder, OrdersData, Status, Token } from "../types"; import { useGetHasAllowance, useGetTokenFromParsedTokensList, useNetwork } from "./hooks"; import { ordersStore } from "../store"; import { getGraphOrders } from "../order-history"; @@ -156,7 +156,7 @@ const useAddNewOrder = () => { const config = useTwapContext().config; return useCallback( - (order: HistoryOrder) => { + (order: ExtendsOrderHistory) => { try { if (!config) return; ordersStore.addOrder(config.chainId, order); @@ -211,7 +211,7 @@ const useUpdateOrderStatusToCanceled = () => { }; export const useOrdersHistory = () => { - const { tokens, state, config, account } = useTwapContext(); + const { state, config, account } = useTwapContext(); const QUERY_KEY = useOrderHistoryKey(); const getTokensFromTokensList = useGetTokenFromParsedTokensList(); @@ -241,21 +241,13 @@ export const useOrdersHistory = () => { orders = orders.filter((o) => eqIgnoreCase(config.exchangeAddress, o.exchange || "")); logger("filtered orders by exchange address", config.exchangeAddress, orders); - orders = orders.map((order) => { - return { - ...order, - srcToken: getTokensFromTokensList(order.srcTokenAddress), - dstToken: getTokensFromTokensList(order.dstTokenAddress), - }; - }); - let result = compact(orders.filter((o) => o.srcToken && o.dstToken)); - result = orderBy(result, (o) => o.createdAt, "desc"); - result = groupBy(result, "status"); + orders = orderBy(orders, (o) => o.createdAt, "desc"); + orders = groupBy(orders, "status"); - return result as any; + return orders as any; }, { - enabled: !!config && tokens?.length > 10 && !state.showConfirmation && !!account, + enabled: !!config && !state.showConfirmation && !!account, refetchInterval: REFETCH_ORDER_HISTORY, onError: (error: any) => console.log(error), refetchOnWindowFocus: true, diff --git a/packages/lib/src/store.ts b/packages/lib/src/store.ts index 8323acd0..5f0741f9 100644 --- a/packages/lib/src/store.ts +++ b/packages/lib/src/store.ts @@ -29,7 +29,7 @@ class OrdersStore implements OrdersState { } private saveOrders(): void { - localStorage.setItem("twap-orders", JSON.stringify(this.orders)); + localStorage?.setItem("twap-orders", JSON.stringify(this.orders)); } cancelOrder(chainId: number, orderId: number): void { console.log("Cancelling order", orderId); @@ -47,7 +47,7 @@ class OrdersStore implements OrdersState { } private loadOrders(): void { - const storedOrders = localStorage.getItem("twap-orders"); + const storedOrders = localStorage?.getItem("twap-orders"); if (storedOrders) { this.orders = JSON.parse(storedOrders); } diff --git a/packages/lib/src/types.ts b/packages/lib/src/types.ts index eef16022..914fe1e7 100644 --- a/packages/lib/src/types.ts +++ b/packages/lib/src/types.ts @@ -228,12 +228,15 @@ export interface HistoryOrder { srcTokenAddress?: string; dstTokenAddress?: string; totalChunks?: number; - srcToken?: Token; - dstToken?: Token; dex?: string; exchange?: string; } +export interface ExtendsOrderHistory extends HistoryOrder { + srcToken?: Token; + dstToken?: Token; +} + type UseTrade = (fromToken?: string, toToken?: string, amount?: string) => { isLoading?: boolean; outAmount?: string }; export interface TwapLibProps { srcUsd?: string | number; @@ -253,7 +256,7 @@ export interface TwapLibProps { storeOverride?: StoreOverride; srcToken?: Token; dstToken?: Token; - dappTokens: any; + dappTokens?: any; uiPreferences?: TwapContextUIPreferences; onSrcTokenSelected: (token: any) => void; onDstTokenSelected: (token: any) => void; @@ -263,12 +266,14 @@ export interface TwapLibProps { enableQueryParams?: boolean; minNativeTokenBalance?: string; isLimitPanel?: boolean; - parsedTokens: Token[]; + parsedTokens?: Token[]; onSwitchTokens?: () => void; isWrongChain?: boolean; isExactAppoval?: boolean; fee?: string; nativeUsd?: string; + useParsedToken?: (address?: string) => Token | undefined; + useDappToken?: (address?: string) => any; } export type Token = { @@ -495,6 +500,8 @@ export interface TWAPContextProps { isExactAppoval?: boolean; fee?: string; nativeUsd?: string; + useParsedToken?: (address?: string) => Token | undefined; + useDappToken?: (address?: string) => any; } export enum Status { diff --git a/packages/sushiswap/src/index.tsx b/packages/sushiswap/src/index.tsx index eb5a3c2a..791a13a2 100644 --- a/packages/sushiswap/src/index.tsx +++ b/packages/sushiswap/src/index.tsx @@ -205,14 +205,14 @@ const useParseToken = () => { return { ...nativeToken, logoUrl: getTokenLogo(token) || nativeToken.logoUrl, - }; + } as Token; } return { address: Web3.utils.toChecksumAddress(token.address), decimals: token.decimals, symbol: token.symbol, logoUrl: getTokenLogo(token), - }; + } as Token; } catch (error) { console.error("Invalid token", token); } @@ -240,6 +240,7 @@ interface SushiProps extends TWAPProps { connector?: any; NetworkSelector?: FC<{ children: ReactNode }>; Button?: FC<{ children: ReactNode; disabled?: boolean }>; + useToken: (address?: string) => any; } interface AdapterContextProps extends SushiProps { @@ -253,11 +254,8 @@ const useAdapterContext = () => useContext(AdapterContext); const useWToken = () => { const context = useAdapterContext(); - - return useMemo(() => { - const wTokenAddress = network(context.config.chainId).wToken.address; - return context.dappTokens?.find((it: any) => eqIgnoreCase(it.address || "", wTokenAddress || "")); - }, [context.dappTokens, context.config]); + const token = context.useToken(network(context.config.chainId).wToken.address); + return token; }; const useIsNative = () => { @@ -356,6 +354,15 @@ const useParsedTokens = () => { }, [context.dappTokens, parseToken]); }; +const useParsedToken = (address?: string) => { + const { useToken } = useAdapterContext(); + const parseToken = useParseToken(); + + const token = useToken(address); + + return useMemo(() => parseToken(token), [token, parseToken]); +}; + const useIsWrongChain = () => { const context = useAdapterContext(); @@ -406,8 +413,8 @@ const TWAPContent = () => { translations={translations as Translations} provider={provider} account={!context.configChainId ? undefined : context.account} - dappTokens={context.dappTokens} - parsedTokens={parsedTokens} + useParsedToken={useParsedToken} + useDappToken={context.useToken} srcToken={srcToken} dstToken={dstToken} onDstTokenSelected={context.onDstTokenSelected}