diff --git a/packages/web/src/common/utils/data-check-util.ts b/packages/web/src/common/utils/data-check-util.ts index 6a2fe2591..3ef56b295 100644 --- a/packages/web/src/common/utils/data-check-util.ts +++ b/packages/web/src/common/utils/data-check-util.ts @@ -21,3 +21,8 @@ export const notNullNumberType = (v: any) => { export const notEmptyStringType = (v: string) => { return notNullStringType(v) && emptyStrCheckAfterTrim(v); }; + +export function isAmount(str: string) { + const regex = /^\d+(\.\d*)?$/; + return regex.test(str); +} diff --git a/packages/web/src/common/values/data-constant.ts b/packages/web/src/common/values/data-constant.ts index 55f13be1d..c976f1735 100644 --- a/packages/web/src/common/values/data-constant.ts +++ b/packages/web/src/common/values/data-constant.ts @@ -8,7 +8,7 @@ export type IncentivizedOptions = export type StatusOptions = "SUCCESS" | "PENDING" | "FAILED"; export type ActiveStatusOptions = "ACTIVE" | "IN_ACTIVE" | "NONE"; export type TokenTableSelectType = "NATIVE" | "GRC20" | "ALL"; -export type ExactTypeOption = "EXACT_IN" | "EXACT_OUT"; +export type SwapDirectionType = "EXACT_IN" | "EXACT_OUT"; export enum NotificationType { "Approve" = 0, "CreatePool" = 1, diff --git a/packages/web/src/components/earn-add/earn-add-confirm-amount-info/EarnAddConfirmAmountInfo.stories.tsx b/packages/web/src/components/earn-add/earn-add-confirm-amount-info/EarnAddConfirmAmountInfo.stories.tsx deleted file mode 100644 index 3691f170c..000000000 --- a/packages/web/src/components/earn-add/earn-add-confirm-amount-info/EarnAddConfirmAmountInfo.stories.tsx +++ /dev/null @@ -1,32 +0,0 @@ -import EarnAddConfirmAmountInfo, { type EarnAddConfirmAmountInfoProps } from "./EarnAddConfirmAmountInfo"; -import { Meta, StoryObj } from "@storybook/react"; - - -const token = { - info: { - chainId: "test3", - address: "0x111111111117dC0aa78b770fA6A738034120C302", - path: "gno.land/r/demo/1inch", - name: "1inch", - symbol: "1INCH", - decimals: 6, - logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", - priceId: "1inch", - createdAt: "1999-01-01T00:00:01Z" - }, - amount: "12,211", - usdPrice: "$12.3", -}; - -export default { - title: "earn-add/EarnAddConfirmAmountInfo", - component: EarnAddConfirmAmountInfo, -} as Meta; - -export const Default: StoryObj = { - args: { - tokenA: token, - tokenB: token, - feeRate: "0.30%" - }, -}; \ No newline at end of file diff --git a/packages/web/src/components/earn-add/earn-add-confirm/EarnAddConfirm.stories.tsx b/packages/web/src/components/earn-add/earn-add-confirm/EarnAddConfirm.stories.tsx index 459a62463..23ef5753c 100644 --- a/packages/web/src/components/earn-add/earn-add-confirm/EarnAddConfirm.stories.tsx +++ b/packages/web/src/components/earn-add/earn-add-confirm/EarnAddConfirm.stories.tsx @@ -7,6 +7,7 @@ export default { component: EarnAddConfirm, } as Meta; + const tokenA = { info: { chainId: "test3", @@ -41,7 +42,11 @@ const tokenB = { const amountInfo = { tokenA: tokenA, + tokenAAmount: "123", + tokenAUSDPrice: "1234", tokenB: tokenB, + tokenBAmount: "123", + tokenBUSDPrice: "1234", feeRate: "0.30%" }; diff --git a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx index e7b5609aa..9d96d02a5 100644 --- a/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx +++ b/packages/web/src/components/earn-add/earn-add-liquidity/EarnAddLiquidity.tsx @@ -10,10 +10,10 @@ import { TokenAmountInputModel } from "@hooks/token/use-token-amount-input"; import DoubleLogo from "@components/common/double-logo/DoubleLogo"; import IconSettings from "@components/common/icons/IconSettings"; import Badge, { BADGE_TYPE } from "@components/common/badge/Badge"; -import { TokenModel } from "@models/token/token-model"; import { PoolModel } from "@models/pool/pool-model"; import SelectPriceRange from "@components/common/select-price-range/SelectPriceRange"; import SelectPriceRangeSummary from "@components/common/select-price-range-summary/SelectPriceRangeSummary"; +import { TokenModel } from "@models/token/token-model"; interface EarnAddLiquidityProps { mode: AddLiquidityType; diff --git a/packages/web/src/components/home/home-swap/HomeSwap.tsx b/packages/web/src/components/home/home-swap/HomeSwap.tsx index 990aa3f69..973afcf62 100644 --- a/packages/web/src/components/home/home-swap/HomeSwap.tsx +++ b/packages/web/src/components/home/home-swap/HomeSwap.tsx @@ -5,7 +5,14 @@ import Button, { ButtonHierarchy } from "@components/common/button/Button"; import SelectPairButton from "@components/common/select-pair-button/SelectPairButton"; import IconSwapArrowDown from "@components/common/icons/IconSwapArrowDown"; import { DeviceSize } from "@styles/media"; -import { SwapTokenModel } from "@models/swap/swap-token-model"; +import { TokenModel } from "@models/token/token-model"; + +interface SwapTokenModel { + token: TokenModel; + amount: string; + price: string; + balance: string; +} interface HomeSwapProps { from: SwapTokenModel; diff --git a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.spec.tsx b/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.spec.tsx deleted file mode 100644 index f6c01788b..000000000 --- a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.spec.tsx +++ /dev/null @@ -1,49 +0,0 @@ -import ConfirmSwapModal from "./ConfirmSwapModal"; -import { render } from "@testing-library/react"; -import { Provider as JotaiProvider } from "jotai"; -import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; -import { DEVICE_TYPE } from "@styles/media"; - -describe("ConfirmSwapModal Component", () => { - it("should render", () => { - const mockProps = { - onConfirmModal: () => null, - submitSwap: () => null, - tolerance: "", - submit: false, - isFetching: false, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, - swapGasInfo: dummySwapGasInfo, - breakpoint: DEVICE_TYPE.WEB, - }; - render( - - - - - , - ); - }); -}); diff --git a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.stories.tsx b/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.stories.tsx index fa5c2cdc4..a867886f0 100644 --- a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.stories.tsx +++ b/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.stories.tsx @@ -4,8 +4,79 @@ import { css } from "@emotion/react"; import { action } from "@storybook/addon-actions"; import ConfirmSwapModal from "./ConfirmSwapModal"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; -import { DEVICE_TYPE } from "@styles/media"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; + +const swapTokenInfo: SwapTokenInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenAAmount: "", + tokenABalance: "", + tokenAUSD: 0, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenBAmount: "", + tokenBBalance: "", + tokenBUSD: 0, + direction: "EXACT_IN", + slippage: 10 +}; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; export default { title: "swap/ConfirmSwapModal", @@ -22,35 +93,12 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - onConfirmModal: action("onConfirmModal"), - submitSwap: action("submitSwap"), - swapGasInfo: dummySwapGasInfo, - breakpoint: DEVICE_TYPE.WEB, - tolerance: "5", - submit: false, - isFetching: true, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + submitted: false, + swapTokenInfo, + swapSummaryInfo, + swapResult: null, + swap: action("swap"), + close: action("close"), }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.tsx b/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.tsx index 49c40a210..5f4efb2d8 100644 --- a/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.tsx +++ b/packages/web/src/components/swap/confirm-swap-modal/ConfirmSwapModal.tsx @@ -1,65 +1,86 @@ -import React, { useEffect, useRef } from "react"; +import React, { useMemo, useRef } from "react"; import { ConfirmSwapModalBackground, ConfirmModal, SwapDivider, } from "./ConfirmSwapModal.styles"; -import { - SwapData, - SwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { TokenInfo } from "../swap-card/SwapCard"; import IconClose from "@components/common/icons/IconCancel"; import IconSwapArrowDown from "@components/common/icons/IconSwapArrowDown"; import IconInfo from "@components/common/icons/IconInfo"; import Button, { ButtonHierarchy } from "@components/common/button/Button"; -import { DEVICE_TYPE } from "@styles/media"; import IconSuccess from "@components/common/icons/IconSuccess"; import IconOpenLink from "@components/common/icons/IconOpenLink"; import IconFailed from "@components/common/icons/IconFailed"; import LoadingSpinner from "@components/common/loading-spinner/LoadingSpinner"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; +import { SwapSummaryInfo, swapDirectionToGuaranteedType } from "@models/swap/swap-summary-info"; +import { SwapResultInfo } from "@models/swap/swap-result-info"; +import useModalCloseEvent from "@hooks/common/use-modal-close-event"; +import { toNumberFormat } from "@utils/number-utils"; +import { numberToFormat } from "@utils/string-utils"; interface ConfirmSwapModalProps { - onConfirmModal: () => void; - submitSwap: (event: React.MouseEvent) => void; - from: TokenInfo; - to: TokenInfo; - swapGasInfo: SwapGasInfo; - breakpoint: DEVICE_TYPE; - tolerance: string; - submit: boolean; - isFetching: boolean; - swapResult: SwapData | null; + submitted: boolean; + swapTokenInfo: SwapTokenInfo; + swapSummaryInfo: SwapSummaryInfo; + swapResult: SwapResultInfo | null; + + swap: (event: React.MouseEvent) => void; + close: () => void; } const ConfirmSwapModal: React.FC = ({ - onConfirmModal, - submitSwap, - from, - to, - swapGasInfo, - breakpoint, - tolerance, - submit, - isFetching, + submitted, + swapTokenInfo, + swapSummaryInfo, swapResult, + swap, + close, }) => { const menuRef = useRef(null); - useEffect(() => { - const closeMenu = (e: MouseEvent) => { - if (menuRef.current && menuRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - onConfirmModal(); - } - }; - window.addEventListener("click", closeMenu, true); - return () => { - window.removeEventListener("click", closeMenu, true); - }; - }, [menuRef, onConfirmModal]); + useModalCloseEvent(menuRef, close); + + + const swapRateDescription = useMemo(() => { + const { tokenA, tokenB, swapRate } = swapSummaryInfo; + return `1 ${tokenA.symbol} = ${numberToFormat(swapRate)} ${tokenB.symbol}`; + }, [swapSummaryInfo]); + + const swapRateUSD = useMemo(() => { + const swapRateUSD = swapSummaryInfo.swapRateUSD; + return numberToFormat(swapRateUSD); + }, [swapSummaryInfo.swapRateUSD]); + + const priceImpactStr = useMemo(() => { + const priceImpact = swapSummaryInfo.priceImpact; + return `${priceImpact}%`; + }, [swapSummaryInfo.priceImpact]); + + const slippageStr = useMemo(() => { + const slippage = swapTokenInfo.slippage; + return `${slippage}%`; + }, [swapTokenInfo.slippage]); + + const guaranteedTypeStr = useMemo(() => { + const swapDirection = swapSummaryInfo.swapDirection; + return swapDirectionToGuaranteedType(swapDirection); + }, [swapSummaryInfo.swapDirection]); + + const guaranteedStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.guaranteedAmount; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.guaranteedAmount]); + + const gasFeeStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.gasFee; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.gasFee]); + + const gasFeeUSDStr = useMemo(() => { + const gasFeeUSD = swapSummaryInfo.gasFeeUSD; + return `$${toNumberFormat(gasFeeUSD)}`; + }, [swapSummaryInfo.gasFeeUSD]); return ( @@ -67,107 +88,33 @@ const ConfirmSwapModal: React.FC = ({
Confirm Swap -
+
- {submit ? ( - <> - {isFetching && ( - <> -
- -
-
- Waiting for Confirmation - - Swapping 0.1 GNOS for 0.12 GNOT - -
- Confirm this transaction in your wallet -
-
- - )} - {!isFetching && swapResult?.success && ( - <> -
- -
-
- Transaction Submitted -
- View Transaction -
{ - window.open(swapResult?.transaction, "_blank"); - }} - > - -
-
-
-
-
- - )} - {!isFetching && !swapResult?.success && ( - <> -
- -
-
- Transaction Rejected -
- - Your transaction has been rejected. Please try again. - -
-
-
-
- - )} - + {submitted ? ( + ) : ( <>
- {from.amount} + {swapTokenInfo.tokenAAmount}
logo - {from.symbol} + {swapSummaryInfo.tokenA.symbol}
- {from.price} + {swapTokenInfo.tokenAUSD}
@@ -177,30 +124,29 @@ const ConfirmSwapModal: React.FC = ({
- {to.amount} + {swapTokenInfo.tokenBAmount}
logo - {to.symbol} + {swapSummaryInfo.tokenB.symbol}
- {to.price} + {swapTokenInfo.tokenBUSD}
-
+
- {from.amount} {from.symbol} = {from.gnosExchangePrice}{" "} - GNOS + {swapRateDescription} - {from.usdExchangePrice} + {swapRateUSD}
@@ -208,26 +154,26 @@ const ConfirmSwapModal: React.FC = ({
Price Impact - {swapGasInfo.priceImpact} + {priceImpactStr}
Max. Slippage - {tolerance}% + {slippageStr}
- Min. Received + {guaranteedTypeStr} - {swapGasInfo.minReceived} + {guaranteedStr}
Gas Fee - {swapGasInfo.gasFee} GNOT{" "} + {gasFeeStr} - ({swapGasInfo.usdExchangeGasFee}) + ({gasFeeUSDStr})
@@ -238,11 +184,11 @@ const ConfirmSwapModal: React.FC = ({ text="Confrim Swap" style={{ fullWidth: true, - height: breakpoint === DEVICE_TYPE.MOBILE ? 41 : 57, + height: 57, fontType: "body7", hierarchy: ButtonHierarchy.Primary, }} - onClick={submitSwap} + onClick={swap} />
@@ -253,4 +199,98 @@ const ConfirmSwapModal: React.FC = ({ ); }; +interface ConfirmSwapResultProps { + swapResult: SwapResultInfo | null; + close: () => void; +} + +const ConfirmSwapResult: React.FC = ({ + swapResult, + close, +}) => { + + if (swapResult === null) { + return ( +
+
+ +
+
+ Waiting for Confirmation + + Swapping 0.1 GNOS for 0.12 GNOT + +
+ Confirm this transaction in your wallet +
+
+
+ ); + } + + if (swapResult.success) { + return ( + <> +
+ +
+
+ Transaction Submitted +
+ View Transaction +
{ + window.open(swapResult?.hash, "_blank"); + }} + > + +
+
+
+
+
+ + ); + } + + return ( + <> +
+ +
+
+ Transaction Rejected +
+ + Your transaction has been rejected. Please try again. + +
+
+
+
+ + ); +}; + export default ConfirmSwapModal; diff --git a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.spec.tsx b/packages/web/src/components/swap/select-token-modal/SelectTokenModal.spec.tsx deleted file mode 100644 index 6226d8326..000000000 --- a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.spec.tsx +++ /dev/null @@ -1,24 +0,0 @@ -import SearchMenuModal from "./SelectTokenModal"; -import { render } from "@testing-library/react"; -import { Provider as JotaiProvider } from "jotai"; -import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; -import { coinList } from "@containers/swap-container/SwapContainer"; - -describe("SearchMenuModal Component", () => { - it("should render", () => { - const mockProps = { - search: () => null, - keyword: "", - onSelectTokenModal: () => null, - coinList: coinList(), - changeToken: () => null, - }; - render( - - - - - , - ); - }); -}); diff --git a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.stories.tsx b/packages/web/src/components/swap/select-token-modal/SelectTokenModal.stories.tsx deleted file mode 100644 index 56b20dceb..000000000 --- a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.stories.tsx +++ /dev/null @@ -1,41 +0,0 @@ -import React from "react"; -import { ComponentStory, ComponentMeta } from "@storybook/react"; -import { css } from "@emotion/react"; -import { action } from "@storybook/addon-actions"; - -import SelectTokenModal from "./SelectTokenModal"; -import { coinList } from "@containers/swap-container/SwapContainer"; - -export default { - title: "swap/SelectTokenModal", - component: SelectTokenModal, -} as ComponentMeta; - -const Template: ComponentStory = args => ( -
-
- -
-
-); - -export const Default = Template.bind({}); -Default.args = { - search: action("search"), - onSelectTokenModal: action("onClick"), - keyword: "", - coinList: coinList(), - changeToken: action("changeToken"), -}; - -const wrapper = () => css` - display: flex; - width: 100%; - align-items: center; - justify-content: center; - margin-top: 50px; -`; - -const contentWrap = () => css` - width: 500px; -`; diff --git a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.styles.ts b/packages/web/src/components/swap/select-token-modal/SelectTokenModal.styles.ts deleted file mode 100644 index ae0b2bd45..000000000 --- a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.styles.ts +++ /dev/null @@ -1,209 +0,0 @@ -import mixins from "@styles/mixins"; -import { fonts } from "@constants/font.constant"; -import styled from "@emotion/styled"; -import { Z_INDEX } from "@styles/zIndex"; -import { media } from "@styles/media"; - -export const SearchModalBackground = styled.div` - position: fixed; - top: 0px; - bottom: 0px; - left: 0px; - right: 0px; - width: 100%; - height: 100%; - background: rgba(10, 14, 23, 0.7); - z-index: ${Z_INDEX.modalOverlay}; -`; - -export const SearchModal = styled.div` - ${mixins.flexbox("column", "flex-start", "flex-start")}; - position: absolute; - width: 460px; - padding: 24px 0px 16px 0px; - gap: 24px; - top: calc(40vh - 230px); - left: calc(50vw - 230px); - border-radius: 8px; - box-shadow: 10px 14px 48px 0px rgba(0, 0, 0, 0.12); - border: 1px solid ${({ theme }) => theme.color.border02}; - background-color: ${({ theme }) => theme.color.background06}; - ${media.mobile} { - width: 328px; - top: calc(40vh - 164px); - left: calc(50vw - 164px); - padding: 16px 0px; - } - - .modal-body { - ${mixins.flexbox("column", "flex-start", "flex-start")}; - width: 100%; - padding: 0px 24px; - gap: 24px; - ${media.mobile} { - padding: 0px 12px; - gap: 16px; - } - .modal-header { - ${mixins.flexbox("row", "center", "space-between")}; - width: 100%; - span { - color: ${({ theme }) => theme.color.text02}; - ${fonts.body7} - font-weight: 600; - ${media.mobile} { - ${fonts.body9} - } - } - - .close-wrap { - ${mixins.flexbox("row", "center", "center")}; - cursor: pointer; - width: 24px; - height: 24px; - .close-icon { - width: 24px; - height: 24px; - * { - fill: ${({ theme }) => theme.color.icon01}; - } - } - } - } - .search-wrap { - ${mixins.flexbox("row", "center", "space-between")}; - width: 100%; - padding: 12px 16px; - border-radius: 8px; - ${fonts.body9} - ${media.mobile} { - padding: 8px 12px; - ${fonts.body11} - } - - &:focus-within { - background-color: ${({ theme }) => theme.color.background13}; - border: 1px solid ${({ theme }) => theme.color.border03}; - color: ${({ theme }) => theme.color.text01}; - .search-icon * { - fill: ${({ theme }) => theme.color.icon03}; - } - } - - &:not(:focus-within, .empty-status) { - border: 1px solid ${({ theme }) => theme.color.border11}; - color: ${({ theme }) => theme.color.text01}; - .search-icon * { - fill: ${({ theme }) => theme.color.icon05}; - } - } - - &:not(:focus-within).empty-status { - color: ${({ theme }) => theme.color.text17}; - border: 1px solid ${({ theme }) => theme.color.border02}; - .search-icon * { - fill: ${({ theme }) => theme.color.icon08}; - } - } - } - .coin-select { - display: grid; - width: 100%; - grid-template-rows: auto; - col-gap: 8px; - row-gap: 8px; - grid-template-columns: repeat(4, 1fr); - - ${media.mobile} { - grid-template-columns: repeat(3, 1fr); - } - .coin-button { - ${mixins.flexbox("row", "center", "flex-start")}; - margin: 0 auto; - padding: 6px 12px 6px 6px; - gap: 8px; - border-radius: 36px; - border: 1px solid ${({ theme }) => theme.color.border02}; - background-color: ${({ theme }) => theme.color.background02}; - &:hover { - background-color: ${({ theme }) => theme.color.background09}; - } - cursor: pointer; - span { - color: ${({ theme }) => theme.color.text01}; - ${fonts.body9} - ${media.mobile} { - ${fonts.body11} - } - } - .coin-logo { - width: 24px; - height: 24px; - } - } - } - } - - .list-wrap { - ${mixins.flexbox("column", "flex-start", "flex-start")}; - width: 100%; - gap: 4px; - height: 292px; - ${media.mobile} { - height: 248px; - } - overflow-y: auto; - - .list { - ${mixins.flexbox("row", "center", "space-between")}; - width: 100%; - padding: 16px 24px; - gap: 8px; - ${media.mobile} { - padding: 12px; - } - &:hover { - background-color: ${({ theme }) => theme.color.background09}; - } - cursor: pointer; - .coin-logo { - width: 24px; - height: 24px; - } - .coin-info { - ${mixins.flexbox("row", "center", "flex-start")}; - gap: 8px; - .coin-name { - color: ${({ theme }) => theme.color.text02}; - ${fonts.body8} - ${media.mobile} { - ${fonts.body12} - } - } - .coin-symbol { - color: ${({ theme }) => theme.color.text04}; - ${fonts.body8} - ${media.mobile} { - ${fonts.body12} - } - } - } - .coin-balance { - color: ${({ theme }) => theme.color.text02}; - ${fonts.body7} - ${media.mobile} { - ${fonts.body11} - } - } - } - } -`; - -export const InputStyle = styled.input` - width: 100%; - height: 100%; - margin-right: 16px; - &::placeholder { - color: ${({ theme }) => theme.color.text04}; - } -`; diff --git a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.tsx b/packages/web/src/components/swap/select-token-modal/SelectTokenModal.tsx deleted file mode 100644 index 6c7af3d29..000000000 --- a/packages/web/src/components/swap/select-token-modal/SelectTokenModal.tsx +++ /dev/null @@ -1,101 +0,0 @@ -import React, { useEffect, useRef } from "react"; -import { - SearchModalBackground, - SearchModal, - InputStyle, -} from "./SelectTokenModal.styles"; -import IconSearch from "@components/common/icons/IconSearch"; -import IconClose from "@components/common/icons/IconCancel"; -import { tokenInfo } from "@containers/swap-container/SwapContainer"; - -interface SelectTokenModalProps { - onSelectTokenModal: () => void; - search: (e: React.ChangeEvent) => void; - keyword: string; - coinList: tokenInfo[]; - changeToken: (token: tokenInfo, type: string) => void; -} - -const SelectTokenModal: React.FC = ({ - onSelectTokenModal, - search, - keyword, - coinList, - changeToken, -}) => { - const menuRef = useRef(null); - - useEffect(() => { - const closeMenu = (e: MouseEvent) => { - if (menuRef.current && menuRef.current.contains(e.target as Node)) { - return; - } else { - e.stopPropagation(); - onSelectTokenModal(); - } - }; - window.addEventListener("click", closeMenu, true); - return () => { - window.removeEventListener("click", closeMenu, true); - }; - }, [menuRef, onSelectTokenModal]); - - return ( - - -
-
- Select a token -
- -
-
-
- - -
-
- {coinList.map((data, idx) => ( -
{ - changeToken(data, "from"); - onSelectTokenModal(); - }} - > - logo - {data.symbol} -
- ))} -
-
-
- {coinList.map((data, idx) => ( -
{ - changeToken(data, "from"); - onSelectTokenModal(); - }} - > -
- logo - {data.name} - {data.symbol} -
- {data.balance} -
- ))} -
-
-
- ); -}; - -export default SelectTokenModal; diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.spec.tsx b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.spec.tsx index 795026856..5df45dc3a 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.spec.tsx +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.spec.tsx @@ -6,10 +6,9 @@ import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapTheme describe("SettingMenuModal Component", () => { it("should render", () => { const mockProps = { - onSettingMenu: () => null, - tolerance: "", - changeTolerance: () => null, - resetTolerance: () => null, + slippage: 0, + changeSlippage: () => null, + close: () => null, }; render( diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx index 799fe98bd..901f8c6da 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.stories.tsx @@ -1,6 +1,6 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; -import { css, Theme } from "@emotion/react"; +import { css } from "@emotion/react"; import { action } from "@storybook/addon-actions"; import SettingMenuModal from "./SettingMenuModal"; @@ -19,10 +19,9 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - onSettingMenu: action("onSettingMenu"), - changeTolerance: action("changeTolerance"), - resetTolerance: action("resetTolerance"), - tolerance: "", + slippage: 0, + changeSlippage: action("changeSlippage"), + close: action("close"), }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx index 6a5c82f56..93c20abe7 100644 --- a/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx +++ b/packages/web/src/components/swap/setting-menu-modal/SettingMenuModal.tsx @@ -2,26 +2,27 @@ import Button from "@components/common/button/Button"; import IconClose from "@components/common/icons/IconCancel"; import IconInfo from "@components/common/icons/IconInfo"; import Tooltip from "@components/common/tooltip/Tooltip"; -import React, { useEffect, useRef } from "react"; +import React, { useCallback, useRef } from "react"; import { ModalTooltipWrap, SettingMenuModalWrapper, } from "./SettingMenuModal.styles"; +import useModalCloseEvent from "@hooks/common/use-modal-close-event"; interface SettingMenuModalProps { - onSettingMenu: () => void; - tolerance: string; - changeTolerance: (e: React.ChangeEvent) => void; - resetTolerance: () => void; + slippage: number; + changeSlippage: (value: string) => void; + close: () => void; } const SettingMenuModal: React.FC = ({ - onSettingMenu, - tolerance, - changeTolerance, - resetTolerance, + slippage, + changeSlippage, + close, }) => { const settingMenuRef = useRef(null); + useModalCloseEvent(settingMenuRef, close); + const TooltipFloatingContent = (
@@ -34,30 +35,21 @@ const SettingMenuModal: React.FC = ({ ); - useEffect(() => { - const closeMenu = (e: MouseEvent) => { - if ( - settingMenuRef.current && - settingMenuRef.current.contains(e.target as Node) - ) { - return; - } else { - e.stopPropagation(); - onSettingMenu(); - } - }; - window.addEventListener("click", closeMenu, true); - return () => { - window.removeEventListener("click", closeMenu, true); - }; - }, [settingMenuRef, onSettingMenu]); + const onChangeSlippage = useCallback((event: React.ChangeEvent) => { + const value = event.target.value; + changeSlippage(value); + }, [changeSlippage]); + + const onClickReset = useCallback(() => { + changeSlippage("10"); + }, [changeSlippage]); return (
Settings -
+
@@ -78,14 +70,14 @@ const SettingMenuModal: React.FC = ({ fontType: "p1", textColor: "text20", }} - onClick={resetTolerance} + onClick={onClickReset} />
%
diff --git a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.spec.tsx b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.spec.tsx deleted file mode 100644 index 4e887acae..000000000 --- a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.spec.tsx +++ /dev/null @@ -1,21 +0,0 @@ -import { render } from "@testing-library/react"; -import { Provider as JotaiProvider } from "jotai"; -import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; -import SwapButtonTooltip from "./SwapButtonTooltip"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; - -describe("SwapButtonTooltip Component", () => { - it("SwapButtonTooltip render", () => { - const mockProps = { - swapGasInfo: dummySwapGasInfo, - }; - - render( - - - - - , - ); - }); -}); diff --git a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.stories.tsx b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.stories.tsx index 3873a866a..1f50ac6b5 100644 --- a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.stories.tsx +++ b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.stories.tsx @@ -2,7 +2,45 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapButtonTooltip from "./SwapButtonTooltip"; import { css } from "@emotion/react"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; export default { title: "swap/SwapButtonTooltip", @@ -19,7 +57,7 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - swapGasInfo: dummySwapGasInfo, + swapSummaryInfo, }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts index 68f75a0d5..d397ed3ce 100644 --- a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts +++ b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.styles.ts @@ -5,7 +5,7 @@ import { fonts } from "@constants/font.constant"; export const SwapButtonTooltipWrap = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 300px; - height: 123px + height: 123px; padding: 16px; gap: 8px; ${fonts.body12}; diff --git a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.tsx b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.tsx index 12c24336d..355e67446 100644 --- a/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.tsx +++ b/packages/web/src/components/swap/swap-button-tooltip/SwapButtonTooltip.tsx @@ -1,32 +1,55 @@ -import React from "react"; +import React, { useMemo } from "react"; import { IconWrap, SwapButtonTooltipWrap } from "./SwapButtonTooltip.styles"; -import { SwapGasInfo } from "@containers/swap-container/SwapContainer"; import Tooltip from "@components/common/tooltip/Tooltip"; import IconInfo from "@components/common/icons/IconInfo"; +import { SwapSummaryInfo, swapDirectionToGuaranteedType } from "@models/swap/swap-summary-info"; +import { toNumberFormat } from "@utils/number-utils"; interface WalletBalanceDetailInfoProps { - swapGasInfo: SwapGasInfo; + swapSummaryInfo: SwapSummaryInfo; } const SwapButtonTooltip: React.FC = ({ - swapGasInfo, + swapSummaryInfo, }) => { - const TooltipFloatingContent = ( - -
- Price Impact - {swapGasInfo.gasFee} -
-
- Min. Received - {swapGasInfo.minReceived} -
-
- Gas Fee - {swapGasInfo.gasFee} -
-
- ); + const priceImpactStr = useMemo(() => { + const priceImpact = swapSummaryInfo.priceImpact; + return `${priceImpact}%`; + }, [swapSummaryInfo.priceImpact]); + + const guaranteedTypeStr = useMemo(() => { + const swapDirection = swapSummaryInfo.swapDirection; + return swapDirectionToGuaranteedType(swapDirection); + }, [swapSummaryInfo.swapDirection]); + + const guaranteedStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.guaranteedAmount; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.guaranteedAmount]); + + const gasFeeStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.gasFee; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.gasFee]); + + const TooltipFloatingContent = useMemo(() => { + return ( + +
+ Price Impact + {priceImpactStr} +
+
+ {guaranteedTypeStr} + {guaranteedStr} +
+
+ Gas Fee + {gasFeeStr} +
+
+ ); + }, [gasFeeStr, guaranteedStr, guaranteedTypeStr, priceImpactStr]); return ( diff --git a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.spec.tsx b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.spec.tsx index b489b5ed3..169e189af 100644 --- a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.spec.tsx +++ b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.spec.tsx @@ -2,36 +2,11 @@ import { render } from "@testing-library/react"; import { Provider as JotaiProvider } from "jotai"; import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; import SwapCardAutoRouter from "./SwapCardAutoRouter"; -import { - dummyAutoRouterInfo, -} from "@containers/swap-container/SwapContainer"; describe("SwapCard Component", () => { it("SwapCard render", () => { const mockProps = { - autoRouterInfo: dummyAutoRouterInfo, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapRouteInfos: [] }; render( diff --git a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.stories.tsx b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.stories.tsx index 8259a1198..8c35893fd 100644 --- a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.stories.tsx +++ b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.stories.tsx @@ -1,13 +1,44 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapCardAutoRouter from "./SwapCardAutoRouter"; -import { css, Theme } from "@emotion/react"; -import { - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { action } from "@storybook/addon-actions"; -import { DEVICE_TYPE } from "@styles/media"; +import { css } from "@emotion/react"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import PoolData from "@repositories/pool/mock/pools.json"; + +const pools = PoolData.pools; + +const swapRouteInfos: SwapRouteInfo[] = [{ + from: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + to: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1, + pools, + version: "V1", + weight: 100, +}]; export default { title: "swap/SwapCardAutoRouter", @@ -24,32 +55,10 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - autoRouterInfo: dummyAutoRouterInfo, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapRouteInfos, }; -const wrapper = (theme: Theme) => css` +const wrapper = () => css` display: flex; width: 100%; align-items: center; @@ -57,6 +66,6 @@ const wrapper = (theme: Theme) => css` margin-top: 50px; `; -const contentWrap = (theme: Theme) => css` +const contentWrap = () => css` width: 500px; `; diff --git a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.tsx b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.tsx index b832b610b..40dd7ef17 100644 --- a/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.tsx +++ b/packages/web/src/components/swap/swap-card-auto-router/SwapCardAutoRouter.tsx @@ -1,110 +1,64 @@ -import React from "react"; +import React, { useMemo } from "react"; import { AutoRouterWrapper, DotLine } from "./SwapCardAutoRouter.styles"; -import { TokenInfo } from "../swap-card/SwapCard"; -import { AutoRouterInfo } from "@containers/swap-container/SwapContainer"; -import IconLogoPrimary from "@components/common/icons/IconLogoPrimary"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import DoubleLogo from "@components/common/double-logo/DoubleLogo"; interface ContentProps { - to: TokenInfo; - from: TokenInfo; - autoRouterInfo: AutoRouterInfo; + swapRouteInfos: SwapRouteInfo[]; } const SwapCardAutoRouter: React.FC = ({ - to, - from, - autoRouterInfo, + swapRouteInfos, }) => { + const bestGasFee = useMemo(() => { + const totalGasFee = swapRouteInfos.reduce((prev, current) => prev + current.gasFeeUSD, 0); + return `$${totalGasFee}`; + }, [swapRouteInfos]); + return ( -
- token logo -
-
V1
- {autoRouterInfo.v1fee[0]} -
- -
-
-
- pair-logo -
-
- pair-logo -
-
-

{autoRouterInfo.v1fee[1]}

-
- -
-
-
- pair-logo -
-
- -
-
-

{autoRouterInfo.v1fee[2]}

-
- -
- -
-
-
- token logo -
-
V1
- {autoRouterInfo.v2fee[0]} -
- -
-
-
- pair-logo -
-
- -
-
-

{autoRouterInfo.v2fee[1]}

-
- -
- -
-
-
- token logo -
-
V1
- {autoRouterInfo.v3fee[0]} -
- -
-
-
- pair-logo -
-
- -
-
-

{autoRouterInfo.v3fee[1]}

-
- -
- -
-
+ {swapRouteInfos.map((swapRouteInfo, index) => ( + + ))}

- Best price route costs ~$0.58 in gas. This route optimizes your total - output by considering split routes, multiple hops, and the gas cost of - each step. + {`Best price route costs ~${bestGasFee} in gas. This route optimizes your total output by considering split routes, multiple hops, and the gas cost of each step.`}

); }; +interface SwapCardAutoRouterItemProps { + swapRouteInfo: SwapRouteInfo; +} + +const SwapCardAutoRouterItem: React.FC = ({ + swapRouteInfo, +}) => { + const weightStr = useMemo(() => { + return `${swapRouteInfo.weight}%`; + }, [swapRouteInfo.weight]); + + return ( +
+ token logo +
+
{swapRouteInfo.version}
+ {weightStr} +
+ + {swapRouteInfo.pools.map((pool, index) => ( + <> +
+ +

{pool.fee}

+
+ + + ))} + token logo +
+ ); +}; + + export default SwapCardAutoRouter; diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.spec.tsx b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.spec.tsx index f2961d10d..d00e5e6f8 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.spec.tsx +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.spec.tsx @@ -2,44 +2,51 @@ import { render } from "@testing-library/react"; import { Provider as JotaiProvider } from "jotai"; import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; import SwapCardContentDetail from "./SwapCardContentDetail"; -import { - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { DEVICE_TYPE } from "@styles/media"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; describe("SwapCardContentDetail Component", () => { it("SwapCardContentDetail render", () => { const mockProps = { - autoRouter: false, - showAutoRouter: () => null, - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: () => null, - autoRouterInfo: dummyAutoRouterInfo, - breakpoint: DEVICE_TYPE.WEB, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapSummaryInfo, + swapRouteInfos: [], }; render( diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.stories.tsx b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.stories.tsx index 34d516fcb..ebe04b1c7 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.stories.tsx +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.stories.tsx @@ -2,12 +2,82 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapCardContentDetail from "./SwapCardContentDetail"; import { css } from "@emotion/react"; -import { - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { action } from "@storybook/addon-actions"; -import { DEVICE_TYPE } from "@styles/media"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import PoolData from "@repositories/pool/mock/pools.json"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; + +const pools = PoolData.pools; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; + +const swapRouteInfos: SwapRouteInfo[] = [{ + from: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + to: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1, + pools, + version: "V1", + weight: 100, +}]; export default { title: "swap/SwapCardContentDetail", @@ -24,35 +94,8 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - autoRouter: true, - showAutoRouter: action("onClick"), - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: action("onClick"), - autoRouterInfo: dummyAutoRouterInfo, - breakpoint: DEVICE_TYPE.WEB, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapSummaryInfo, + swapRouteInfos, }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts index f25bd7faf..70219e0b3 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.styles.ts @@ -3,15 +3,15 @@ import styled from "@emotion/styled"; import { media } from "@styles/media"; import mixins from "@styles/mixins"; -interface exchangeProps { - swapInfo: boolean; +interface WrapperProps { + opened: boolean; } -export const DetailWrapper = styled.div` +export const DetailWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - border-radius: ${({ swapInfo }) => { - return swapInfo ? "8px 8px 0px 0px" : "8px"; + border-radius: ${({ opened }) => { + return opened ? "8px 8px 0px 0px" : "8px"; }}; background: ${({ theme }) => theme.color.background01}; border: 1px solid ${({ theme }) => theme.color.border02}; @@ -68,11 +68,11 @@ export const DetailWrapper = styled.div` } `; -export const FeelWrapper = styled.div` +export const FeelWrapper = styled.div` ${mixins.flexbox("column", "flex-start", "flex-start")}; width: 100%; - border-radius: ${({ swapInfo }) => { - return swapInfo ? "0px 0px 8px 8px" : "8px"; + border-radius: ${({ opened }) => { + return opened ? "0px 0px 8px 8px" : "8px"; }}; background: ${({ theme }) => theme.color.background01}; border-left: 1px solid ${({ theme }) => theme.color.border02}; diff --git a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx index 50af30f1d..9f0aa130b 100644 --- a/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx +++ b/packages/web/src/components/swap/swap-card-content-detail/SwapCardContentDetail.tsx @@ -1,88 +1,97 @@ -import React from "react"; +import React, { useCallback, useMemo, useState } from "react"; import { DetailWrapper, FeelWrapper } from "./SwapCardContentDetail.styles"; -import { TokenInfo } from "../swap-card/SwapCard"; import IconNote from "@components/common/icons/IconNote"; import IconStrokeArrowDown from "@components/common/icons/IconStrokeArrowDown"; import IconStrokeArrowUp from "@components/common/icons/IconStrokeArrowUp"; import SwapCardFeeInfo from "../swap-card-fee-info/SwapCardFeeInfo"; import SwapCardAutoRouter from "../swap-card-auto-router/SwapCardAutoRouter"; -import { - AutoRouterInfo, - SwapGasInfo, -} from "@containers/swap-container/SwapContainer"; import SwapButtonTooltip from "../swap-button-tooltip/SwapButtonTooltip"; import { DEVICE_TYPE } from "@styles/media"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import { numberToFormat } from "@utils/string-utils"; +import { useWindowSize } from "@hooks/common/use-window-size"; interface ContentProps { - to: TokenInfo; - from: TokenInfo; - swapInfo: boolean; - showSwapInfo: () => void; - autoRouter: boolean; - showAutoRouter: () => void; - swapGasInfo: SwapGasInfo; - autoRouterInfo: AutoRouterInfo; - breakpoint: DEVICE_TYPE; + swapSummaryInfo: SwapSummaryInfo; + swapRouteInfos: SwapRouteInfo[]; } const SwapCardContentDetail: React.FC = ({ - to, - from, - swapInfo, - showSwapInfo, - autoRouter, - showAutoRouter, - swapGasInfo, - autoRouterInfo, - breakpoint, + swapSummaryInfo, + swapRouteInfos, }) => { + const { breakpoint } = useWindowSize(); + const [openedDetailInfo, setOpenedDetailInfo] = useState(false); + const [openedRouteInfo, setOpenedRouteInfo] = useState(false); + + const swapRateDescription = useMemo(() => { + const { tokenA, tokenB, swapRate } = swapSummaryInfo; + return `1 ${tokenA.symbol} = ${numberToFormat(swapRate)} ${tokenB.symbol}`; + }, [swapSummaryInfo]); + + const swapRateUSD = useMemo(() => { + const swapRateUSD = swapSummaryInfo.swapRateUSD; + return numberToFormat(swapRateUSD); + }, [swapSummaryInfo.swapRateUSD]); + + const gasFeeUSDStr = useMemo(() => { + const gasFeeUSD = swapSummaryInfo.gasFeeUSD; + return `$${gasFeeUSD}`; + }, [swapSummaryInfo.gasFeeUSD]); + + const toggleDetailInfo = useCallback(() => { + setOpenedDetailInfo(!openedDetailInfo); + }, [openedDetailInfo]); + + const toggleRouteInfo = useCallback(() => { + setOpenedRouteInfo(!openedRouteInfo); + }, [openedRouteInfo]); + return ( <> - +
- - - {from.amount} {from.symbol} = {from.gnosExchangePrice} GNOS - + + {swapRateDescription} {breakpoint !== DEVICE_TYPE.MOBILE && ( - {from.usdExchangePrice} + {`($${swapRateUSD})`} )}
- {swapGasInfo.usdExchangeGasFee} - {swapInfo ? ( + {gasFeeUSDStr} + {openedDetailInfo ? ( ) : ( )}
- {swapInfo && ( - + + {openedDetailInfo && ( +
- {swapInfo && ( + {openedDetailInfo && ( )} - {autoRouter && ( + {openedRouteInfo && ( )}
diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.spec.tsx b/packages/web/src/components/swap/swap-card-content/SwapCardContent.spec.tsx index 1304f9556..c8fea37ca 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.spec.tsx +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.spec.tsx @@ -2,52 +2,52 @@ import { render } from "@testing-library/react"; import { Provider as JotaiProvider } from "jotai"; import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; import SwapCardContent from "./SwapCardContent"; -import { - coinList, - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { DEVICE_TYPE } from "@styles/media"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; + +const swapTokenInfo: SwapTokenInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenAAmount: "", + tokenABalance: "", + tokenAUSD: 0, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenBAmount: "", + tokenBBalance: "", + tokenBUSD: 0, + direction: "EXACT_IN", + slippage: 10 +}; describe("SwapCardContent Component", () => { it("SwapCardContent render", () => { const mockProps = { - autoRouter: false, - showAutoRouter: () => null, - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: () => null, - autoRouterInfo: dummyAutoRouterInfo, - tokenModal: true, - onSelectTokenModal: () => null, - search: () => null, - keyword: "", - coinList: coinList(), - changeToken: () => null, - selectToken: () => null, - breakpoint: DEVICE_TYPE.WEB, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapTokenInfo, + swapSummaryInfo: null, + swapRouteInfos: [], + changeTokenA: () => null, + changeTokenAAmount: () => null, + changeTokenB: () => null, + changeTokenBAmount: () => null, + changeSlippage: () => null, }; render( diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx b/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx index 73c5994b8..17f376fc7 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.stories.tsx @@ -1,14 +1,42 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapCardContent from "./SwapCardContent"; -import { css, Theme } from "@emotion/react"; -import { - coinList, - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; +import { css } from "@emotion/react"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; import { action } from "@storybook/addon-actions"; -import { DEVICE_TYPE } from "@styles/media"; + +const swapTokenInfo: SwapTokenInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenAAmount: "", + tokenABalance: "", + tokenAUSD: 0, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenBAmount: "", + tokenBBalance: "", + tokenBUSD: 0, + direction: "EXACT_IN", + slippage: 10 +}; export default { title: "swap/SwapCardContent", @@ -25,45 +53,16 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - autoRouter: true, - showAutoRouter: action("onClick"), - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: action("onClick"), - autoRouterInfo: dummyAutoRouterInfo, - search: action("search"), - tokenModal: true, - onSelectTokenModal: action("onClick"), - keyword: "", - coinList: coinList(), - changeToken: action("changeToken"), - selectToken: action("selectToken"), - breakpoint: DEVICE_TYPE.WEB, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapTokenInfo, + swapSummaryInfo: null, + swapRouteInfos: [], + changeTokenA: action("changeTokenA"), + changeTokenAAmount: action("changeTokenAAmount"), + changeTokenB: action("changeTokenB"), + changeTokenBAmount: action("changeTokenBAmount"), }; -const wrapper = (theme: Theme) => css` +const wrapper = () => css` display: flex; width: 100%; align-items: center; @@ -71,6 +70,6 @@ const wrapper = (theme: Theme) => css` margin-top: 50px; `; -const contentWrap = (theme: Theme) => css` +const contentWrap = () => css` width: 500px; `; diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts b/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts index eeb813d69..1f83ba095 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.styles.ts @@ -40,6 +40,11 @@ export const ContentWrapper = styled.div` color: ${({ theme }) => theme.color.text01}; } + .token-selector { + display: block; + height: 32px; + } + .amount-info { ${mixins.flexbox("row", "center", "space-between")}; width: 100%; diff --git a/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx b/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx index c73284e47..e4c9dd00e 100644 --- a/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx +++ b/packages/web/src/components/swap/swap-card-content/SwapCardContent.tsx @@ -1,162 +1,106 @@ -import React, { useCallback, useState } from "react"; -import { ContentWrapper, SelectPairButton } from "./SwapCardContent.styles"; -import { TokenInfo } from "../swap-card/SwapCard"; +import React, { useCallback } from "react"; +import { ContentWrapper } from "./SwapCardContent.styles"; import IconSwapArrowDown from "@components/common/icons/IconSwapArrowDown"; import SwapCardContentDetail from "../swap-card-content-detail/SwapCardContentDetail"; -import { - AutoRouterInfo, - tokenInfo, - SwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import SelectTokenModal from "../select-token-modal/SelectTokenModal"; -import { DEVICE_TYPE } from "@styles/media"; -import IconStrokeArrowDown from "@components/common/icons/IconStrokeArrowDown"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import { TokenModel } from "@models/token/token-model"; +import { isAmount } from "@common/utils/data-check-util"; +import SelectPairButton from "@components/common/select-pair-button/SelectPairButton"; interface ContentProps { - to: TokenInfo; - from: TokenInfo; - swapInfo: boolean; - showSwapInfo: () => void; - autoRouter: boolean; - showAutoRouter: () => void; - swapGasInfo: SwapGasInfo; - autoRouterInfo: AutoRouterInfo; - tokenModal: boolean; - onSelectTokenModal: () => void; - search: (e: React.ChangeEvent) => void; - keyword: string; - coinList: tokenInfo[]; - changeToken: (token: tokenInfo, type: string) => void; - selectToken: (e: string) => void; - breakpoint: DEVICE_TYPE; -} - -function isAmount(str: string) { - const regex = /^\d+(\.\d*)?$/; - return regex.test(str); + swapTokenInfo: SwapTokenInfo; + swapSummaryInfo: SwapSummaryInfo | null; + swapRouteInfos: SwapRouteInfo[]; + changeTokenA: (token: TokenModel) => void; + changeTokenAAmount: (value: string) => void; + changeTokenB: (token: TokenModel) => void; + changeTokenBAmount: (value: string) => void; + switchSwapDirection: () => void; } const SwapCardContent: React.FC = ({ - to, - from, - swapInfo, - showSwapInfo, - autoRouter, - showAutoRouter, - swapGasInfo, - autoRouterInfo, - tokenModal, - onSelectTokenModal, - search, - keyword, - coinList, - changeToken, - selectToken, - breakpoint, + swapTokenInfo, + swapSummaryInfo, + swapRouteInfos, + changeTokenA, + changeTokenAAmount, + changeTokenB, + changeTokenBAmount, + switchSwapDirection, }) => { - const [fromAmount, setFromAmount] = useState(from.amount); - const [toAmount, setToAmount] = useState(to.amount); - const onChangeFromAmount = useCallback( + const tokenA = swapTokenInfo.tokenA; + const tokenB = swapTokenInfo.tokenB; + + const onChangeTokenAAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; if (value !== "" && !isAmount(value)) return; - setFromAmount(value); + changeTokenAAmount(value); }, - [], + [changeTokenAAmount], ); - const onChangeToAmount = useCallback( + const onChangeTokenBAmount = useCallback( (e: React.ChangeEvent) => { const value = e.target.value; if (value !== "" && !isAmount(value)) return; - setToAmount(value); + changeTokenBAmount(value); }, - [], + [changeTokenBAmount], ); return ( - <> - {tokenModal && ( - - )} - -
-
- - { - selectToken("from"); - onSelectTokenModal(); - }} - > - token logo - {from.symbol} - - -
-
- {from.price} - Balance : {from.balance} -
-
-
- -
+ +
+
+ +
+
-
-
- - { - selectToken("to"); - onSelectTokenModal(); - }} - > - token logo - {to.symbol} - - +
+ {`$${swapTokenInfo.tokenAUSD}`} + Balance : {swapTokenInfo.tokenABalance} +
+
+
+
-
- {to.price} - Balance : {to.balance} +
+
+
+
+ +
+
+
+ {`$${swapTokenInfo.tokenBUSD}`} + Balance : {swapTokenInfo.tokenBBalance} +
+
+ + {swapSummaryInfo && ( - - + )} + ); }; diff --git a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.spec.tsx b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.spec.tsx index 6cc16ead2..eacf76ed3 100644 --- a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.spec.tsx +++ b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.spec.tsx @@ -2,14 +2,52 @@ import { render } from "@testing-library/react"; import { Provider as JotaiProvider } from "jotai"; import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; import SwapCardFeeInfo from "./SwapCardFeeInfo"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; describe("SwapCardFeeInfo Component", () => { it("SwapCardFeeInfo render", () => { const mockProps = { - autoRouter: false, - showAutoRouter: () => null, - swapGasInfo: dummySwapGasInfo, + openedRouteInfo: false, + toggleRouteInfo: () => null, + swapSummaryInfo, }; render( diff --git a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.stories.tsx b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.stories.tsx index 47e6ee7b6..b8c6e63c1 100644 --- a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.stories.tsx +++ b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.stories.tsx @@ -1,9 +1,47 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapCardFeeInfo from "./SwapCardFeeInfo"; -import { css, Theme } from "@emotion/react"; -import { dummySwapGasInfo } from "@containers/swap-container/SwapContainer"; +import { css } from "@emotion/react"; import { action } from "@storybook/addon-actions"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; export default { title: "swap/SwapCardFeeInfo", @@ -20,9 +58,9 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - autoRouter: true, - showAutoRouter: action("onClick"), - swapGasInfo: dummySwapGasInfo, + openedRouteInfo: true, + toggleRouteInfo: action("toggleRouteInfo"), + swapSummaryInfo }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.tsx b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.tsx index 429211c37..c976e6032 100644 --- a/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.tsx +++ b/packages/web/src/components/swap/swap-card-fee-info/SwapCardFeeInfo.tsx @@ -1,37 +1,64 @@ -import React from "react"; +import React, { useMemo } from "react"; import { FeeWrapper, SwapDivider } from "./SwapCardFeeInfo.styles"; import IconStrokeArrowDown from "@components/common/icons/IconStrokeArrowDown"; import IconStrokeArrowUp from "@components/common/icons/IconStrokeArrowUp"; import IconRouter from "@components/common/icons/IconRouter"; -import { SwapGasInfo } from "@containers/swap-container/SwapContainer"; +import { SwapSummaryInfo, swapDirectionToGuaranteedType } from "@models/swap/swap-summary-info"; +import { toNumberFormat } from "@utils/number-utils"; interface ContentProps { - autoRouter: boolean; - showAutoRouter: () => void; - swapGasInfo: SwapGasInfo; + openedRouteInfo: boolean; + toggleRouteInfo: () => void; + swapSummaryInfo: SwapSummaryInfo; } const SwapCardFeeInfo: React.FC = ({ - autoRouter, - showAutoRouter, - swapGasInfo, + openedRouteInfo, + toggleRouteInfo, + swapSummaryInfo, }) => { + + const priceImpactStr = useMemo(() => { + const priceImpact = swapSummaryInfo.priceImpact; + return `${priceImpact}%`; + }, [swapSummaryInfo.priceImpact]); + + const guaranteedTypeStr = useMemo(() => { + const swapDirection = swapSummaryInfo.swapDirection; + return swapDirectionToGuaranteedType(swapDirection); + }, [swapSummaryInfo.swapDirection]); + + const guaranteedStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.guaranteedAmount; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.guaranteedAmount]); + + const gasFeeStr = useMemo(() => { + const { amount, currency } = swapSummaryInfo.gasFee; + return `${toNumberFormat(amount)} ${currency}`; + }, [swapSummaryInfo.gasFee]); + + const gasFeeUSDStr = useMemo(() => { + const gasFeeUSD = swapSummaryInfo.gasFeeUSD; + return `$${toNumberFormat(gasFeeUSD)}`; + }, [swapSummaryInfo.gasFeeUSD]); + return (
Price Impact - {swapGasInfo.priceImpact} + {priceImpactStr}
- Min. Received - {swapGasInfo.minReceived} + {guaranteedTypeStr} + {guaranteedStr}
Gas Fee - {swapGasInfo.gasFee} GNOT{" "} - ({swapGasInfo.usdExchangeGasFee}) + {gasFeeStr} + {`(${gasFeeUSDStr})`}
@@ -40,14 +67,10 @@ const SwapCardFeeInfo: React.FC = ({

Auto Router

- {autoRouter ? ( - - ) : ( - - )} + + {openedRouteInfo ? + : + }
); diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.spec.tsx b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.spec.tsx index 4eb0e198f..4bc7f28ea 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.spec.tsx +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.spec.tsx @@ -6,13 +6,10 @@ import SwapCardHeader from "./SwapCardHeader"; describe("SwapCardHeader Component", () => { it("SwapCardHeader render", () => { const mockProps = { - settingMenuToggle: true, - onSettingMenu: () => null, - tolerance: "", - changeTolerance: () => null, - resetTolerance: () => null, - handleCopyClipBoard: () => null, - copied: true, + copied: false, + copyURL: () => null, + slippage: 0, + changeSlippage: () => null, }; render( diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx index c98949aaf..819765365 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.stories.tsx @@ -19,13 +19,10 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - settingMenuToggle: true, - onSettingMenu: action("onSettingMenu"), - tolerance: "", - changeTolerance: action("changeTolerance"), - resetTolerance: action("resetTolerance"), - handleCopyClipBoard: action("handleCopyClipBoard"), - copied: true, + copied: false, + copyURL: action("copyURL"), + slippage: 0, + changeSlippage: action("changeSlippage"), }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx index 9ff2e6fd9..9b89f5020 100644 --- a/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx +++ b/packages/web/src/components/swap/swap-card-header/SwapCardHeader.tsx @@ -1,7 +1,7 @@ import IconLink from "@components/common/icons/IconLink"; import IconPolygon from "@components/common/icons/IconPolygon"; import IconSettings from "@components/common/icons/IconSettings"; -import React from "react"; +import React, { useCallback, useState } from "react"; import SettingMenuModal from "../setting-menu-modal/SettingMenuModal"; import { CopyTooltip, @@ -9,30 +9,34 @@ import { SwapCardHeaderWrapper, } from "./SwapCardHeader.styles"; interface SwapCardHeaderProps { - settingMenuToggle: boolean; - onSettingMenu: () => void; - tolerance: string; - changeTolerance: (e: React.ChangeEvent) => void; - resetTolerance: () => void; - handleCopyClipBoard: (text: string) => void; copied: boolean; + copyURL: () => void; + slippage: number; + changeSlippage: (value: string) => void; } const SwapCardHeader: React.FC = ({ - settingMenuToggle, - onSettingMenu, - tolerance, - changeTolerance, - resetTolerance, - handleCopyClipBoard, copied, + copyURL, + slippage, + changeSlippage, }) => { + const [openedSetting, setOpenedSetting] = useState(false); + + const openSetting = useCallback(() => { + setOpenedSetting(true); + }, []); + + const closeSetting = useCallback(() => { + setOpenedSetting(false); + }, []); + return (

Swap

handleCopyClipBoard("Copy Completed.")} + onClick={copyURL} > <> @@ -47,15 +51,14 @@ const SwapCardHeader: React.FC = ({
-
+
- {settingMenuToggle && ( + {openedSetting && ( )} diff --git a/packages/web/src/components/swap/swap-card/SwapCard.spec.tsx b/packages/web/src/components/swap/swap-card/SwapCard.spec.tsx deleted file mode 100644 index 15186c1f4..000000000 --- a/packages/web/src/components/swap/swap-card/SwapCard.spec.tsx +++ /dev/null @@ -1,76 +0,0 @@ -import { render } from "@testing-library/react"; -import { Provider as JotaiProvider } from "jotai"; -import GnoswapThemeProvider from "@providers/gnoswap-theme-provider/GnoswapThemeProvider"; -import SwapCard from "./SwapCard"; -import { - coinList, - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { DEVICE_TYPE } from "@styles/media"; - -describe("SwapCard Component", () => { - it("SwapCard render", () => { - const mockProps = { - search: () => null, - keyword: "", - gnosAmount: "1", - isConnected: true, - autoRouter: false, - showAutoRouter: () => null, - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: () => null, - autoRouterInfo: dummyAutoRouterInfo, - settingMenuToggle: true, - onSettingMenu: () => null, - tolerance: "", - changeTolerance: () => null, - tokenModal: true, - onSelectTokenModal: () => null, - swapOpen: true, - onConfirmModal: () => null, - coinList: coinList(), - changeToken: () => null, - selectToken: () => null, - submitSwap: () => null, - resetTolerance: () => null, - breakpoint: DEVICE_TYPE.WEB, - handleCopyClipBoard: () => null, - submit: false, - isFetching: true, - copied: true, - swapResult: { success: true, transaction: "https//:naver.com" }, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, - }; - - render( - - - - - , - ); - }); -}); diff --git a/packages/web/src/components/swap/swap-card/SwapCard.stories.tsx b/packages/web/src/components/swap/swap-card/SwapCard.stories.tsx index b33274a92..fe955b2b1 100644 --- a/packages/web/src/components/swap/swap-card/SwapCard.stories.tsx +++ b/packages/web/src/components/swap/swap-card/SwapCard.stories.tsx @@ -2,13 +2,82 @@ import React from "react"; import { ComponentStory, ComponentMeta } from "@storybook/react"; import SwapCard from "./SwapCard"; import { css } from "@emotion/react"; -import { - coinList, - dummyAutoRouterInfo, - dummySwapGasInfo, -} from "@containers/swap-container/SwapContainer"; -import { action } from "@storybook/addon-actions"; -import { DEVICE_TYPE } from "@styles/media"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; +import PoolData from "@repositories/pool/mock/pools.json"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; + +const pools = PoolData.pools; + +const swapSummaryInfo: SwapSummaryInfo = { + tokenA: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + tokenB: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + swapDirection: "EXACT_IN", + swapRate: 1.14, + swapRateUSD: 1.14, + priceImpact: 0.3, + guaranteedAmount: { + amount: 45124, + currency: "GNOT" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1 +}; + +const swapRouteInfos: SwapRouteInfo[] = [{ + from: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + to: { + chainId: "test3", + address: "0x111111111117dC0aa78b770fA6A738034120C302", + path: "gno.land/r/demo/1inch", + name: "1inch", + symbol: "1INCH", + decimals: 6, + logoURI: "https://assets.coingecko.com/coins/images/13469/thumb/1inch-token.png?1608803028", + priceId: "1inch", + createdAt: "1999-01-01T00:00:01Z" + }, + gasFee: { + amount: 0.000001, + currency: "GNOT" + }, + gasFeeUSD: 0.1, + pools, + version: "V1", + weight: 100, +}]; export default { title: "swap/SwapCard", @@ -25,57 +94,8 @@ const Template: ComponentStory = args => ( export const Default = Template.bind({}); Default.args = { - search: action("search"), - keyword: "", - gnosAmount: "1500", - isConnected: true, - autoRouter: true, - showAutoRouter: action("onClick"), - swapGasInfo: dummySwapGasInfo, - swapInfo: true, - showSwapInfo: action("onClick"), - autoRouterInfo: dummyAutoRouterInfo, - settingMenuToggle: true, - onSettingMenu: action("onSettingMenu"), - tolerance: "", - changeTolerance: action("changeTolerance"), - tokenModal: true, - onSelectTokenModal: action("onClick"), - swapOpen: true, - onConfirmModal: action("onClick"), - submitSwap: action("submitSwap"), - coinList: coinList(), - changeToken: action("changeToken"), - selectToken: action("selectToken"), - resetTolerance: action("resetTolerance"), - handleCopyClipBoard: action("handleCopyClipBoard"), - breakpoint: DEVICE_TYPE.WEB, - submit: false, - copied: true, - isFetching: true, - swapResult: { success: true, transaction: "https//:naver.com" }, - from: { - token: "USDCoin", - symbol: "USDC", - amount: "121", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48/logo.png", - }, - to: { - token: "HEX", - symbol: "HEX", - amount: "5000", - price: "$0.00", - gnosExchangePrice: "1250", - usdExchangePrice: "($1541.55)", - balance: "0", - logoURI: - "https://raw.githubusercontent.com/Uniswap/assets/master/blockchains/ethereum/assets/0x2b591e99afE9f32eAA6214f7B7629768c40Eeb39/logo.png", - }, + swapSummaryInfo, + swapRouteInfos, }; const wrapper = () => css` diff --git a/packages/web/src/components/swap/swap-card/SwapCard.tsx b/packages/web/src/components/swap/swap-card/SwapCard.tsx index b60736a8a..015d65dcd 100644 --- a/packages/web/src/components/swap/swap-card/SwapCard.tsx +++ b/packages/web/src/components/swap/swap-card/SwapCard.tsx @@ -3,178 +3,160 @@ import Button, { ButtonHierarchy } from "@components/common/button/Button"; import { SwapCardWrapper } from "./SwapCard.styles"; import SwapCardHeader from "../swap-card-header/SwapCardHeader"; import SwapCardContent from "../swap-card-content/SwapCardContent"; -import { - AutoRouterInfo, - tokenInfo, - SwapData, - SwapGasInfo, -} from "@containers/swap-container/SwapContainer"; import ConfirmSwapModal from "../confirm-swap-modal/ConfirmSwapModal"; -import { DEVICE_TYPE } from "@styles/media"; - -export interface TokenInfo { - token: string; - symbol: string; - amount: string; - price: string; - gnosExchangePrice: string; - usdExchangePrice: string; - balance: string; - logoURI: string; -} +import { TokenModel } from "@models/token/token-model"; +import { SwapTokenInfo } from "@models/swap/swap-token-info"; +import { SwapSummaryInfo } from "@models/swap/swap-summary-info"; +import { SwapRouteInfo } from "@models/swap/swap-route-info"; +import { SwapResultInfo } from "@models/swap/swap-result-info"; +import { FontsKey } from "@constants/font.constant"; interface SwapCardProps { - search: (e: React.ChangeEvent) => void; - keyword: string; - isConnected: boolean; - from: TokenInfo; - to: TokenInfo; - gnosAmount: string; - swapInfo: boolean; - showSwapInfo: () => void; - autoRouter: boolean; - showAutoRouter: () => void; - swapGasInfo: SwapGasInfo; - autoRouterInfo: AutoRouterInfo; - settingMenuToggle: boolean; - onSettingMenu: () => void; - tolerance: string; - changeTolerance: (e: React.ChangeEvent) => void; - tokenModal: boolean; - onSelectTokenModal: () => void; - swapOpen: boolean; - onConfirmModal: () => void; - coinList: tokenInfo[]; - changeToken: (token: tokenInfo, type: string) => void; - selectToken: (e: string) => void; - submitSwap: (event: React.MouseEvent) => void; - breakpoint: DEVICE_TYPE; - submit: boolean; - isFetching: boolean; - swapResult: SwapData | null; - resetTolerance: () => void; - handleCopyClipBoard: (text: string) => void; + connectedWallet: boolean; copied: boolean; + swapTokenInfo: SwapTokenInfo; + swapSummaryInfo: SwapSummaryInfo | null; + swapRouteInfos: SwapRouteInfo[]; + isAvailSwap: boolean; + swapError?: string | null; + submitted: boolean; + swapResult: SwapResultInfo | null; + openedConfirmModal: boolean; + + changeTokenA: (token: TokenModel) => void; + changeTokenAAmount: (value: string) => void; + changeTokenB: (token: TokenModel) => void; + changeTokenBAmount: (value: string) => void; + changeSlippage: (value: string) => void; + + switchSwapDirection: () => void; + openConfirmModal: () => void; + openConnectWallet: () => void; + closeModal: () => void; + copyURL: () => void; + swap: () => void; } const SwapCard: React.FC = ({ - search, - keyword, - from, - to, - gnosAmount, - isConnected, - swapInfo, - showSwapInfo, - autoRouter, - showAutoRouter, - swapGasInfo, - autoRouterInfo, - settingMenuToggle, - onSettingMenu, - tolerance, - changeTolerance, - tokenModal, - onSelectTokenModal, - swapOpen, - onConfirmModal, - coinList, - changeToken, - selectToken, - submitSwap, - breakpoint, - submit, - isFetching, - swapResult, - resetTolerance, - handleCopyClipBoard, + connectedWallet, copied, + swapTokenInfo, + swapSummaryInfo, + swapRouteInfos, + isAvailSwap, + swapError, + submitted, + swapResult, + openedConfirmModal, + changeTokenA, + changeTokenAAmount, + changeTokenB, + changeTokenBAmount, + changeSlippage, + switchSwapDirection, + openConfirmModal, + openConnectWallet, + closeModal, + copyURL, + swap, }) => { + return ( <>
- {isConnected ? ( - Number(gnosAmount) - Number(from.gnosExchangePrice) > 0 ? ( -
- {swapOpen && ( + + {openedConfirmModal && swapSummaryInfo && ( )} ); }; +interface SwapButtonProps { + connectedWallet: boolean; + isAvailSwap: boolean; + swapError?: string | null; + openConfirmModal: () => void; + openConnectWallet: () => void; +} + +const SwapButton: React.FC = ({ + connectedWallet, + isAvailSwap, + swapError, + openConfirmModal, + openConnectWallet, +}) => { + + const defaultStyle = { + fullWidth: true, + fontType: "body7" as FontsKey, + hierarchy: ButtonHierarchy.Primary, + height: 56, + }; + + if (!connectedWallet) { + return ( +