From 2dfa281cec723bbb2290f19857ce11bb1762c0a4 Mon Sep 17 00:00:00 2001 From: Nguyen Van Viet Date: Mon, 6 Jan 2025 15:03:20 +0700 Subject: [PATCH] Feat/zap-earn (#2557) * feat: earning landing page * feat: integrate api for zap earn landing * implement pool explorer * integrate liquidity widget * add no data notify * add missing 'chainId' from interfae * fake a pool to open liquidity widget * add navigate to earn landing page & open liquidity widget * add feature favorite * integrate new zap in widget ui * add wallet connect & change network callback * add loading for favorite to prevent multi click * add interval re-fetch for pool data * fix widget over height issue * add pagination for pool data with tag * fix height issue * add dynamic data * fix img radius * fix ui * add notify if dex is not suportted on chain * add copy pool address to pool explorer mobile * fix zap widget mobile full width * remove change network callback * fix default chain filter in pool explorer * fix click action for card on landing page * turn pool address copied to lower case * fix earn tab on header and limit the displayed pools in landing page * fix text color for error button * fix pool address displayed color * update env endpoint and liquidity widget version with Tailwind * update liquidity widget code from main * update wrong network message from liquidity widget * update zap-earn endpoint from liquidity widget * format displayed price * show only chain that have pool data * make dropdown content highlight by increasing brightness & re-format dex name * fix dropdown width * add tooltip for filter tab & add disclaimer * fix bug change network but protocol still keep * fix wrong network message * add loading to landing page * add token pair to list all tokens if don't have * fix issue q from url query string disappear on reload * integrate protocols sushiswapv3 & thrusterv3, fix issue widget doesn't get route when wrong network, fix placeholder in search input to remove token name * add delay for toggle favorite * reset search after change filter * reset search after change filter * fix dropdown ui * add zap migration widget * update yarn.lock * fix migration widget ui * my liquidity page * switch to pre-release endpoint * re-format data after switch to Krystal data * responsive earn landing page * fix endpoint of liquidity widget to pre release * fix bug sorting in pool explorer page does not work * fix bugs * integrate API for my positions page * add tooltip for position screen and empty data UI * add tooltip for position screen and empty data UI * add logic only remove search when change tag * fix loading for positions page * fix tooltip for desktop only * fix description * fix bug can not open zap widget for pancake protocol * Update pool timeline and change Kyberswap to KyberSwap * update zap widget * update user position button * fix bug sort pool when filtered by tag * update description for transaction time limit in setting * fix font family for zap widget * fix font family for zap widget * add some sort logic * reset sort if pool filter by tag * remove sort for pool with tags filter * filter earn by 2 chains ETH and Base, 3 dexes Uniswap Pancakeswap and Sushiswap * fix default chain logic * add default sort when select all pools * add action open increase zap widget for user position screen * add position detail screen * improve code struture position detail * remove arb from list supported chain * fix bugs * integrate Kyber Earn API * fix bug favorite pool when API fetched failed * reset pool explorer data when API fetched failed * write a utils func to format apr * update new widget * update widget * fix user positions issues * fix bugs widget * fix bugs & improve widget * fix price impact warning * add tooltip for tvl in pool explorer * fix issue widget * update widget * move zap migration out of zap in widget * fix pool explorer wrapper max height * fix widget styles affect outside * fix widget * fix: zap migration debounce liquidity out * fix: koicl name * feat: integrate zapout * fix: zapout padding * fix: zapout * fix: zapout sushi * fix: yarn lock * remove unneccesary logs * add liquidity chart to liquidity widget * fix liquidity widget name * add liquidity chart * fix: zap migration swap impact * add fee amount for pancake * fix user positions filter * add filter network and protocol for user position screen * remove chart cursor * fix token order * fix: zap migration * fix in-consistent token image * fix: zap out select token * add option all chains to user position filters * fix earning fee calculation * fix: zap out * fix bug filter * fix: zapout issues * bugs fix * bugs fix * fix: zap migration * fix bug chart * fix: zap migration * fix: zap migration * fix zap widget * fix liquidity chart if revert * fix: zap migration cache store * fix: zap migration cache store * fix: zap migration source pos has 0 fee * fix: cache pos * fix bug zap in * fix: zap out * fix bug liquidity chart when set full range * fix bug zap in * feat: zap out responsive * fix: issues * feat: zap migraiton responsive * fix: import token * change earning 24h & 7d fee * add connect wallet button for my position page * add pool explorer button & my position button to position detail page * fix button text * redirect position detail to user position if wallet chaged * fix: zap in * fix bugs * fix: zapout crash * fix bugs liquidity chart * update view pos * fix bug liquidity chart * fix bugs * fix: zap out route * fix: zap out route * fix bugs liquidity chart * fix: swap pi zap migration * fix bugs liquidity chart * fix bugs liquidity chart * feat: integrate suggested slippage * fix: zap migration tooltip * chore: adjust suggested slp * fix bugs liquidity chart * add back action for zap migration widget * switch to prod api * switch Zap earn API to pre release * change zap api endpoint * fix: zap migration domain * feat: default slp * fix: change default slp * chore: raise gas limit * fix token total amount * fix: slp warning * switch zap earn api endpoint to production * fix: slp warning * format tick bottom on liquidity chart * prepare release --------- Co-authored-by: Tien Nguyen --- .env | 1 + .env.dev | 1 + .env.production | 1 + .env.stg | 1 + package.json | 2 + src/assets/images/earn-bg.png | Bin 0 -> 217240 bytes src/assets/svg/cursor.svg | 4 + src/assets/svg/fire.svg | 3 + src/assets/svg/help-circle.svg | 6 + src/assets/svg/ic_earn_not_found.svg | 5 + src/assets/svg/ic_left_arrow.svg | 5 + src/assets/svg/ic_pool_high_apr.svg | 14 + src/assets/svg/ic_pool_highlighted.svg | 5 + src/assets/svg/ic_pool_low_volatility.svg | 23 + src/assets/svg/ic_pool_solid_earning.svg | 6 + src/assets/svg/ic_user_earn_position.svg | 5 + src/assets/svg/liquidity-pools.svg | 6 + src/assets/svg/liquidity-positions.svg | 6 + src/assets/svg/low-volatility.svg | 11 + src/assets/svg/play-icon.svg | 19 + src/assets/svg/solid-earning.svg | 4 + src/assets/svg/staking.svg | 5 + src/components/CoinbaseSubscribeBtn.tsx | 4 +- src/components/Copy/index.tsx | 2 +- src/components/Header/index.tsx | 9 +- .../Header/web3/WalletModal/index.tsx | 1 + src/components/Image/index.tsx | 32 + src/components/Menu/index.tsx | 38 +- src/components/Modal/index.tsx | 29 +- .../SwapForm/AddMEVProtectionModal.tsx | 2 +- src/constants/index.ts | 5 + src/pages/App.tsx | 10 + src/pages/Earns/PoolExplorer/DropdownMenu.tsx | 158 +++++ src/pages/Earns/PoolExplorer/TableContent.tsx | 262 ++++++++ src/pages/Earns/PoolExplorer/index.tsx | 289 +++++++++ src/pages/Earns/PoolExplorer/styles.tsx | 221 +++++++ src/pages/Earns/PoolExplorer/useFilter.ts | 64 ++ src/pages/Earns/PositionDetail/Header.tsx | 86 +++ .../Earns/PositionDetail/LeftSection.tsx | 138 +++++ .../LiquidityChartRangeInput.tsx | 122 ++++ .../LiquidityChart/components/Area.tsx | 49 ++ .../LiquidityChart/components/AxisBottom.tsx | 37 ++ .../LiquidityChart/components/Brush.tsx | 254 ++++++++ .../LiquidityChart/components/Chart.tsx | 187 ++++++ .../LiquidityChart/components/InfoBox.tsx | 27 + .../LiquidityChart/components/Line.tsx | 24 + .../LiquidityChart/components/Loader.tsx | 40 ++ .../LiquidityChart/components/Zoom.tsx | 126 ++++ .../LiquidityChart/components/svg.tsx | 46 ++ .../PositionDetail/LiquidityChart/hooks.ts | 111 ++++ .../PositionDetail/LiquidityChart/index.tsx | 46 ++ .../PositionDetail/LiquidityChart/types.ts | 119 ++++ .../LiquidityChart/uniswapv3.ts | 344 +++++++++++ .../PositionDetail/LiquidityChart/utils.ts | 51 ++ .../Earns/PositionDetail/RightSection.tsx | 115 ++++ src/pages/Earns/PositionDetail/index.tsx | 177 ++++++ src/pages/Earns/PositionDetail/styles.tsx | 141 +++++ src/pages/Earns/UserPositions/Filter.tsx | 61 ++ src/pages/Earns/UserPositions/index.tsx | 311 ++++++++++ src/pages/Earns/UserPositions/styles.tsx | 201 ++++++ src/pages/Earns/UserPositions/useFilter.ts | 25 + src/pages/Earns/index.tsx | 583 ++++++++++++++++++ src/pages/Earns/useLiquidityWidget.tsx | 367 +++++++++++ src/pages/Earns/useSupportedDexesAndChains.ts | 69 +++ src/pages/Earns/utils.ts | 12 + src/services/ksSetting.ts | 1 + src/services/poolService.ts | 53 ++ src/services/zapEarn.ts | 295 +++++++++ src/state/index.ts | 6 + yarn.lock | 248 +++++++- 70 files changed, 5696 insertions(+), 35 deletions(-) create mode 100644 src/assets/images/earn-bg.png create mode 100644 src/assets/svg/cursor.svg create mode 100644 src/assets/svg/fire.svg create mode 100644 src/assets/svg/help-circle.svg create mode 100644 src/assets/svg/ic_earn_not_found.svg create mode 100644 src/assets/svg/ic_left_arrow.svg create mode 100644 src/assets/svg/ic_pool_high_apr.svg create mode 100644 src/assets/svg/ic_pool_highlighted.svg create mode 100644 src/assets/svg/ic_pool_low_volatility.svg create mode 100644 src/assets/svg/ic_pool_solid_earning.svg create mode 100644 src/assets/svg/ic_user_earn_position.svg create mode 100644 src/assets/svg/liquidity-pools.svg create mode 100644 src/assets/svg/liquidity-positions.svg create mode 100644 src/assets/svg/low-volatility.svg create mode 100644 src/assets/svg/play-icon.svg create mode 100644 src/assets/svg/solid-earning.svg create mode 100644 src/assets/svg/staking.svg create mode 100644 src/components/Image/index.tsx create mode 100644 src/pages/Earns/PoolExplorer/DropdownMenu.tsx create mode 100644 src/pages/Earns/PoolExplorer/TableContent.tsx create mode 100644 src/pages/Earns/PoolExplorer/index.tsx create mode 100644 src/pages/Earns/PoolExplorer/styles.tsx create mode 100644 src/pages/Earns/PoolExplorer/useFilter.ts create mode 100644 src/pages/Earns/PositionDetail/Header.tsx create mode 100644 src/pages/Earns/PositionDetail/LeftSection.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/LiquidityChartRangeInput.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Area.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/AxisBottom.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Brush.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Chart.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/InfoBox.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Line.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Loader.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/Zoom.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/components/svg.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/hooks.ts create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/index.tsx create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/types.ts create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/uniswapv3.ts create mode 100644 src/pages/Earns/PositionDetail/LiquidityChart/utils.ts create mode 100644 src/pages/Earns/PositionDetail/RightSection.tsx create mode 100644 src/pages/Earns/PositionDetail/index.tsx create mode 100644 src/pages/Earns/PositionDetail/styles.tsx create mode 100644 src/pages/Earns/UserPositions/Filter.tsx create mode 100644 src/pages/Earns/UserPositions/index.tsx create mode 100644 src/pages/Earns/UserPositions/styles.tsx create mode 100644 src/pages/Earns/UserPositions/useFilter.ts create mode 100644 src/pages/Earns/index.tsx create mode 100644 src/pages/Earns/useLiquidityWidget.tsx create mode 100644 src/pages/Earns/useSupportedDexesAndChains.ts create mode 100644 src/pages/Earns/utils.ts create mode 100644 src/services/poolService.ts create mode 100644 src/services/zapEarn.ts diff --git a/.env b/.env index ff60e51de2..ca38e603ee 100644 --- a/.env +++ b/.env @@ -39,3 +39,4 @@ VITE_CAMPAIGN_URL=https://kyberswap-arbitrum-stip.kyberengineering.io/api VITE_REFERRAL_URL=https://referral.kyberswap.com/api VITE_TOKEN_API_URL=https://pre-token-api.kyberengineering.io/api +VITE_ZAP_EARN_URL=https://zap-earn-service.kyberengineering.io/api diff --git a/.env.dev b/.env.dev index 72870471f9..2c0f3f967f 100644 --- a/.env.dev +++ b/.env.dev @@ -40,3 +40,4 @@ VITE_CAMPAIGN_URL=https://kyberswap-arbitrum-stip.kyberengineering.io/api VITE_REFERRAL_URL=https://referral.kyberswap.com/api VITE_TOKEN_API_URL=https://pre-token-api.kyberengineering.io/api +VITE_ZAP_EARN_URL=https://zap-earn-service.kyberengineering.io/api diff --git a/.env.production b/.env.production index adadcd6137..f100f0f62b 100644 --- a/.env.production +++ b/.env.production @@ -39,3 +39,4 @@ VITE_CAMPAIGN_URL=https://kyberswap-arbitrum-stip.kyberengineering.io/api VITE_REFERRAL_URL=https://referral.kyberswap.com/api VITE_TOKEN_API_URL=https://token-api.kyberengineering.io/api +VITE_ZAP_EARN_URL=https://zap-earn-service.kyberengineering.io/api diff --git a/.env.stg b/.env.stg index 9b57746e81..a1b443faba 100644 --- a/.env.stg +++ b/.env.stg @@ -37,3 +37,4 @@ VITE_CAMPAIGN_URL=https://kyberswap-arbitrum-stip.kyberengineering.io/api VITE_REFERRAL_URL=https://referral.kyberswap.com/api VITE_TOKEN_API_URL=https://pre-token-api.kyberengineering.io/api +VITE_ZAP_EARN_URL=https://zap-earn-service.kyberengineering.io/api diff --git a/package.json b/package.json index ab7469ca1e..165322cf41 100644 --- a/package.json +++ b/package.json @@ -50,6 +50,8 @@ "@kyberswap/ks-sdk-classic": "^1.0.3", "@kyberswap/ks-sdk-core": "1.1.7", "@kyberswap/ks-sdk-elastic": "^1.1.2", + "@kyberswap/liquidity-widgets": "1.1.0", + "@kyberswap/zap-migration-widgets": "1.0.0", "@kyberswap/oauth2": "1.0.2", "@lingui/macro": "^4.6.0", "@lingui/react": "^4.6.0", diff --git a/src/assets/images/earn-bg.png b/src/assets/images/earn-bg.png new file mode 100644 index 0000000000000000000000000000000000000000..086ac0666e8bc84ca94dfb8dacdeea95e8e7af7d GIT binary patch literal 217240 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�aUBzthp^d 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
-feTHJaE3F0efebR&+6>;1Z>

x#6)E+-AbJ4! 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 literal 0 HcmV?d00001 diff --git a/src/assets/svg/cursor.svg b/src/assets/svg/cursor.svg new file mode 100644 index 0000000000..274df257e6 --- /dev/null +++ b/src/assets/svg/cursor.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svg/fire.svg b/src/assets/svg/fire.svg new file mode 100644 index 0000000000..6553da38b9 --- /dev/null +++ b/src/assets/svg/fire.svg @@ -0,0 +1,3 @@ + + + diff --git a/src/assets/svg/help-circle.svg b/src/assets/svg/help-circle.svg new file mode 100644 index 0000000000..9f74035e94 --- /dev/null +++ b/src/assets/svg/help-circle.svg @@ -0,0 +1,6 @@ + + + + + \ No newline at end of file diff --git a/src/assets/svg/ic_earn_not_found.svg b/src/assets/svg/ic_earn_not_found.svg new file mode 100644 index 0000000000..e0c6e2d6ea --- /dev/null +++ b/src/assets/svg/ic_earn_not_found.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/ic_left_arrow.svg b/src/assets/svg/ic_left_arrow.svg new file mode 100644 index 0000000000..6b8df42051 --- /dev/null +++ b/src/assets/svg/ic_left_arrow.svg @@ -0,0 +1,5 @@ + + + + + \ No newline at end of file diff --git a/src/assets/svg/ic_pool_high_apr.svg b/src/assets/svg/ic_pool_high_apr.svg new file mode 100644 index 0000000000..a53bbd26a8 --- /dev/null +++ b/src/assets/svg/ic_pool_high_apr.svg @@ -0,0 +1,14 @@ + + + + + + + \ No newline at end of file diff --git a/src/assets/svg/ic_pool_highlighted.svg b/src/assets/svg/ic_pool_highlighted.svg new file mode 100644 index 0000000000..f75f1817b9 --- /dev/null +++ b/src/assets/svg/ic_pool_highlighted.svg @@ -0,0 +1,5 @@ + + + \ No newline at end of file diff --git a/src/assets/svg/ic_pool_low_volatility.svg b/src/assets/svg/ic_pool_low_volatility.svg new file mode 100644 index 0000000000..943eeeb223 --- /dev/null +++ b/src/assets/svg/ic_pool_low_volatility.svg @@ -0,0 +1,23 @@ + + + + + + + + + + + \ No newline at end of file diff --git a/src/assets/svg/ic_pool_solid_earning.svg b/src/assets/svg/ic_pool_solid_earning.svg new file mode 100644 index 0000000000..6c9f4f6298 --- /dev/null +++ b/src/assets/svg/ic_pool_solid_earning.svg @@ -0,0 +1,6 @@ + + + + \ No newline at end of file diff --git a/src/assets/svg/ic_user_earn_position.svg b/src/assets/svg/ic_user_earn_position.svg new file mode 100644 index 0000000000..a04d03038d --- /dev/null +++ b/src/assets/svg/ic_user_earn_position.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/assets/svg/liquidity-pools.svg b/src/assets/svg/liquidity-pools.svg new file mode 100644 index 0000000000..bd62353ea6 --- /dev/null +++ b/src/assets/svg/liquidity-pools.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svg/liquidity-positions.svg b/src/assets/svg/liquidity-positions.svg new file mode 100644 index 0000000000..a66214e69c --- /dev/null +++ b/src/assets/svg/liquidity-positions.svg @@ -0,0 +1,6 @@ + + + + + + diff --git a/src/assets/svg/low-volatility.svg b/src/assets/svg/low-volatility.svg new file mode 100644 index 0000000000..75a48cfbba --- /dev/null +++ b/src/assets/svg/low-volatility.svg @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/src/assets/svg/play-icon.svg b/src/assets/svg/play-icon.svg new file mode 100644 index 0000000000..496aec879e --- /dev/null +++ b/src/assets/svg/play-icon.svg @@ -0,0 +1,19 @@ + + + + + + + + + + + + + + + + + + + diff --git a/src/assets/svg/solid-earning.svg b/src/assets/svg/solid-earning.svg new file mode 100644 index 0000000000..d3b6facf36 --- /dev/null +++ b/src/assets/svg/solid-earning.svg @@ -0,0 +1,4 @@ + + + + diff --git a/src/assets/svg/staking.svg b/src/assets/svg/staking.svg new file mode 100644 index 0000000000..f4812c0fac --- /dev/null +++ b/src/assets/svg/staking.svg @@ -0,0 +1,5 @@ + + + + + diff --git a/src/components/CoinbaseSubscribeBtn.tsx b/src/components/CoinbaseSubscribeBtn.tsx index 8925a40f54..c955cab921 100644 --- a/src/components/CoinbaseSubscribeBtn.tsx +++ b/src/components/CoinbaseSubscribeBtn.tsx @@ -71,8 +71,8 @@ export default function CoinbaseSubscribeBtn({ onlyShowIfNotSubscribe = false }: size={13} text={ !isSubscribed - ? "Subscribe to receive Kyberswap's updates directly on your Coinbase Wallet." - : "Unsubscribe to stop receiving Kyberswap's updates directly on your Coinbase Wallet." + ? "Subscribe to receive KyberSwap's updates directly on your Coinbase Wallet." + : "Unsubscribe to stop receiving KyberSwap's updates directly on your Coinbase Wallet." } /> )} diff --git a/src/components/Copy/index.tsx b/src/components/Copy/index.tsx index e4cca029de..3977b466e8 100644 --- a/src/components/Copy/index.tsx +++ b/src/components/Copy/index.tsx @@ -106,7 +106,7 @@ const CopyHelper = forwardRef(function CopyHelper( ) return ( - + e.stopPropagation()} margin={margin} style={style}> {text ? ( {copyIcon} {text} diff --git a/src/components/Header/index.tsx b/src/components/Header/index.tsx index 712b6ca287..b65cd38469 100644 --- a/src/components/Header/index.tsx +++ b/src/components/Header/index.tsx @@ -8,7 +8,7 @@ import styled from 'styled-components' import Announcement from 'components/Announcement' import SelectNetwork from 'components/Header/web3/SelectNetwork' import SelectWallet from 'components/Header/web3/SelectWallet' -import Menu from 'components/Menu' +import Menu, { NewLabel } from 'components/Menu' import Row, { RowFixed } from 'components/Row' import { AGGREGATOR_ANALYTICS_URL, APP_PATHS } from 'constants/index' import { Z_INDEXS } from 'constants/styles' @@ -18,7 +18,6 @@ import { useHolidayMode } from 'state/user/hooks' import { MEDIA_WIDTHS } from 'theme' import AboutNavGroup from './groups/AboutNavGroup' -import CampaignNavGroup from './groups/CampaignNavGroup' import KyberDAONavGroup from './groups/KyberDaoGroup' import SwapNavGroup from './groups/SwapNavGroup' import { StyledNavExternalLink, StyledNavLink } from './styleds' @@ -208,10 +207,12 @@ export default function Header() { {!isPartnerSwap && ( - + + Earn + New + Market - Analytics diff --git a/src/components/Header/web3/WalletModal/index.tsx b/src/components/Header/web3/WalletModal/index.tsx index 33b8df91c7..0f8f2e4afb 100644 --- a/src/components/Header/web3/WalletModal/index.tsx +++ b/src/components/Header/web3/WalletModal/index.tsx @@ -231,6 +231,7 @@ export default function WalletModal() { //&& activationState.connection.type === ConnectionType.WALLET_CONNECT_V2 // walletView === WALLET_VIEWS.PENDING && ['WALLET_CONNECT', 'KRYSTAL_WC', 'BLOCTO'].includes(pendingWalletKey) } + zindex={99999} > {getModalContent()} diff --git a/src/components/Image/index.tsx b/src/components/Image/index.tsx new file mode 100644 index 0000000000..7a5ec977bc --- /dev/null +++ b/src/components/Image/index.tsx @@ -0,0 +1,32 @@ +import HelpIcon from 'assets/svg/help-circle.svg' + +export function Image({ + src, + alt, + className, + width, + height, + style, +}: { + src: string + alt: string + className?: string + width?: string + height?: string + style?: React.CSSProperties +}) { + return ( + {alt} { + currentTarget.onerror = null // prevents looping + currentTarget.src = HelpIcon + }} + /> + ) +} diff --git a/src/components/Menu/index.tsx b/src/components/Menu/index.tsx index f9cdea3c50..fa6cd53325 100644 --- a/src/components/Menu/index.tsx +++ b/src/components/Menu/index.tsx @@ -220,7 +220,6 @@ export default function Menu() { const showAbout = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) const showBlog = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) const showAnalytics = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) - const showCampaigns = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`) const bridgeLink = networkInfo.bridgeURL const toggleClaimPopup = useToggleModal(ApplicationModal.CLAIM_POPUP) @@ -349,26 +348,23 @@ export default function Menu() { )} - {showCampaigns && ( - - } - title={ - - Campaigns - New - - } - link="#" - options={[ - { link: APP_PATHS.AGGREGATOR_CAMPAIGN, label: t`Aggregator Trading` }, - { link: APP_PATHS.LIMIT_ORDER_CAMPAIGN, label: t`Limit Order` }, - { link: APP_PATHS.REFFERAL_CAMPAIGN, label: t`Referral` }, - { link: APP_PATHS.MY_DASHBOARD, label: t`My Dashboard`, external: true }, - ]} - /> - - )} + + } + title={ + + Campaigns + + } + link="#" + options={[ + { link: APP_PATHS.AGGREGATOR_CAMPAIGN, label: t`Aggregator Trading` }, + { link: APP_PATHS.LIMIT_ORDER_CAMPAIGN, label: t`Limit Order` }, + { link: APP_PATHS.REFFERAL_CAMPAIGN, label: t`Referral` }, + { link: APP_PATHS.MY_DASHBOARD, label: t`My Dashboard`, external: true }, + ]} + /> + {bridgeLink && ( diff --git a/src/components/Modal/index.tsx b/src/components/Modal/index.tsx index 4c68967782..8a825c2468 100644 --- a/src/components/Modal/index.tsx +++ b/src/components/Modal/index.tsx @@ -24,9 +24,20 @@ const StyledDialogOverlay = styled(AnimatedDialogOverlay)<{ zindex: string | num const AnimatedDialogContent = motion(DialogContent) // destructure to not pass custom props to Dialog DOM element const StyledDialogContent = styled( - ({ borderRadius, minHeight, maxHeight, maxWidth, width, height, bgColor, mobile, isOpen, margin, ...rest }) => ( - - ), + ({ + borderRadius, + minHeight, + maxHeight, + maxWidth, + width, + height, + bgColor, + mobile, + isOpen, + margin, + mobileFullWidth, + ...rest + }) => , ).attrs({ 'aria-label': 'dialog', })` @@ -61,8 +72,13 @@ const StyledDialogContent = styled( width: ${width || '65vw'}; margin: 0; `} - ${({ theme, mobile, borderRadius }) => theme.mediaWidth.upToSmall` - width: 85vw; + ${({ theme, mobile, borderRadius, mobileFullWidth }) => theme.mediaWidth.upToSmall` + ${ + !mobileFullWidth && + ` + width: 85vw; + ` + } ${ mobile && ` @@ -95,6 +111,7 @@ export interface ModalProps { enableSwipeGesture?: boolean bypassScrollLock?: boolean bypassFocusLock?: boolean + mobileFullWidth?: boolean } export default function Modal({ @@ -118,6 +135,7 @@ export default function Modal({ enableSwipeGesture = false, bypassScrollLock = false, bypassFocusLock = false, + mobileFullWidth = false, }: ModalProps) { const animateValues = { initial: { opacity: 0 }, @@ -160,6 +178,7 @@ export default function Modal({ bgColor={bgColor} borderRadius={borderRadius} mobile={isMobile} + mobileFullWidth={mobileFullWidth} className={className} {...animateValues} > diff --git a/src/components/SwapForm/AddMEVProtectionModal.tsx b/src/components/SwapForm/AddMEVProtectionModal.tsx index bcb1d33279..b7f71e3577 100644 --- a/src/components/SwapForm/AddMEVProtectionModal.tsx +++ b/src/components/SwapForm/AddMEVProtectionModal.tsx @@ -55,7 +55,7 @@ export default function AddMEVProtectionModal({ isOpen, onClose }: { isOpen: boo return } - const name = 'Ethereum Mainnet (Kyberswap RPC)' + const name = 'Ethereum Mainnet (KyberSwap RPC)' mixpanelHandler(MIXPANEL_TYPE.MEV_ADD_CLICK_MODAL, { type: name }) addNewNetwork( ChainId.MAINNET, diff --git a/src/constants/index.ts b/src/constants/index.ts index 8a32a77b5f..d93e90868e 100644 --- a/src/constants/index.ts +++ b/src/constants/index.ts @@ -169,6 +169,11 @@ export const APP_PATHS = { LIMIT_ORDER_CAMPAIGN: '/campaigns/limit-order', REFFERAL_CAMPAIGN: '/campaigns/referrals', MY_DASHBOARD: '/campaigns/dashboard', + + EARN: '/earns', + EARN_POOLS: '/earns/pools', + EARN_POSITIONS: '/earns/positions', + EARN_POSITION_DETAIL: '/earns/position/:chainId/:id', } as const export const TERM_FILES_PATH = { diff --git a/src/pages/App.tsx b/src/pages/App.tsx index d82d7f1f10..3bd8dd277a 100644 --- a/src/pages/App.tsx +++ b/src/pages/App.tsx @@ -59,6 +59,11 @@ const NotificationCenter = lazy(() => import('pages/NotificationCenter')) const Campaign = lazy(() => import('pages/Campaign')) const CampaignMyDashboard = lazy(() => import('pages/Campaign/MyDashboard')) +const Earns = lazy(() => import('pages/Earns')) +const EarnPoolExplorer = lazy(() => import('pages/Earns/PoolExplorer')) +const EarnUserPositions = lazy(() => import('pages/Earns/UserPositions')) +const EarnPositionDetail = lazy(() => import('pages/Earns/PositionDetail')) + const AppWrapper = styled.div` display: flex; flex-flow: column; @@ -316,6 +321,11 @@ export default function App() { } /> } /> + } /> + } /> + } /> + } /> + } /> diff --git a/src/pages/Earns/PoolExplorer/DropdownMenu.tsx b/src/pages/Earns/PoolExplorer/DropdownMenu.tsx new file mode 100644 index 0000000000..e196036ec1 --- /dev/null +++ b/src/pages/Earns/PoolExplorer/DropdownMenu.tsx @@ -0,0 +1,158 @@ +import { useEffect, useMemo, useRef, useState } from 'react' +import { useMedia } from 'react-use' +import styled from 'styled-components' + +import { ReactComponent as DropdownSVG } from 'assets/svg/down.svg' +import { MEDIA_WIDTHS } from 'theme' + +const DropdownWrapper = styled.div<{ mobileFullWidth: boolean }>` + position: relative; + width: fit-content; + + ${({ theme, mobileFullWidth }) => theme.mediaWidth.upToSmall` + ${mobileFullWidth && 'width: 100%;'} + `} +` + +const DropdownTitleWrapper = styled.div` + background: ${({ theme }) => theme.background}; + border-radius: 30px; + padding: 6px 12px; + font-size: 14px; + cursor: pointer; + color: ${({ theme }) => theme.subText}; + display: flex; + align-items: center; + justify-content: center; +` + +const DropdownTitle = styled.div<{ width?: number }>` + width: ${({ width }) => (width ? `${width}px` : '')}; + min-width: ${({ width }) => (!width ? '100px' : 'max-content')}; + display: flex; + align-items: center; + justify-content: center; + gap: 6px; + text-transform: capitalize; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + min-width: max-content; + `} +` + +const DropdownIcon = styled(DropdownSVG)<{ open: boolean }>` + transform: ${({ open }) => (open ? 'rotate(180deg)' : 'rotate(0deg)')}; + transition: transform 0.3s; +` + +const ItemIcon = styled.img` + width: 18px; + height: 18px; +` + +const DropdownContent = styled.div<{ alignLeft: boolean }>` + position: absolute; + top: 42px; + left: 0; + background: ${({ theme }) => theme.background}; + border-radius: 24px; + padding: 8px 12px; + font-size: 14px; + color: ${({ theme }) => theme.text}; + width: max-content; + display: flex; + flex-direction: column; + align-items: ${({ alignLeft }) => (alignLeft ? 'flex-start' : 'center')}; + gap: 4px; + max-height: 218px; + overflow-y: auto; + z-index: 100; + filter: brightness(1.2); +` + +const DropdownContentItem = styled.div` + padding: 8px; + border-radius: 30px; + display: flex; + align-items: center; + justify-content: center; + gap: 8px; + cursor: pointer; + text-transform: capitalize; + + &:hover { + background: ${({ theme }) => theme.tableHeader}; + } +` + +export interface MenuOption { + label: string + value: string + icon?: string +} + +const DropdownMenu = ({ + options, + value, + width, + alignLeft = false, + mobileFullWidth = false, + onChange, +}: { + options: MenuOption[] + value: string | number + width?: number + alignLeft?: boolean + mobileFullWidth?: boolean + onChange: (value: string | number) => void +}) => { + const upToExtraSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) + + const [open, setOpen] = useState(false) + const ref = useRef(null) + + const optionValue = useMemo(() => options.find(option => option.value === value), [options, value]) + + const handleOpenChange = () => setOpen(!open) + + const handleSelectItem = (newValue: string | number) => { + onChange(newValue) + setOpen(false) + } + + useEffect(() => { + const handleClickOutside = (event: MouseEvent) => { + if (!ref?.current?.contains(event.target as Node)) setOpen(false) + } + + document.addEventListener('mousedown', handleClickOutside) + + return () => { + document.removeEventListener('mousedown', handleClickOutside) + } + }, [ref]) + + return ( + + + + {optionValue?.icon && } + {(!upToExtraSmall || !optionValue?.icon) && optionValue?.label} + + + + {open && ( + + {options.map((option: MenuOption) => ( + handleSelectItem(option.value)}> + {option.icon && } + {option.label} + + ))} + + )} + + ) +} + +export default DropdownMenu diff --git a/src/pages/Earns/PoolExplorer/TableContent.tsx b/src/pages/Earns/PoolExplorer/TableContent.tsx new file mode 100644 index 0000000000..b616e5b7a2 --- /dev/null +++ b/src/pages/Earns/PoolExplorer/TableContent.tsx @@ -0,0 +1,262 @@ +import { useEffect, useMemo, useState } from 'react' +import { Star } from 'react-feather' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import { useGetDexListQuery } from 'services/ksSetting' +import { EarnPool, useAddFavoriteMutation, usePoolsExplorerQuery, useRemoveFavoriteMutation } from 'services/zapEarn' + +import { NotificationType } from 'components/Announcement/type' +import CopyHelper from 'components/Copy' +import { Image } from 'components/Image' +import Loader from 'components/Loader' +import { NETWORKS_INFO } from 'constants/networks' +import { useActiveWeb3React, useWeb3React } from 'hooks' +import useTheme from 'hooks/useTheme' +import { useNotify, useWalletModalToggle } from 'state/application/hooks' +import { MEDIA_WIDTHS } from 'theme' +import { formatDisplayNumber } from 'utils/numbers' + +import { formatAprNumber } from '../utils' +import { + Apr, + CurrencyRoundedImage, + CurrencySecondImage, + FeeTier, + MobileTableBottomRow, + MobileTableRow, + SymbolText, + TableBody, + TableRow, +} from './styles' +import useFilter from './useFilter' + +const TableContent = ({ onOpenZapInWidget }: { onOpenZapInWidget: (pool: EarnPool) => void }) => { + const theme = useTheme() + const { account } = useActiveWeb3React() + const { library } = useWeb3React() + const { filters } = useFilter() + const notify = useNotify() + const toggleWalletModal = useWalletModalToggle() + + const dexList = useGetDexListQuery({ + chainId: NETWORKS_INFO[filters.chainId].ksSettingRoute, + }) + const { data: poolData, refetch, isError } = usePoolsExplorerQuery(filters, { pollingInterval: 5 * 60_000 }) + const [addFavorite] = useAddFavoriteMutation() + const [removeFavorite] = useRemoveFavoriteMutation() + + const [favoriteLoading, setFavoriteLoading] = useState([]) + const [delayFavorite, setDelayFavorite] = useState(false) + + const upToMedium = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`) + + const tablePoolData = useMemo(() => { + return (poolData?.data?.pools || []).map(pool => ({ + ...pool, + dexLogo: dexList.data?.find(dex => dex.dexId === pool.exchange)?.logoURL || '', + dexName: dexList.data?.find(dex => dex.dexId === pool.exchange)?.name || '', + })) + }, [poolData, dexList]) + + const handleFavorite = async (e: React.MouseEvent, pool: EarnPool) => { + e.stopPropagation() + if (favoriteLoading.includes(pool.address) || delayFavorite) return + handleAddFavoriteLoading(pool.address) + + if (!account) { + toggleWalletModal() + handleRemoveFavoriteLoading(pool.address) + return + } + + let signature = '' + let msg = '' + + const key = `poolExplorer_${account}` + try { + const data = JSON.parse(localStorage.getItem(key) || '') + if (data.issuedAt) { + const expire = new Date(data.issuedAt) + expire.setDate(expire.getDate() + 7) + const now = new Date() + if (expire > now) { + signature = data.signature + msg = data.msg + } + } + } catch { + handleRemoveFavoriteLoading(pool.address) + } + if (!signature) { + const issuedAt = new Date().toISOString() + msg = `Click sign to add favorite pools at Kyberswap.com without logging in.\nThis request won’t trigger any blockchain transaction or cost any gas fee. Expires in 7 days. \n\nIssued at: ${issuedAt}` + signature = await library?.send('personal_sign', [`0x${Buffer.from(msg, 'utf8').toString('hex')}`, account]) + localStorage.setItem( + key, + JSON.stringify({ + signature, + msg, + issuedAt, + }), + ) + } + + const isPoolFavorite = !!pool.favorite?.isFavorite + setDelayFavorite(true) + await (isPoolFavorite ? removeFavorite : addFavorite)({ + chainId: filters.chainId, + userAddress: account, + poolAddress: pool.address, + message: msg, + signature, + }) + .then(res => { + if ((res as any).error) { + notify( + { + title: `${!isPoolFavorite ? 'Add' : 'Remove'} failed`, + summary: (res as any).error.data.message || 'Some thing went wrong', + type: NotificationType.ERROR, + }, + 8000, + ) + } else refetch() + }) + .catch(err => { + // localStorage.removeItem(key) + console.log(err) + notify( + { + title: `${!isPoolFavorite ? 'Add' : 'Remove'} failed`, + summary: err.message || 'Some thing went wrong', + type: NotificationType.ERROR, + }, + 8000, + ) + }) + .finally(() => handleRemoveFavoriteLoading(pool.address)) + } + const handleAddFavoriteLoading = (poolAddress: string) => { + if (!favoriteLoading.includes(poolAddress)) setFavoriteLoading([...favoriteLoading, poolAddress]) + } + const handleRemoveFavoriteLoading = (poolAddress: string) => + setFavoriteLoading(favoriteLoading.filter(address => address !== poolAddress)) + + useEffect(() => { + if (delayFavorite) + setTimeout(() => { + setDelayFavorite(false) + }, 500) + }, [delayFavorite]) + + if (!tablePoolData?.length || isError) + return ( + + No data found + + ) + + if (upToMedium) + return ( + + {tablePoolData.map((pool, index) => ( + onOpenZapInWidget(pool)}> + + + + + + + + + + {pool.tokens?.[0]?.symbol}/{pool.tokens?.[1]?.symbol} + + + + + + {pool.feeTier}% + + + + + 0}>{formatAprNumber(pool.apr)}% + handleFavorite(e, pool)} + /> + + + + + Earn Fees + {formatDisplayNumber(pool.earnFee, { style: 'currency', significantDigits: 6 })} + + + TVL + {formatDisplayNumber(pool.tvl, { style: 'currency', significantDigits: 6 })} + + + Volume + {formatDisplayNumber(pool.volume, { style: 'currency', significantDigits: 6 })} + + + + ))} + + ) + + return ( + + {tablePoolData.map(pool => ( + onOpenZapInWidget(pool)}> + + + {pool.dexName} + + + + + + + + {pool.tokens?.[0]?.symbol}/{pool.tokens?.[1]?.symbol} + + {pool.feeTier}% + + 0}>{formatAprNumber(pool.apr)}% + + {formatDisplayNumber(pool.earnFee, { style: 'currency', significantDigits: 6 })} + + + {formatDisplayNumber(pool.tvl, { style: 'currency', significantDigits: 6 })} + + + {formatDisplayNumber(pool.volume, { style: 'currency', significantDigits: 6 })} + + + {favoriteLoading.includes(pool.address) ? ( + + ) : ( + handleFavorite(e, pool)} + /> + )} + + + ))} + + ) +} + +export default TableContent diff --git a/src/pages/Earns/PoolExplorer/index.tsx b/src/pages/Earns/PoolExplorer/index.tsx new file mode 100644 index 0000000000..0a747da054 --- /dev/null +++ b/src/pages/Earns/PoolExplorer/index.tsx @@ -0,0 +1,289 @@ +import { t } from '@lingui/macro' +import { useEffect, useState } from 'react' +import { Info, Star } from 'react-feather' +import { useSearchParams } from 'react-router-dom' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import { usePoolsExplorerQuery } from 'services/zapEarn' + +import { ReactComponent as IconHighAprPool } from 'assets/svg/ic_pool_high_apr.svg' +import { ReactComponent as IconHighlightedPool } from 'assets/svg/ic_pool_highlighted.svg' +import { ReactComponent as IconLowVolatility } from 'assets/svg/ic_pool_low_volatility.svg' +import { ReactComponent as IconSolidEarningPool } from 'assets/svg/ic_pool_solid_earning.svg' +import { ReactComponent as IconUserEarnPosition } from 'assets/svg/ic_user_earn_position.svg' +import Pagination from 'components/Pagination' +import Search from 'components/Search' +import { MouseoverTooltip, MouseoverTooltipDesktopOnly } from 'components/Tooltip' +import { APP_PATHS } from 'constants/index' +import useDebounce from 'hooks/useDebounce' +import useTheme from 'hooks/useTheme' +import SortIcon, { Direction } from 'pages/MarketOverview/SortIcon' +import { MEDIA_WIDTHS } from 'theme' + +import useLiquidityWidget from '../useLiquidityWidget' +import useSupportedDexesAndChains from '../useSupportedDexesAndChains' +import DropdownMenu, { MenuOption } from './DropdownMenu' +import TableContent from './TableContent' +import { + ContentWrapper, + Disclaimer, + HeadSection, + NavigateButton, + PoolPageWrapper, + TableHeader, + TableWrapper, + Tag, + TagContainer, +} from './styles' +import useFilter from './useFilter' + +export enum FilterTag { + HIGHLIGHTED_POOL = 'highlighted_pool', + HIGH_APR = 'high_apr', + SOLID_EARNING = 'solid_earning', + LOW_VOLATILITY = 'low_volatility', +} + +export enum SortBy { + APR = 'apr', + EARN_FEE = 'earn_fee', + TVL = 'tvl', + VOLUME = 'volume', +} + +const filterTags = [ + { + label: 'Highlighted Pools', + value: FilterTag.HIGHLIGHTED_POOL, + icon: , + tooltip: 'Pools matching your wallet tokens or top 24h volume pools if no wallet is connected', + }, + { + label: 'High APR', + value: FilterTag.HIGH_APR, + icon: , + tooltip: 'Top 100 Pools with assets that offer exceptionally high APYs', + }, + { + label: 'Solid Earning', + value: FilterTag.SOLID_EARNING, + icon: , + tooltip: 'Top 100 pools that have the high total earned fee in the last 7 days', + }, + { + label: 'Low Volatility', + value: FilterTag.LOW_VOLATILITY, + icon: , + tooltip: 'Top 100 highest TVL Pools consisting of stable coins or correlated pairs', + }, +] + +export const timings: MenuOption[] = [ + { label: '24h', value: '24h' }, + { label: '7d', value: '7d' }, + { label: '30d', value: '30d' }, +] + +const Earn = () => { + const [search, setSearch] = useState('') + const deboundedSearch = useDebounce(search, 300) + const [searchParams] = useSearchParams() + const theme = useTheme() + const { filters, updateFilters } = useFilter(setSearch) + const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget() + const { data: poolData, isError } = usePoolsExplorerQuery(filters, { pollingInterval: 5 * 60_000 }) + const { supportedDexes, supportedChains } = useSupportedDexesAndChains(filters) + + const upToExtraSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToExtraSmall}px)`) + const upToMedium = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`) + const upToLarge = useMedia(`(max-width: ${MEDIA_WIDTHS.upToLarge}px)`) + + const onChainChange = (newChainId: string | number) => { + updateFilters('chainId', newChainId.toString()) + } + const onProtocolChange = (newProtocol: string | number) => { + updateFilters('protocol', newProtocol.toString()) + } + const onIntervalChange = (newInterval: string | number) => { + updateFilters('interval', newInterval.toString()) + } + const onSortChange = (sortBy: string) => { + if (!filters.sortBy || filters.sortBy !== sortBy) { + updateFilters('sortBy', sortBy) + updateFilters('orderBy', Direction.DESC) + return + } + if (filters.orderBy === Direction.DESC) { + updateFilters('orderBy', Direction.ASC) + return + } + updateFilters('sortBy', '') + updateFilters('orderBy', '') + } + + useEffect(() => { + if (searchParams.get('q') && !search) { + setSearch(searchParams.get('q') || '') + } + // eslint-disable-next-line react-hooks/exhaustive-deps + }, []) + + useEffect(() => { + if (filters.q !== deboundedSearch) { + updateFilters('q', deboundedSearch || '') + } + }, [deboundedSearch, filters.q, updateFilters]) + + return ( + + {liquidityWidget} + +

+ + {t`Earning with Smart Liquidity Providing`} + + + {t`KyberSwap Zap: Instantly and easily add liquidity to high-APY pools using any token or a combination of tokens.`} + +
+ + + updateFilters('tag', '')}> + {t`All pools`} + + + updateFilters('tag', 'favorite')}> + + + + {filterTags.map((item, index) => + !upToMedium ? ( + + updateFilters('tag', item.value)} + > + {!upToExtraSmall && item.icon} + {item.label} + + + ) : ( + updateFilters('tag', item.value)} + > + {!upToExtraSmall && item.icon} + {item.label} + + ), + )} + + {!upToLarge && ( + } text={t`My Positions`} to={APP_PATHS.EARN_POSITIONS} /> + )} + + + + + + + + setSearch(val)} + style={{ height: '36px' }} + /> + + + {upToLarge && ( + } text={t`My Positions`} to={APP_PATHS.EARN_POSITIONS} /> + )} + + + + {!upToMedium && ( + + Protocol + Pair + onSortChange(SortBy.APR)} + > + APR + + + onSortChange(SortBy.EARN_FEE)} + > + Earn Fees + + + onSortChange(SortBy.TVL)} + > + TVL + + + + + + + + onSortChange(SortBy.VOLUME)} + > + Volume + + +
+ + )} + + + {!isError && ( + updateFilters('page', newPage.toString())} + totalCount={poolData?.data?.pagination?.totalItems || 0} + currentPage={filters.page || 1} + pageSize={filters.limit || 10} + /> + )} + + + {t`KyberSwap provides tools for tracking & adding liquidity to third-party Protocols. For any pool-related concerns, please contact the respective Liquidity Protocol directly.`} + + ) +} + +export default Earn diff --git a/src/pages/Earns/PoolExplorer/styles.tsx b/src/pages/Earns/PoolExplorer/styles.tsx new file mode 100644 index 0000000000..cbab62a7d1 --- /dev/null +++ b/src/pages/Earns/PoolExplorer/styles.tsx @@ -0,0 +1,221 @@ +import { rgba } from 'polished' +import { useNavigate } from 'react-router-dom' +import { Text } from 'rebass' +import styled from 'styled-components' + +import { Image } from 'components/Image' + +export const PoolPageWrapper = styled.div` + padding: 32px 24px 68px; + width: 100%; + max-width: 1500px; + display: flex; + flex-direction: column; + gap: 16px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 24px 16px 100px; + `} +` + +export const LiquidityWidgetWrapper = styled.div` + width: 100%; + display: flex; + justify-content: center; +` + +export const HeadSection = styled.div` + display: flex; + width: 100%; + align-items: center; + justify-content: space-between; +` + +export const TagContainer = styled.div` + display: flex; + gap: 1rem; + width: 100%; + overflow-x: auto; + + ${({ theme }) => theme.mediaWidth.upToSmall` + gap: 0.75rem; + `} +` + +export const Tag = styled.div<{ active: boolean }>` + background: ${({ theme, active }) => (active ? rgba(theme.primary, 0.2) : theme.background)}; + border: 1px solid ${({ theme, active }) => (active ? theme.primary : 'transparent')}; + border-radius: 12px; + padding: 4px 16px; + font-size: 14px; + cursor: pointer; + color: ${({ theme, active }) => (active ? theme.text : theme.subText)}; + font-weight: ${({ active }) => (active ? '500' : '400')}; + display: flex; + align-items: center; + gap: 8px; + flex: 0 0 auto; + line-height: 28px; + height: 42px; + + ${({ theme }) => theme.mediaWidth.upToMedium` + height: 38px; + `} +` + +export const StyledNavigateButton = styled.div<{ mobileFullWidth?: boolean }>` + display: flex; + align-items: center; + justify-content: center; + gap: 10px; + background-color: ${({ theme }) => rgba(theme.primary, 0.1)}; + color: ${({ theme }) => theme.subText}; + border-radius: 12px; + padding: 8px 16px; + width: max-content; + font-size: 14px; + cursor: pointer; + + :hover { + filter: brightness(1.1); + } + + ${({ theme, mobileFullWidth }) => theme.mediaWidth.upToSmall` + ${mobileFullWidth && 'width: 100%;'} + `} +` + +interface NavigateButtonProps { + icon: React.ReactNode + text: string + to: string + mobileFullWidth?: boolean +} + +export const NavigateButton: React.FC = ({ icon, text, to, mobileFullWidth }) => { + const navigate = useNavigate() + + return ( + navigate({ pathname: to })}> + {icon} + {text} + + ) +} + +export const TableWrapper = styled.div` + background: ${({ theme }) => rgba(theme.background, 0.8)}; + border-radius: 16px; + overflow: hidden; + + ${({ theme }) => theme.mediaWidth.upToMedium` + margin: 0 -16px; + border-radius: 0; + `} +` + +export const ContentWrapper = styled.div`` + +export const TableHeader = styled.div` + display: grid; + grid-template-columns: 1fr 1fr 0.5fr 1fr 1fr 1fr 80px; + align-items: center; + color: ${({ theme }) => theme.subText}; + border-bottom: 1px solid ${({ theme }) => theme.tableHeader}; + padding-bottom: 24px; + margin: 24px; + margin-bottom: 0; + + ${({ theme }) => theme.mediaWidth.upToLarge` + grid-template-columns: 1fr 1.2fr 0.5fr 1fr 1fr 1fr 80px; + `} +` + +export const TableBody = styled.div` + max-height: 740px; + overflow-y: auto; +` + +export const TableRow = styled.div` + display: grid; + grid-template-columns: 1fr 1fr 0.5fr 1fr 1fr 1fr 80px; + padding: 24px; + cursor: pointer; + + :hover { + background: #31cb9e1a; + } + + ${({ theme }) => theme.mediaWidth.upToLarge` + grid-template-columns: 1fr 1.2fr 0.5fr 1fr 1fr 1fr 80px; + `} +` + +export const FeeTier = styled.div` + border-radius: 30px; + padding: 4px 8px; + font-size: 12px; + background: ${({ theme }) => rgba(theme.white, 0.04)}; + color: ${({ theme }) => theme.subText}; + width: fit-content; + + ${({ theme }) => theme.mediaWidth.upToExtraSmall` + font-size: 14px; + `} +` + +export const CurrencyRoundedImage = styled(Image)` + border-radius: 50%; + width: 24px; + height: 24px; +` + +export const CurrencySecondImage = styled(CurrencyRoundedImage)` + position: relative; + left: -6px; +` + +export const SymbolText = styled.div` + max-width: 115px; + white-space: nowrap; + overflow: hidden; + text-overflow: ellipsis; +` + +export const Apr = styled.div<{ positive: boolean }>` + display: flex; + justify-content: flex-end; + color: ${({ positive, theme }) => (positive ? theme.primary : theme.red)}; +` + +export const MobileTableRow = styled.div` + padding: 28px 24px 0; + cursor: pointer; + + :hover { + background: ${({ theme }) => rgba(theme.primary, 0.2)}; + } +` +export const MobileTableBottomRow = styled.div<{ withoutBorder: boolean }>` + display: grid; + grid-template-columns: 1.5fr 1fr 1fr; + padding: 16px 0; + border-bottom: ${({ withoutBorder, theme }) => (withoutBorder ? 'none' : `1px solid ${theme.tableHeader}`)}; +` + +export const Disclaimer = styled.div` + font-size: 14px; + font-style: italic; + color: #737373; + text-align: center; + width: 100%; + position: absolute; + bottom: 28px; + left: 50%; + transform: translateX(-50%); + padding: 0 16px; + + ${({ theme }) => theme.mediaWidth.upToMedium` + bottom: 20px; + `} +` diff --git a/src/pages/Earns/PoolExplorer/useFilter.ts b/src/pages/Earns/PoolExplorer/useFilter.ts new file mode 100644 index 0000000000..db7af5d70c --- /dev/null +++ b/src/pages/Earns/PoolExplorer/useFilter.ts @@ -0,0 +1,64 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' +import { useCallback, useMemo } from 'react' +import { useSearchParams } from 'react-router-dom' +import { PoolQueryParams, earnSupportedChains } from 'services/zapEarn' + +import { useActiveWeb3React } from 'hooks' +import { Direction } from 'pages/MarketOverview/SortIcon' + +import { FilterTag, SortBy, timings } from '.' + +export default function useFilter(setSearch?: (search: string) => void) { + const [searchParams, setSearchParams] = useSearchParams() + const { account, chainId } = useActiveWeb3React() + + const filters: PoolQueryParams = useMemo(() => { + return { + chainId: +( + searchParams.get('chainId') || (chainId && earnSupportedChains.includes(chainId) ? chainId : ChainId.MAINNET) + ), + page: +(searchParams.get('page') || 1), + limit: 10, + interval: searchParams.get('interval') || (timings[0].value as string), + protocol: searchParams.get('protocol') || '', + userAddress: account, + tag: searchParams.get('tag') || '', + sortBy: searchParams.get('sortBy') || (!searchParams.get('tag') ? SortBy.TVL : ''), + orderBy: searchParams.get('orderBy') || (!searchParams.get('tag') ? Direction.DESC : ''), + q: searchParams.get('q')?.trim() || '', + } + }, [searchParams, account, chainId]) + + const updateFilters = useCallback( + (key: keyof PoolQueryParams, value: string) => { + if (!value) { + searchParams.delete(key) + if (key === 'tag') { + searchParams.set('sortBy', SortBy.TVL) + searchParams.set('orderBy', Direction.DESC) + } + } else { + searchParams.set(key, value) + if (key === 'chainId') searchParams.delete('protocol') + if (key === 'tag') { + searchParams.delete('sortBy') + searchParams.delete('orderBy') + if (setSearch) setSearch('') + if (value === FilterTag.LOW_VOLATILITY) { + searchParams.set('sortBy', SortBy.APR) + searchParams.set('orderBy', Direction.DESC) + } + } + } + if (key !== 'sortBy' && key !== 'orderBy' && key !== 'page') searchParams.delete('page') + + setSearchParams(searchParams) + }, + [setSearchParams, searchParams, setSearch], + ) + + return { + filters, + updateFilters, + } +} diff --git a/src/pages/Earns/PositionDetail/Header.tsx b/src/pages/Earns/PositionDetail/Header.tsx new file mode 100644 index 0000000000..6e318e135a --- /dev/null +++ b/src/pages/Earns/PositionDetail/Header.tsx @@ -0,0 +1,86 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' +import { t } from '@lingui/macro' +import { useNavigate } from 'react-router-dom' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import { EarnSupportedProtocols, PositionStatus, earnSupportedProtocols } from 'services/zapEarn' + +import CopyHelper from 'components/Copy' +import { MouseoverTooltipDesktopOnly } from 'components/Tooltip' +import useTheme from 'hooks/useTheme' +import { MEDIA_WIDTHS } from 'theme' +import { shortenAddress } from 'utils' + +import { ParsedPosition } from '.' +import { CurrencyRoundedImage, CurrencySecondImage } from '../PoolExplorer/styles' +import { Badge, BadgeType, ChainImage, DexImage, ImageContainer, PositionOverview } from '../UserPositions/styles' +import { DexInfo, IconArrowLeft } from './styles' + +const PositionDetailHeader = ({ position }: { position: ParsedPosition }) => { + const theme = useTheme() + const navigate = useNavigate() + const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) + + const onOpenPositionInDexSite = () => { + if (!position || !earnSupportedProtocols.includes(position.dex)) return + + const chainName = + position.dex === EarnSupportedProtocols.UNISWAP_V3 && position.chainName === 'eth' + ? 'ethereum' + : position.chainName + + if (position.dex === EarnSupportedProtocols.UNISWAP_V3) + window.open(`https://app.uniswap.org/positions/v3/${chainName}/${position.id}`) + else if (position.dex === EarnSupportedProtocols.SUSHISWAP_V3) + window.open(`https://www.sushi.com/${chainName}/pool/v3/${position.poolAddress}/${position.id}`) + else if (position.dex === EarnSupportedProtocols.PANCAKESWAP_V3) + window.open(`https://pancakeswap.finance/liquidity/${position.id}`) + } + + return ( + + navigate(-1)} /> + + + + + + + + + {position.token0Symbol}/{position.token1Symbol} + + {position.poolFee && {position.poolFee}%} + + + + #{position.id} + + + ● {position.status === PositionStatus.IN_RANGE ? t`In range` : t`Out of range`} + + + + {position.poolAddress ? shortenAddress(position.chainId as ChainId, position.poolAddress, 4) : ''} + + + + + + + + {position.dex} + + + + + + + ) +} + +export default PositionDetailHeader diff --git a/src/pages/Earns/PositionDetail/LeftSection.tsx b/src/pages/Earns/PositionDetail/LeftSection.tsx new file mode 100644 index 0000000000..6ef31066b4 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LeftSection.tsx @@ -0,0 +1,138 @@ +import { t } from '@lingui/macro' +import { Flex, Text } from 'rebass' + +import HelpIcon from 'assets/svg/help-circle.svg' +import InfoHelper from 'components/InfoHelper' +import useTheme from 'hooks/useTheme' +import { formatDisplayNumber } from 'utils/numbers' + +import { ParsedPosition } from '.' +import { DexImage } from '../UserPositions/styles' +import { formatAprNumber } from '../utils' +import { InfoLeftColumn, InfoRight, InfoSection, InfoSectionFirstFormat, VerticalDivider } from './styles' + +const LeftSection = ({ position }: { position: ParsedPosition }) => { + const theme = useTheme() + + return ( + + + + {t`Total Liquidity`} + + + + {formatDisplayNumber(position.totalValue, { + style: 'currency', + significantDigits: 4, + })} + + + { + currentTarget.onerror = null // prevents looping + currentTarget.src = HelpIcon + }} + /> + {formatDisplayNumber(position.token0TotalAmount, { significantDigits: 6 })} + {position.token0Symbol} + + + { + currentTarget.onerror = null // prevents looping + currentTarget.src = HelpIcon + }} + /> + {formatDisplayNumber(position.token1TotalAmount, { significantDigits: 6 })} + {position.token1Symbol} + + + + + + + {t`Est. Position APR`} + + + + 0 ? theme.primary : theme.text}> + {formatAprNumber(position.apr * 100)}% + + + + + {t`Fee Earn`} + + + + + 1 {t`day`} + + + {position.earning24h || position.earning24h === 0 + ? formatDisplayNumber(position.earning24h, { significantDigits: 4, style: 'currency' }) + : '--'} + + + + + + 7 {t`days`} + + + {position.earning7d || position.earning7d === 0 + ? formatDisplayNumber(position.earning7d, { significantDigits: 4, style: 'currency' }) + : '--'} + + + + + + {t`All`} + + + {position.totalEarnedFee + ? formatDisplayNumber(position.totalEarnedFee, { style: 'currency', significantDigits: 4 }) + : '--'} + + + + + + + {t`Total Unclaimed Fee`} + + + + {formatDisplayNumber(position.totalUnclaimedFee, { style: 'currency', significantDigits: 4 })} + + + {formatDisplayNumber(position.token0UnclaimedAmount, { significantDigits: 4 })} + {position.token0Symbol} + + {formatDisplayNumber(position.token0UnclaimedValue, { + style: 'currency', + significantDigits: 4, + })} + + + + {formatDisplayNumber(position.token1UnclaimedAmount, { significantDigits: 4 })} + {position.token1Symbol} + + {formatDisplayNumber(position.token1UnclaimedValue, { + style: 'currency', + significantDigits: 4, + })} + + + + + + ) +} + +export default LeftSection diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/LiquidityChartRangeInput.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/LiquidityChartRangeInput.tsx new file mode 100644 index 0000000000..faa236d4f9 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/LiquidityChartRangeInput.tsx @@ -0,0 +1,122 @@ +import { format } from 'd3' +import { useCallback, useMemo } from 'react' +import styled from 'styled-components' + +import useTheme from 'hooks/useTheme' + +import { Chart } from './components/Chart' +import { InfoBox } from './components/InfoBox' +import Loader from './components/Loader' +import { Bound, ChartEntry, TickDataRaw, ZOOM_LEVELS, ZoomLevels } from './types' + +const Container = styled.div` + display: flex; + align-items: center; + justify-content: center; + min-height: 13rem; + gap: 1rem; + width: 100%; + margin-top: 8px; +` + +const ChartWrapper = styled.div` + position: relative; + display: flex; + align-items: center; + justify-content: center; + width: 76%; +` + +export default function LiquidityChartRangeInput({ + feeAmount, + ticksAtLimit = {}, + price, + priceLower, + priceUpper, + isLoading, + error, + zoomLevel, + formattedData, + isUninitialized, + revertPrice, +}: { + tickCurrent?: number + liquidity?: bigint + isLoading?: boolean + error?: Error + feeAmount?: number + ticks?: TickDataRaw[] + ticksAtLimit?: { [bound in Bound]?: boolean } + price?: number + priceLower?: string + priceUpper?: string + zoomLevel?: ZoomLevels + formattedData: ChartEntry[] | undefined + isUninitialized: boolean + revertPrice: boolean +}) { + const theme = useTheme() + const isSorted = !revertPrice + + const brushDomain: [number, number] | undefined = useMemo(() => { + if (!priceLower || !priceUpper) return undefined + + const leftPrice = !revertPrice ? priceLower : 1 / parseFloat(priceUpper) + const rightPrice = !revertPrice ? priceUpper : 1 / parseFloat(priceLower) + + if (leftPrice && rightPrice) { + const sortedPrices = [ + parseFloat(leftPrice.toString().replace(/,/g, '')), + parseFloat(rightPrice.toString().replace(/,/g, '')), + ].sort((a, b) => a - b) + return sortedPrices.length === 2 ? (sortedPrices as [number, number]) : undefined + } + return undefined + }, [priceLower, priceUpper, revertPrice]) + + // console.log('brushDomain', brushDomain) + + const onBrushDomainChangeEnded = useCallback((_domain: [number, number], _mode: string | undefined) => {}, []) + + const brushLabelValue = useCallback( + (d: 'w' | 'e', x: number) => { + if (!price) return '' + + if (d === 'w' && ticksAtLimit[isSorted ? Bound.LOWER : Bound.UPPER]) return '0' + if (d === 'e' && ticksAtLimit[isSorted ? Bound.UPPER : Bound.LOWER]) return '∞' + + const percent = (x < price ? -1 : 1) * ((Math.max(x, price) - Math.min(x, price)) / price) * 100 + + return price ? `${format(Math.abs(percent) > 1 ? '.2~s' : '.2~f')(percent)}%` : '' + }, + [isSorted, price, ticksAtLimit], + ) + + return ( + + {isUninitialized ? ( +
} /> + ) : isLoading ? ( + } /> + ) : error ? ( + } /> + ) : !formattedData || formattedData.length === 0 || !price ? ( + } /> + ) : ( + + + + )} + + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Area.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Area.tsx new file mode 100644 index 0000000000..100ae2b365 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Area.tsx @@ -0,0 +1,49 @@ +import { ScaleLinear, area, curveStepAfter } from 'd3' +import { useMemo } from 'react' + +import useTheme from 'hooks/useTheme' + +import { ChartEntry } from '../types' + +export const Area = ({ + series, + xScale, + yScale, + xValue, + yValue, + fill, + opacity, +}: { + series: ChartEntry[] + xScale: ScaleLinear + yScale: ScaleLinear + xValue: (d: ChartEntry) => number + yValue: (d: ChartEntry) => number + fill?: string | undefined + opacity?: number +}) => { + const theme = useTheme() + + return useMemo( + () => ( + xScale(xValue(d as ChartEntry))) + .y1((d: unknown) => yScale(yValue(d as ChartEntry))) + .y0(yScale(0))( + series.filter(d => { + const value = xScale(xValue(d)) + return value > 0 && value <= window.innerWidth + }) as Iterable<[number, number]>, + ) ?? undefined + } + /> + ), + [fill, opacity, series, xScale, xValue, yScale, yValue, theme.red], + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/AxisBottom.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/AxisBottom.tsx new file mode 100644 index 0000000000..4d66c48a81 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/AxisBottom.tsx @@ -0,0 +1,37 @@ +import { NumberValue, ScaleLinear, axisBottom, Axis as d3Axis, select } from 'd3' +import { useMemo } from 'react' + +import useTheme from 'hooks/useTheme' + +const Axis = ({ axisGenerator }: { axisGenerator: d3Axis }) => { + const theme = useTheme() + + const axisRef = (axis: SVGGElement) => { + axis && + select(axis) + .call(axisGenerator) + .call(g => g.select('.domain').attr('color', '#064E38').attr('stroke-width', 1.5)) + .call(g => g.selectAll('.tick line').attr('color', 'transparent')) + .call(g => g.selectAll('.tick text').attr('color', theme.subText)) + } + + return +} + +export const AxisBottom = ({ + xScale, + innerHeight, + offset = 0, +}: { + xScale: ScaleLinear + innerHeight: number + offset?: number +}) => + useMemo( + () => ( + + + + ), + [innerHeight, offset, xScale], + ) diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Brush.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Brush.tsx new file mode 100644 index 0000000000..5fad2012a1 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Brush.tsx @@ -0,0 +1,254 @@ +import { BrushBehavior, D3BrushEvent, ScaleLinear, brushX, select } from 'd3' +import { useCallback, useEffect, useMemo, useRef, useState } from 'react' +import styled from 'styled-components' + +import useTheme from 'hooks/useTheme' + +import { usePreviousValue } from '../hooks' +import { OffScreenHandle, brushHandlePath } from './svg' + +const StyledG = styled.g` + transition-property: opacity; + transition-timing-function: cubic-bezier(0.4, 0, 0.2, 1); + transition-duration: 300ms; + animation-duration: 300ms; +` + +// flips the handles draggers when close to the container edges +const FLIP_HANDLE_THRESHOLD_PX = 20 + +// margin to prevent tick snapping from putting the brush off screen +const BRUSH_EXTENT_MARGIN_PX = 2 + +/** + * Returns true if every element in `a` maps to the + * same pixel coordinate as elements in `b` + */ +const compare = (a: [number, number], b: [number, number], xScale: ScaleLinear): boolean => { + // normalize pixels to 1 decimals + const aNorm = a.map(x => xScale(x).toFixed(1)) + const bNorm = b.map(x => xScale(x).toFixed(1)) + return aNorm.every((v, i) => v === bNorm[i]) +} + +export const Brush = ({ + id, + xScale, + interactive, + brushLabelValue, + brushExtent, + setBrushExtent, + innerWidth, + innerHeight, +}: { + id: string + xScale: ScaleLinear + interactive: boolean + brushLabelValue: (d: 'w' | 'e', x: number) => string + brushExtent: [number, number] + setBrushExtent: (extent: [number, number], mode: string | undefined) => void + innerWidth: number + innerHeight: number +}) => { + const theme = useTheme() + const brushRef = useRef(null) + const brushBehavior = useRef | null>(null) + + // only used to drag the handles on brush for performance + const [localBrushExtent, setLocalBrushExtent] = useState<[number, number] | null>(brushExtent) + const [showLabels, setShowLabels] = useState(false) + const [hovering, setHovering] = useState(false) + + const previousBrushExtent = usePreviousValue(brushExtent) + + const brushed = useCallback( + (event: D3BrushEvent) => { + const { type, selection, mode } = event + + if (!selection) { + setLocalBrushExtent(null) + return + } + + const scaled = (selection as [number, number]).map(xScale.invert) as [number, number] + + // avoid infinite render loop by checking for change + if (type === 'end' && !compare(brushExtent, scaled, xScale)) { + setBrushExtent(scaled, mode) + } + + setLocalBrushExtent(scaled) + }, + [xScale, brushExtent, setBrushExtent], + ) + + // keep local and external brush extent in sync + // i.e. snap to ticks on bruhs end + useEffect(() => { + setLocalBrushExtent(brushExtent) + }, [brushExtent]) + + // initialize the brush + useEffect(() => { + if (!brushRef.current) return + + brushBehavior.current = brushX() + .extent([ + [Math.max(0 + BRUSH_EXTENT_MARGIN_PX, xScale(0)), 0], + [innerWidth - BRUSH_EXTENT_MARGIN_PX, innerHeight], + ]) + .handleSize(30) + .filter(() => interactive) + .on('brush end', brushed) + + brushBehavior.current(select(brushRef.current)) + + if (previousBrushExtent && compare(brushExtent, previousBrushExtent, xScale)) { + select(brushRef.current) + .transition() + .call(brushBehavior.current.move as any, brushExtent.map(xScale)) + } + + // brush linear gradient + select(brushRef.current) + .selectAll('.selection') + .attr('stroke', 'none') + .attr('fill-opacity', '0.1') + .attr('fill', `url(#${id}-gradient-selection)`) + .attr('cursor', 'default') + + select(brushRef.current).selectAll('.overlay').attr('cursor', 'default') + select(brushRef.current).selectAll('.handle').attr('cursor', 'default') + }, [brushExtent, brushed, id, innerHeight, innerWidth, interactive, previousBrushExtent, xScale]) + + // respond to xScale changes only + useEffect(() => { + if (!brushRef.current || !brushBehavior.current) return + + brushBehavior.current.move(select(brushRef.current) as any, brushExtent.map(xScale) as any) + }, [brushExtent, xScale]) + + // show labels when local brush changes + useEffect(() => { + setShowLabels(true) + const timeout = setTimeout(() => setShowLabels(false), 1500) + return () => clearTimeout(timeout) + }, [localBrushExtent]) + + // variables to help render the SVGs + const flipWestHandle = localBrushExtent && xScale(localBrushExtent[0]) > FLIP_HANDLE_THRESHOLD_PX + const flipEastHandle = localBrushExtent && xScale(localBrushExtent[1]) > innerWidth - FLIP_HANDLE_THRESHOLD_PX + + const showWestArrow = localBrushExtent && (xScale(localBrushExtent[0]) < 0 || xScale(localBrushExtent[1]) < 0) + const showEastArrow = + localBrushExtent && (xScale(localBrushExtent[0]) > innerWidth || xScale(localBrushExtent[1]) > innerWidth) + + const westHandleInView = + localBrushExtent && xScale(localBrushExtent[0]) >= 0 && xScale(localBrushExtent[0]) <= innerWidth + const eastHandleInView = + localBrushExtent && xScale(localBrushExtent[1]) >= 0 && xScale(localBrushExtent[1]) <= innerWidth + + return useMemo( + () => ( + <> + setHovering(true)} + onMouseLeave={() => setHovering(false)} + /> + + {localBrushExtent && ( + <> + {westHandleInView ? ( + + + + + + + + + + {brushLabelValue('w', localBrushExtent[0])} + + + + ) : null} + + {eastHandleInView ? ( + + + + + + + + + + {brushLabelValue('e', localBrushExtent[1])} + + + + ) : null} + + {showWestArrow && } + + {showEastArrow && ( + + + + )} + + )} + + ), + [ + id, + innerWidth, + innerHeight, + localBrushExtent, + westHandleInView, + xScale, + flipWestHandle, + theme.primary, + theme.subText, + showLabels, + hovering, + brushLabelValue, + eastHandleInView, + flipEastHandle, + showWestArrow, + showEastArrow, + ], + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Chart.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Chart.tsx new file mode 100644 index 0000000000..d242e5edb3 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Chart.tsx @@ -0,0 +1,187 @@ +import { ZoomTransform, max, scaleLinear } from 'd3' +import partition from 'lodash/partition' +import { useEffect, useMemo, useState } from 'react' + +import useTheme from 'hooks/useTheme' + +import { Bound, ChartEntry, LiquidityChartRangeInputProps } from '../types' +import { Area } from './Area' +import { AxisBottom } from './AxisBottom' +import { Brush } from './Brush' +import { Line } from './Line' +import Zoom from './Zoom' + +const xAccessor = (d: ChartEntry) => d.price0 +const yAccessor = (d: ChartEntry) => d.activeLiquidity + +export function Chart({ + id = 'liquidityChartRangeInput', + data: { series, current }, + ticksAtLimit, + dimensions: { width, height }, + margins, + brushDomain, + brushLabels, + onBrushDomainChange, + zoomLevels, + showZoomButtons = true, +}: LiquidityChartRangeInputProps) { + const theme = useTheme() + + const [zoom, setZoom] = useState(null) + + const [innerHeight, innerWidth] = useMemo( + () => [height - margins.top - margins.bottom, width - margins.left - margins.right], + [width, height, margins], + ) + + const { xScale, yScale } = useMemo(() => { + const scales = { + xScale: scaleLinear() + .domain([current * zoomLevels.initialMin, current * zoomLevels.initialMax] as number[]) + .range([0, innerWidth]), + yScale: scaleLinear() + .domain([0, max(series, yAccessor)] as number[]) + .range([innerHeight, 0]), + } + + if (zoom) { + const newXscale = zoom.rescaleX(scales.xScale) + scales.xScale.domain(newXscale.domain()) + } + + return scales + }, [current, zoomLevels.initialMin, zoomLevels.initialMax, innerWidth, series, innerHeight, zoom]) + + useEffect(() => { + // reset zoom as necessary + setZoom(null) + }, [zoomLevels]) + + const [leftSeries, rightSeries] = useMemo(() => { + const isHighToLow = series[0]?.price0 > series[series.length - 1]?.price0 + let [left, right] = partition(series, (d: ChartEntry) => + isHighToLow ? +xAccessor(d) < current : +xAccessor(d) > current, + ) + + if (right.length && right[right.length - 1]) { + if (right[right.length - 1].price0 !== current) { + right = [ + ...right, + { + activeLiquidity: right[right.length - 1].activeLiquidity, + price0: current, + }, + ] + } + left = [ + { + activeLiquidity: right[right.length - 1].activeLiquidity, + price0: current, + }, + ...left, + ] + } + + return [left, right] + }, [current, series]) + + return ( + <> + {showZoomButtons && ( + { + onBrushDomainChange( + [current * zoomLevels.initialMin, current * zoomLevels.initialMax] as [number, number], + 'reset', + ) + }} + showResetButton={Boolean(ticksAtLimit[Bound.LOWER] || ticksAtLimit[Bound.UPPER])} + zoomLevels={zoomLevels} + /> + )} + + + + + + + {brushDomain && ( + // mask to highlight selected area + + + + )} + + + + + + + + {brushDomain && ( + // duplicate area chart with mask for selected area + + + + )} + + + + + + + + + + + + + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/InfoBox.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/InfoBox.tsx new file mode 100644 index 0000000000..a657ecb103 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/InfoBox.tsx @@ -0,0 +1,27 @@ +import { ReactNode } from 'react' +import styled from 'styled-components' + +const Box = styled.div` + height: 100%; + display: flex; + justify-content: center; + align-items: center; +` + +const Message = styled.div` + font-weight: 700; + font-size: 1.25rem; + line-height: 1.75rem; + text-align: center; + padding-top: 4px; + color: ${({ theme }) => theme.subText}; +` + +export function InfoBox({ message, icon }: { message?: ReactNode; icon: ReactNode }) { + return ( + + {icon} + {message && {message}} + + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Line.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Line.tsx new file mode 100644 index 0000000000..e231a89015 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Line.tsx @@ -0,0 +1,24 @@ +import { ScaleLinear } from 'd3' +import { useMemo } from 'react' +import styled from 'styled-components' + +const StyledLine = styled.line` + opacity: 0.5; + stroke-width: 2; + stroke: ${({ theme }) => theme.white}; +` + +export const Line = ({ + value, + xScale, + innerHeight, +}: { + value: number + xScale: ScaleLinear + innerHeight: number +}) => { + return useMemo( + () => , + [value, xScale, innerHeight], + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Loader.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Loader.tsx new file mode 100644 index 0000000000..eb7cb968a0 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Loader.tsx @@ -0,0 +1,40 @@ +import useTheme from 'hooks/useTheme' + +/** + * Takes in custom size and stroke for circle color, default to primary color as fill, + * need ...rest for layered styles on top + */ +// TODO: v3 merge with UIkit strokeWidth +export default function Loader({ + size = '16px', + stroke, + strokeWidth, + ...rest +}: { + size?: string + stroke?: string + strokeWidth?: number + [k: string]: unknown +}) { + const theme = useTheme() + + return ( + + + + ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/Zoom.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/Zoom.tsx new file mode 100644 index 0000000000..211ad4dedb --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/Zoom.tsx @@ -0,0 +1,126 @@ +import { ScaleLinear, ZoomBehavior, ZoomTransform, select, zoom, zoomIdentity } from 'd3' +import { useEffect, useMemo, useRef } from 'react' +import { MinusCircle, PlusCircle } from 'react-feather' +import styled from 'styled-components' + +import { ZoomLevels } from '../types' + +const Container = styled.div<{ showResetButton: boolean }>` + display: grid; + gap: 8px; + position: absolute; + bottom: 38px; + right: 8px; + grid-template-columns: 2fr 1fr; + ${({ showResetButton }) => + showResetButton + ? 'grid-template-columns: repeat(3, minmax(0, 1fr))' + : 'grid-template-columns: repeat(2, minmax(0, 1fr))'} +` + +const ResetButton = styled.div` + cursor: pointer; + padding-top: 2px; + padding-left: 1px; +` + +const ZoomAction = styled.div` + cursor: pointer; + color: #f3f8f7; +` + +export default function Zoom({ + xScale, + setZoom, + width, + height, + resetBrush, + showResetButton, + zoomLevels, +}: { + xScale: ScaleLinear + setZoom: (transform: ZoomTransform) => void + width: number + height: number + resetBrush: () => void + showResetButton: boolean + zoomLevels: ZoomLevels +}) { + const zoomBehavior = useRef>() + const svgRef = useRef(null) + + const [zoomIn, zoomOut, zoomInitial, zoomReset] = useMemo( + () => [ + () => + svgRef.current && + zoomBehavior.current && + select(svgRef.current as Element) + .transition() + .call(zoomBehavior.current.scaleBy, 2), + () => + svgRef.current && + zoomBehavior.current && + select(svgRef.current as Element) + .transition() + .call(zoomBehavior.current.scaleBy, 0.5), + () => + svgRef.current && + zoomBehavior.current && + select(svgRef.current as Element) + .transition() + .call(zoomBehavior.current.scaleTo, 0.5), + () => + svgRef.current && + zoomBehavior.current && + select(svgRef.current as Element) + .call(zoomBehavior.current.transform, zoomIdentity.translate(0, 0).scale(1)) + .transition() + .call(zoomBehavior.current.scaleTo, 0.5), + ], + [svgRef], + ) + + useEffect(() => { + const zoomRefEl = document.querySelector('.zoomRef') + if (!zoomRefEl) return + svgRef.current = zoomRefEl as SVGElement + + zoomBehavior.current = zoom() + .scaleExtent([zoomLevels.min, zoomLevels.max]) + .extent([ + [0, 0], + [width, height], + ]) + .on('zoom', ({ transform }: { transform: ZoomTransform }) => setZoom(transform)) + + select(svgRef.current as Element).call(zoomBehavior.current) + }, [height, width, setZoom, svgRef, xScale, zoomBehavior, zoomLevels, zoomLevels.max, zoomLevels.min]) + + useEffect(() => { + // reset zoom to initial on zoomLevel change + zoomInitial() + }, [zoomInitial, zoomLevels]) + + return ( + + {showResetButton && ( + +
{ + resetBrush() + zoomReset() + }} + > + AutoRenewIcon +
+
+ )} + + + + + + +
+ ) +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/components/svg.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/components/svg.tsx new file mode 100644 index 0000000000..a1624e86dd --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/components/svg.tsx @@ -0,0 +1,46 @@ +/* + * Generates an SVG path for the east brush handle. + * Apply `scale(-1, 1)` to generate west brush handle. + * + * |```````\ + * | | | | + * |______/ + * | + * | + * | + * | + * | + * + * https://medium.com/@dennismphil/one-side-rounded-rectangle-using-svg-fb31cf318d90 + */ +export const brushHandlePath = (height: number) => + [`M 0 0`, `v 8`, `C -6 8 -6 18 0 18`, `v ${height - 18}`, `m 0 1`, `V 18`, `C 7 18 7 8 1 8`, `V 0`, `M 0 1`].join(' ') + +export const brushHandleAccentPath = () => + [ + 'm 5 7', // move to first accent + 'v 14', // vertical line + 'M 0 0', // move to origin + 'm 9 7', // move to second accent + 'v 14', // vertical line + 'z', + ].join(' ') + +export const OffScreenHandle = ({ + color, + size = 10, + margin = 10, +}: { + color: string + size?: number + margin?: number +}) => ( + +) diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/hooks.ts b/src/pages/Earns/PositionDetail/LiquidityChart/hooks.ts new file mode 100644 index 0000000000..3d73a723c9 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/hooks.ts @@ -0,0 +1,111 @@ +import { useEffect, useMemo, useRef } from 'react' +import { PoolDetail } from 'services/poolService' + +import { ChartEntry, PRICE_FIXED_DIGITS, TickProcessed } from './types' +import { tickToPrice } from './uniswapv3' +import { computeSurroundingTicks } from './utils' + +export const useDensityChartData = ({ pool, revertPrice }: { pool: PoolDetail | undefined; revertPrice: boolean }) => { + const ticksProcessed = usePoolActiveLiquidity({ + pool, + revertPrice, + }) + + const chartData = useMemo(() => { + if (!ticksProcessed.length) { + return undefined + } + + const newData: ChartEntry[] = [] + + for (let i = 0; i < ticksProcessed.length; i++) { + const t = ticksProcessed[i] + + const chartEntry = { + activeLiquidity: parseFloat(t.liquidityActive.toString()), + price0: parseFloat(t.price0), + } + + if (chartEntry.activeLiquidity > 0) { + newData.push(chartEntry) + } + } + + return newData + }, [ticksProcessed]) + + return chartData +} + +export const usePoolActiveLiquidity = ({ + pool, + revertPrice, +}: { + pool: PoolDetail | undefined + revertPrice: boolean +}) => { + const isPoolAvailable = !!pool && Object.keys(pool).length > 0 + const tickCurrent = isPoolAvailable ? pool.positionInfo.tick : undefined + const tickSpacing = isPoolAvailable ? pool.positionInfo.tickSpacing : undefined + + return useMemo(() => { + if (!isPoolAvailable || (!tickCurrent && tickCurrent !== 0) || !tickSpacing) return [] + + const activeTick = Math.floor(tickCurrent / tickSpacing) * tickSpacing + const ticks = pool.positionInfo.ticks || [] + + // find where the active tick would be to partition the array + // if the active tick is initialized, the pivot will be an element + // if not, take the previous tick as pivot + const pivot = ticks.findIndex(({ index: tick }) => Number(tick) > activeTick) - 1 + + if (pivot < 0) { + // consider setting a local error + // TickData pivot not found + return [] + } + + const activeTickProcessed: TickProcessed = { + liquidityActive: BigInt(pool.positionInfo.liquidity), + tick: activeTick, + liquidityNet: Number(ticks[pivot].index) === activeTick ? BigInt(ticks[pivot].liquidityNet) : 0n, + price0: Number(tickToPrice(activeTick, pool.tokens[0].decimals, pool.tokens[1].decimals, revertPrice)).toFixed( + PRICE_FIXED_DIGITS, + ), + } + + const subsequentTicks = computeSurroundingTicks( + pool.tokens[revertPrice ? 1 : 0].decimals, + pool.tokens[revertPrice ? 0 : 1].decimals, + activeTickProcessed, + ticks, + pivot, + true, + ) + const previousTicks = computeSurroundingTicks( + pool.tokens[revertPrice ? 1 : 0].decimals, + pool.tokens[revertPrice ? 0 : 1].decimals, + activeTickProcessed, + ticks, + pivot, + false, + ) + const ticksProcessed = previousTicks.concat(activeTickProcessed).concat(subsequentTicks) + + return ticksProcessed + }, [pool, revertPrice, tickCurrent, tickSpacing, isPoolAvailable]) +} + +export const usePreviousValue = (value?: TValue): TValue | undefined => { + const prevValue = useRef() + + useEffect(() => { + prevValue.current = value + + return () => { + prevValue.current = undefined + } + }) + + return prevValue.current +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/index.tsx b/src/pages/Earns/PositionDetail/LiquidityChart/index.tsx new file mode 100644 index 0000000000..f832ebcbad --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/index.tsx @@ -0,0 +1,46 @@ +import { usePoolDetailQuery } from 'services/poolService' + +import LiquidityChartRangeInput from './LiquidityChartRangeInput' +import { useDensityChartData } from './hooks' + +const LiquidityChart = ({ + chainId, + poolAddress, + price, + minPrice, + maxPrice, + revertPrice, +}: { + chainId: number + poolAddress: string + price: number + minPrice: number + maxPrice: number + revertPrice: boolean +}) => { + const { data: pool } = usePoolDetailQuery({ chainId, ids: poolAddress }) + const chartData = useDensityChartData({ pool, revertPrice }) + + const isUninitialized = !pool || Object.keys(pool).length === 0 + const tickSpacing = isUninitialized ? undefined : pool.positionInfo.tickSpacing + const fee = isUninitialized ? undefined : pool.swapFee * 10_000 + + if (!tickSpacing) return null + + return ( + + ) +} + +export default LiquidityChart diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/types.ts b/src/pages/Earns/PositionDetail/LiquidityChart/types.ts new file mode 100644 index 0000000000..fac8f7f3c3 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/types.ts @@ -0,0 +1,119 @@ +type BigintIsh = bigint | number | string + +export enum FeeAmount { + LOWEST = 100, + LOW = 500, + MIDDLE = 2500, // For Pancake temporary + MEDIUM = 3000, + HIGH = 10000, +} + +/** + * The default factory tick spacings by fee amount. + */ +export const TICK_SPACINGS = { + [FeeAmount.LOWEST]: 1, + [FeeAmount.LOW]: 10, + [FeeAmount.MEDIUM]: 50, + [FeeAmount.HIGH]: 200, +} + +export const PRICE_FIXED_DIGITS = 8 + +export interface TickProcessed { + tick: number + liquidityActive: bigint + liquidityNet: bigint + price0: string +} + +export interface TickDataRaw { + index: string | number + liquidityGross: BigintIsh + liquidityNet: BigintIsh +} + +export interface ChartEntry { + activeLiquidity: number + price0: number +} + +export enum Bound { + LOWER = 'LOWER', + UPPER = 'UPPER', +} + +interface Dimensions { + width: number + height: number +} + +interface Margins { + top: number + right: number + bottom: number + left: number +} + +export interface ZoomLevels { + initialMin: number + initialMax: number + min: number + max: number +} + +export interface LiquidityChartRangeInputProps { + // to distringuish between multiple charts in the DOM + id?: string + + data: { + series: ChartEntry[] + current: number + } + ticksAtLimit: { [bound in Bound]?: boolean | undefined } + + dimensions: Dimensions + margins: Margins + + interactive?: boolean + + brushLabels: (d: 'w' | 'e', x: number) => string + brushDomain: [number, number] | undefined + onBrushDomainChange: (domain: [number, number], mode: string | undefined) => void + + zoomLevels: ZoomLevels + showZoomButtons?: boolean +} + +export const ZOOM_LEVELS: Record = { + [FeeAmount.LOWEST]: { + initialMin: 0.999, + initialMax: 1.001, + min: 0.00001, + max: 1.5, + }, + [FeeAmount.LOW]: { + initialMin: 0.8, + initialMax: 1.2, + min: 0.00001, + max: 20, + }, + [FeeAmount.MIDDLE]: { + initialMin: 0.3, + initialMax: 1.8, + min: 0.00001, + max: 20, + }, + [FeeAmount.MEDIUM]: { + initialMin: 0.3, + initialMax: 1.8, + min: 0.00001, + max: 20, + }, + [FeeAmount.HIGH]: { + initialMin: 0.1, + initialMax: 2, + min: 0.00001, + max: 20, + }, +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/uniswapv3.ts b/src/pages/Earns/PositionDetail/LiquidityChart/uniswapv3.ts new file mode 100644 index 0000000000..02da1dda0b --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/uniswapv3.ts @@ -0,0 +1,344 @@ +const MaxUint256 = BigInt('0xffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff') + +// TODO: check if other dex type have the same +// The minimum tick that can be used on any pool. +export const MIN_TICK = -887272 +// The maximum tick that can be used on any pool. +export const MAX_TICK: number = -MIN_TICK +const MIN_SQRT_RATIO = 4295128739n +const MAX_SQRT_RATIO = 1461446703485210103287273052203988822378723970342n + +const Q96: bigint = 2n ** 96n // 2^96 as BigInt +const Q32: bigint = 2n ** 32n +const Q192: bigint = Q96 ** 2n + +function mulShift(val: bigint, mulBy: string): bigint { + return (val * BigInt(mulBy)) >> 128n +} + +// Function to convert tick to sqrt(price) Q96 +export function getSqrtRatioAtTick(tick: number): bigint { + if (tick < MIN_TICK || tick > MAX_TICK || !Number.isInteger(tick)) { + throw new Error(`TICK ${tick}: must be within bounds MIN_TICK and MAX_TICK`) + } + const absTick: number = tick < 0 ? tick * -1 : tick + + let ratio: bigint = + (absTick & 0x1) != 0 ? BigInt('0xfffcb933bd6fad37aa2d162d1a594001') : BigInt('0x100000000000000000000000000000000') + if ((absTick & 0x2) != 0) ratio = mulShift(ratio, '0xfff97272373d413259a46990580e213a') + if ((absTick & 0x4) != 0) ratio = mulShift(ratio, '0xfff2e50f5f656932ef12357cf3c7fdcc') + if ((absTick & 0x8) != 0) ratio = mulShift(ratio, '0xffe5caca7e10e4e61c3624eaa0941cd0') + if ((absTick & 0x10) != 0) ratio = mulShift(ratio, '0xffcb9843d60f6159c9db58835c926644') + if ((absTick & 0x20) != 0) ratio = mulShift(ratio, '0xff973b41fa98c081472e6896dfb254c0') + if ((absTick & 0x40) != 0) ratio = mulShift(ratio, '0xff2ea16466c96a3843ec78b326b52861') + if ((absTick & 0x80) != 0) ratio = mulShift(ratio, '0xfe5dee046a99a2a811c461f1969c3053') + if ((absTick & 0x100) != 0) ratio = mulShift(ratio, '0xfcbe86c7900a88aedcffc83b479aa3a4') + if ((absTick & 0x200) != 0) ratio = mulShift(ratio, '0xf987a7253ac413176f2b074cf7815e54') + if ((absTick & 0x400) != 0) ratio = mulShift(ratio, '0xf3392b0822b70005940c7a398e4b70f3') + if ((absTick & 0x800) != 0) ratio = mulShift(ratio, '0xe7159475a2c29b7443b29c7fa6e889d9') + if ((absTick & 0x1000) != 0) ratio = mulShift(ratio, '0xd097f3bdfd2022b8845ad8f792aa5825') + if ((absTick & 0x2000) != 0) ratio = mulShift(ratio, '0xa9f746462d870fdf8a65dc1f90e061e5') + if ((absTick & 0x4000) != 0) ratio = mulShift(ratio, '0x70d869a156d2a1b890bb3df62baf32f7') + if ((absTick & 0x8000) != 0) ratio = mulShift(ratio, '0x31be135f97d08fd981231505542fcfa6') + if ((absTick & 0x10000) != 0) ratio = mulShift(ratio, '0x9aa508b5b7a84e1c677de54f3e99bc9') + if ((absTick & 0x20000) != 0) ratio = mulShift(ratio, '0x5d6af8dedb81196699c329225ee604') + if ((absTick & 0x40000) != 0) ratio = mulShift(ratio, '0x2216e584f5fa1ea926041bedfe98') + if ((absTick & 0x80000) != 0) ratio = mulShift(ratio, '0x48a170391f7dc42444e8fa2') + + if (tick > 0) ratio = MaxUint256 / ratio + + // back to Q96 + return ratio % Q32 > 0n ? ratio / Q32 + 1n : ratio / Q32 +} + +const TWO = 2n +const POWERS_OF_2 = [128, 64, 32, 16, 8, 4, 2, 1].map((pow: number): [number, bigint] => [pow, TWO ** BigInt(pow)]) + +export function mostSignificantBit(x: bigint): number { + if (x <= 0) throw new Error('x must be greater than 0') + if (x > MaxUint256) throw new Error('x must be less than MaxUint256') + + let msb = 0 + for (const [power, min] of POWERS_OF_2) { + if (x >= min) { + // eslint-disable-next-line operator-assignment + x = x >> BigInt(power) + msb += power + } + } + return msb +} + +function getTickAtSqrtRatio(sqrtRatioX96: bigint): number { + if (sqrtRatioX96 < MIN_SQRT_RATIO || sqrtRatioX96 > MAX_SQRT_RATIO) { + throw new Error('SQRT_RATIO') + } + + const sqrtRatioX128 = sqrtRatioX96 << 32n + + const msb = mostSignificantBit(sqrtRatioX128) + + let r: bigint + if (BigInt(msb) >= 128n) { + r = sqrtRatioX128 >> BigInt(msb - 127) + } else { + r = sqrtRatioX128 << BigInt(127 - msb) + } + + let log_2: bigint = (BigInt(msb) - 128n) << 64n + + for (let i = 0; i < 14; i++) { + r = (r * r) >> 127n + const f = r >> 128n + // eslint-disable-next-line operator-assignment + log_2 = log_2 | (f << BigInt(63 - i)) + // eslint-disable-next-line operator-assignment + r = r >> f + } + + const log_sqrt10001 = log_2 * 255738958999603826347141n + + const tickLow = Number((log_sqrt10001 - 3402992956809132418596140100660247210n) >> 128n) + const tickHigh = Number((log_sqrt10001 + 291339464771989622907027621153398088495n) >> 128n) + + return tickLow === tickHigh ? tickLow : getSqrtRatioAtTick(tickHigh) <= sqrtRatioX96 ? tickHigh : tickLow +} + +function mulDivRoundingUp(a: bigint, b: bigint, denominator: bigint): bigint { + const product = a * b + let result = product / denominator + // eslint-disable-next-line operator-assignment + if (product % denominator !== 0n) result = result + 1n + return result +} + +function getAmount0Delta(sqrtRatioAX96: bigint, sqrtRatioBX96: bigint, liquidity: bigint, roundUp: boolean): bigint { + if (sqrtRatioAX96 > sqrtRatioBX96) { + ;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96] + } + + const numerator1 = liquidity << 96n + const numerator2 = sqrtRatioBX96 - sqrtRatioAX96 + + return roundUp + ? mulDivRoundingUp(mulDivRoundingUp(numerator1, numerator2, sqrtRatioBX96), 1n, sqrtRatioAX96) + : (numerator1 * numerator2) / sqrtRatioBX96 / sqrtRatioAX96 +} + +function getToken0Amount( + tickCurrent: number, + tickLower: number, + tickUpper: number, + sqrtRatioX96: bigint, + liquidity: bigint, +): bigint { + if (tickCurrent < tickLower) { + return getAmount0Delta(getSqrtRatioAtTick(tickLower), getSqrtRatioAtTick(tickUpper), liquidity, false) + } + if (tickCurrent < tickUpper) { + return getAmount0Delta(sqrtRatioX96, getSqrtRatioAtTick(tickUpper), liquidity, false) + } + return 0n +} + +function getAmount1Delta(sqrtRatioAX96: bigint, sqrtRatioBX96: bigint, liquidity: bigint, roundUp: boolean): bigint { + if (sqrtRatioAX96 > sqrtRatioBX96) { + ;[sqrtRatioAX96, sqrtRatioBX96] = [sqrtRatioBX96, sqrtRatioAX96] + } + + return roundUp + ? mulDivRoundingUp(liquidity, sqrtRatioBX96 - sqrtRatioAX96, Q96) + : (liquidity * (sqrtRatioBX96 - sqrtRatioAX96)) / Q96 +} + +function getToken1Amount( + tickCurrent: number, + tickLower: number, + tickUpper: number, + sqrtRatioX96: bigint, + liquidity: bigint, +): bigint { + if (tickCurrent < tickLower) { + return 0n + } + if (tickCurrent < tickUpper) { + return getAmount1Delta(getSqrtRatioAtTick(tickLower), sqrtRatioX96, liquidity, false) + } + return getAmount1Delta(getSqrtRatioAtTick(tickLower), getSqrtRatioAtTick(tickUpper), liquidity, false) +} + +export function getPositionAmounts( + tickCurrent: number, + tickLower: number, + tickUpper: number, + sqrtRatioX96: bigint, + liquidity: bigint, +) { + return { + amount0: getToken0Amount(tickCurrent, tickLower, tickUpper, sqrtRatioX96, liquidity), + amount1: getToken1Amount(tickCurrent, tickLower, tickUpper, sqrtRatioX96, liquidity), + } +} + +/* +struct Position { + uint96 nonce; + address operator; + address token0; + address token1; + uint24 fee; + int24 tickLower; + int24 tickUpper; + uint128 liquidity; + uint256 feeGrowthInside0LastX128; + uint256 feeGrowthInside1LastX128; + uint128 tokensOwed0; + uint128 tokensOwed1; +} +*/ +export function decodePosition(rawData: string) { + // Remove the "0x" prefix + const hexData = rawData.slice(2) + + // Decode fields according to the ABI layout + const nonce = decodeUint(hexData.slice(0, 64)) // uint96: first 12 bytes and padding (64 hex chars) + const operator = decodeAddress(hexData.slice(64, 128)) // address: next 32 bytes (64 hex chars) + const token0 = decodeAddress(hexData.slice(128, 192)) // address: next 32 bytes (64 hex chars) + const token1 = decodeAddress(hexData.slice(192, 256)) // address: next 32 bytes (64 hex chars) + const fee = parseInt(hexData.slice(256, 320), 16) // uint24: next 32 bytes (64 hex chars) + const tickLower = decodeInt24(hexData.slice(320, 384)) + const tickUpper = decodeInt24(hexData.slice(384, 448)) + const liquidity = decodeUint(hexData.slice(448, 512)) + const feeGrowthInside0LastX128 = decodeUint(hexData.slice(512, 576)) + const feeGrowthInside1LastX128 = decodeUint(hexData.slice(576, 640)) + const tokensOwed0 = decodeUint(hexData.slice(640, 704)) + const tokensOwed1 = decodeUint(hexData.slice(704, 768)) + + return { + nonce, + operator, + token0, + token1, + fee, + tickLower, + tickUpper, + liquidity, + feeGrowthInside0LastX128, + feeGrowthInside1LastX128, + tokensOwed0, + tokensOwed1, + } +} + +export function tickToPrice(tick: number, baseDecimal: number, quoteDecimal: number, revert = false): string { + const sqrtRatioX96 = getSqrtRatioAtTick(tick) + const ratioX192 = sqrtRatioX96 * sqrtRatioX96 // 1.0001 ** tick * Q192 + + const numerator = ratioX192 * 10n ** BigInt(baseDecimal) + const denominator = Q192 * 10n ** BigInt(quoteDecimal) + + return revert ? divideBigIntToString(denominator, numerator, 18) : divideBigIntToString(numerator, denominator, 18) +} + +function sqrt(y: bigint): bigint { + if (y < 0n) { + throw new Error('sqrt: negative value') + } + let z = 0n + let x: bigint + if (y > 3n) { + z = y + x = y / 2n + 1n + while (x < z) { + z = x + x = (y / x + x) / 2n + } + } else if (y !== 0n) { + z = 1n + } + return z +} + +function encodeSqrtRatioX96(amount1: bigint, amount0: bigint): bigint { + const numerator = BigInt(amount1) << 192n + const denominator = BigInt(amount0) + const ratioX192 = numerator / denominator + return sqrt(ratioX192) +} + +export function priceToClosestTick( + value: string, + token0Decimal: number, + token1Decimal: number, + revert = false, +): number | undefined { + if (!value.match(/^\d*\.?\d+$/)) { + return undefined + } + const [whole, fraction] = value.split('.') + + const decimals = fraction?.length ?? 0 + const withoutDecimals = BigInt((whole ?? '') + (fraction ?? '')) + + const denominator = BigInt(10 ** decimals) * 10n ** BigInt(revert ? token1Decimal : token0Decimal) + const numerator = withoutDecimals * 10n ** BigInt(revert ? token0Decimal : token1Decimal) + + //const sqrtRatioX96 = encodeSqrtRatioX96(numerator, denominator); + const sqrtRatioX96 = !revert ? encodeSqrtRatioX96(numerator, denominator) : encodeSqrtRatioX96(denominator, numerator) + + let tick = getTickAtSqrtRatio(sqrtRatioX96) + const nextTickPrice = tickToPrice(tick + 1, token0Decimal, token1Decimal, revert) + + if (!revert) { + if (+value >= +nextTickPrice) { + tick++ + } + } else if (+value <= +nextTickPrice) { + tick++ + } + return tick +} + +export function nearestUsableTick(tick: number, tickSpacing: number) { + if (!Number.isInteger(tick) || !Number.isInteger(tickSpacing)) throw new Error('INTEGERS') + if (tickSpacing <= 0) throw new Error('TICK_SPACING') + if (tick < MIN_TICK || tick > MAX_TICK) throw new Error('TICK_BOUND') + const rounded = Math.round(tick / tickSpacing) * tickSpacing + if (rounded < MIN_TICK) return rounded + tickSpacing + if (rounded > MAX_TICK) return rounded - tickSpacing + return rounded +} + +function divideBigIntToString(numerator: bigint, denominator: bigint, decimalPlaces: number): string { + const integerPart = numerator / denominator + // Calculate the remainder and use it to find decimal places + let remainder = numerator % denominator + let decimalStr = '' + + for (let i = 0; i < decimalPlaces; i++) { + remainder *= 10n + const digit = remainder / denominator + decimalStr += digit.toString() + remainder %= denominator + } + + return `${integerPart.toString()}.${decimalStr}` +} + +// Function to decode an Ethereum address from hex (last 20 bytes of a 32-byte field) +export function decodeAddress(hex: string): string { + return `0x${hex.slice(-40)}` // Last 20 bytes = 40 hex chars +} + +// Decode a uint256 (or smaller) field from the hex string +export function decodeUint(hex: string): bigint { + return BigInt(`0x${hex}`) +} + +// Function to decode an int24 from hex (handles negative values) +export function decodeInt24(hex: string): number { + const last3Bytes = hex.slice(-6) + const int = parseInt(last3Bytes, 16) + // If the int is greater than the max signed value for 24 bits (2^23), it’s negative + return int >= 0x800000 ? int - 0x1000000 : int +} diff --git a/src/pages/Earns/PositionDetail/LiquidityChart/utils.ts b/src/pages/Earns/PositionDetail/LiquidityChart/utils.ts new file mode 100644 index 0000000000..df9a365164 --- /dev/null +++ b/src/pages/Earns/PositionDetail/LiquidityChart/utils.ts @@ -0,0 +1,51 @@ +import { PRICE_FIXED_DIGITS, TickDataRaw, TickProcessed } from './types' +import { tickToPrice } from './uniswapv3' + +// Computes the numSurroundingTicks above or below the active tick. +export const computeSurroundingTicks = ( + token0decimals: number, + token1decimal: number, + activeTickProcessed: TickProcessed, + sortedTickData: TickDataRaw[], + pivot: number, + ascending: boolean, +): TickProcessed[] => { + let previousTickProcessed: TickProcessed = { + ...activeTickProcessed, + } + + // Iterate outwards (either up or down depending on direction) from the active tick, + // building active liquidity for every tick. + let processedTicks: TickProcessed[] = [] + + for (let i = pivot + (ascending ? 1 : -1); ascending ? i < sortedTickData.length : i >= 0; ascending ? i++ : i--) { + const tick = Number(sortedTickData[i].index) + const currentTickProcessed: TickProcessed = { + liquidityActive: previousTickProcessed.liquidityActive, + tick, + liquidityNet: BigInt(sortedTickData[i].liquidityNet), + price0: Number(tickToPrice(tick, token0decimals, token1decimal)).toFixed(PRICE_FIXED_DIGITS), + } + + // Update the active liquidity. + // If we are iterating ascending and we found an initialized tick we immediately apply + // it to the current processed tick we are building. + // If we are iterating descending, we don't want to apply the net liquidity until the following tick. + if (ascending) { + currentTickProcessed.liquidityActive = + previousTickProcessed.liquidityActive + BigInt(sortedTickData[i].liquidityNet) + } else if (!ascending && previousTickProcessed.liquidityNet !== 0n) { + // We are iterating descending, so look at the previous tick and apply any net liquidity. + currentTickProcessed.liquidityActive = previousTickProcessed.liquidityActive - previousTickProcessed.liquidityNet + } + + processedTicks.push(currentTickProcessed) + previousTickProcessed = currentTickProcessed + } + + if (!ascending) { + processedTicks = processedTicks.reverse() + } + + return processedTicks +} diff --git a/src/pages/Earns/PositionDetail/RightSection.tsx b/src/pages/Earns/PositionDetail/RightSection.tsx new file mode 100644 index 0000000000..d33f876526 --- /dev/null +++ b/src/pages/Earns/PositionDetail/RightSection.tsx @@ -0,0 +1,115 @@ +import { t } from '@lingui/macro' +import { useMemo, useState } from 'react' +import { Flex, Text } from 'rebass' +import { usePoolDetailQuery } from 'services/poolService' + +import { Swap as SwapIcon } from 'components/Icons' +import useTheme from 'hooks/useTheme' +import { formatDisplayNumber, toString } from 'utils/numbers' + +import { ParsedPosition } from '.' +import LiquidityChart from './LiquidityChart' +import { MAX_TICK, MIN_TICK, nearestUsableTick, priceToClosestTick } from './LiquidityChart/uniswapv3' +import { InfoRightColumn, InfoSection, InfoSectionSecondFormat, RevertIconWrapper } from './styles' + +const RightSection = ({ position }: { position: ParsedPosition }) => { + const theme = useTheme() + const { data: pool } = usePoolDetailQuery({ chainId: position.chainId, ids: position.poolAddress }) + const [revert, setRevert] = useState(false) + + const price = useMemo(() => (!revert ? position.pairRate : 1 / position.pairRate), [position.pairRate, revert]) + + const priceRange = useMemo(() => { + if (!pool) return + + const tickSpacing = pool?.positionInfo.tickSpacing + + const minTick = nearestUsableTick(MIN_TICK, tickSpacing) + const maxTick = nearestUsableTick(MAX_TICK, tickSpacing) + const parsedMinPrice = toString(Number((!revert ? position.minPrice : 1 / position.maxPrice).toFixed(18))) + const parsedMaxPrice = toString(Number((!revert ? position.maxPrice : 1 / position.minPrice).toFixed(18))) + + const tickLower = + parsedMinPrice === '0' + ? minTick + : priceToClosestTick(parsedMinPrice, pool.tokens[0].decimals, pool.tokens[1].decimals, false) + const tickUpper = + Number(parsedMaxPrice) === Infinity + ? maxTick + : priceToClosestTick(parsedMaxPrice, pool.tokens[0].decimals, pool.tokens[1].decimals, false) + + const usableTickLower = nearestUsableTick(Number(tickLower), tickSpacing) + const usableTickUpper = nearestUsableTick(Number(tickUpper), tickSpacing) + + if (usableTickLower === minTick && usableTickUpper === maxTick) return ['0', '∞'] + else + return [ + formatDisplayNumber(parsedMinPrice, { significantDigits: 6 }), + formatDisplayNumber(parsedMaxPrice, { significantDigits: 6 }), + ] + }, [pool, position, revert]) + + if (!priceRange || !price) return null + + return ( + + + + + {t`Current Price`} + + + {formatDisplayNumber(price, { + significantDigits: 6, + })} + + + {!revert ? position.token1Symbol : position.token0Symbol} per{' '} + {!revert ? position.token0Symbol : position.token1Symbol} + + setRevert(!revert)}> + + + + + + + + + + + {t`Min Price`} + + + {priceRange[0]} + + + {!revert ? position.token1Symbol : position.token0Symbol}/ + {!revert ? position.token0Symbol : position.token1Symbol} + + + + + {t`Max Price`} + + + {priceRange[1]} + + + {!revert ? position.token1Symbol : position.token0Symbol}/ + {!revert ? position.token0Symbol : position.token1Symbol} + + + + + ) +} + +export default RightSection diff --git a/src/pages/Earns/PositionDetail/index.tsx b/src/pages/Earns/PositionDetail/index.tsx new file mode 100644 index 0000000000..9cec96fbe7 --- /dev/null +++ b/src/pages/Earns/PositionDetail/index.tsx @@ -0,0 +1,177 @@ +import { t } from '@lingui/macro' +import { useEffect, useMemo, useRef } from 'react' +import { useNavigate, useParams } from 'react-router-dom' +import { Flex, Text } from 'rebass' +import { useUserPositionsQuery } from 'services/zapEarn' + +import { ReactComponent as IconEarnNotFound } from 'assets/svg/ic_earn_not_found.svg' +import { ReactComponent as IconUserEarnPosition } from 'assets/svg/ic_user_earn_position.svg' +import { ReactComponent as RocketIcon } from 'assets/svg/rocket.svg' +import LocalLoader from 'components/LocalLoader' +import { APP_PATHS } from 'constants/index' +import { useActiveWeb3React } from 'hooks' + +import { NavigateButton } from '../PoolExplorer/styles' +import { EmptyPositionText, PositionPageWrapper } from '../UserPositions/styles' +import useLiquidityWidget from '../useLiquidityWidget' +import PositionDetailHeader from './Header' +import LeftSection from './LeftSection' +import RightSection from './RightSection' +import { MainSection, PositionAction, PositionActionWrapper, PositionDetailWrapper } from './styles' + +export interface ParsedPosition { + id: string + dex: string + dexImage: string + chainId: number + chainName: string + chainLogo: string + poolAddress: string + tokenAddress: string + token0Logo: string + token1Logo: string + token0Symbol: string + token1Symbol: string + poolFee: number + status: string + totalValue: number + apr: number + token0TotalAmount: number + token1TotalAmount: number + minPrice: number + maxPrice: number + pairRate: number + totalUnclaimedFee: number + token0UnclaimedAmount: number + token1UnclaimedAmount: number + token0UnclaimedValue: number + token1UnclaimedValue: number + earning24h: number + earning7d: number + totalEarnedFee: number +} + +const PositionDetail = () => { + const firstLoading = useRef(false) + const navigate = useNavigate() + + const { account } = useActiveWeb3React() + const { id, chainId } = useParams() + const { liquidityWidget, handleOpenZapInWidget, handleOpenZapOut } = useLiquidityWidget() + const { data: userPosition, isLoading } = useUserPositionsQuery( + { addresses: account || '', positionId: id, chainIds: chainId }, + { skip: !account, pollingInterval: 15_000 }, + ) + const currentWalletAddress = useRef(account) + + const position: ParsedPosition | undefined = useMemo(() => { + if (!userPosition?.[0]) return + const position = userPosition?.[0] + + return { + id: position.tokenId, + dex: position.pool.project || '', + dexImage: position.pool.projectLogo || '', + chainId: position.chainId, + chainName: position.chainName, + chainLogo: position.chainLogo || '', + poolAddress: position.pool.poolAddress || '', + tokenAddress: position.tokenAddress, + token0Logo: position.pool.tokenAmounts[0]?.token.logo || '', + token1Logo: position.pool.tokenAmounts[1]?.token.logo || '', + token0Symbol: position.pool.tokenAmounts[0]?.token.symbol || '', + token1Symbol: position.pool.tokenAmounts[1]?.token.symbol || '', + poolFee: position.pool.fees?.[0], + status: position.status, + totalValue: position.currentPositionValue, + apr: position.apr || 0, + token0TotalAmount: position + ? position.currentAmounts[0]?.quotes.usd.value / position.currentAmounts[0]?.quotes.usd.price + : 0, + token1TotalAmount: position + ? position.currentAmounts[1]?.quotes.usd.value / position.currentAmounts[1]?.quotes.usd.price + : 0, + minPrice: position.minPrice || 0, + maxPrice: position.maxPrice || 0, + pairRate: position.pool.price || 0, + totalUnclaimedFee: + (position.feePending?.[0].quotes.usd.value || 0) + (position.feePending?.[1].quotes.usd.value || 0), + token0UnclaimedAmount: position.feePending[0]?.quotes.usd.value / position.feePending[0]?.quotes.usd.price, + token1UnclaimedAmount: position.feePending[1]?.quotes.usd.value / position.feePending[1]?.quotes.usd.price, + token0UnclaimedValue: position.feePending[0]?.quotes.usd.value, + token1UnclaimedValue: position.feePending[1]?.quotes.usd.value, + earning24h: position.earning24h, + earning7d: position.earning7d, + totalEarnedFee: + position.feePending.reduce((a, b) => a + b.quotes.usd.value, 0) + + position.feesClaimed.reduce((a, b) => a + b.quotes.usd.value, 0), + } + }, [userPosition]) + + const onOpenIncreaseLiquidityWidget = () => { + if (!position) return + handleOpenZapInWidget( + { + exchange: position.dex, + chainId: position.chainId, + address: position.poolAddress, + }, + position.id, + ) + } + + useEffect(() => { + if (!firstLoading.current && !isLoading) { + firstLoading.current = true + } + }, [isLoading]) + + useEffect(() => { + if (!account || account !== currentWalletAddress.current) navigate(APP_PATHS.EARN_POSITIONS) + }, [account, navigate]) + + return ( + <> + {liquidityWidget} + + {isLoading && !firstLoading.current ? ( + + ) : !!position ? ( + <> + + + + + + + + { + handleOpenZapOut(position) + }} + >{t`Remove Liquidity`} + {t`Add Liquidity`} + + + + ) : ( + + + {t`No position found!`} + + } + text={t`Explorer Pools`} + to={APP_PATHS.EARN_POOLS} + /> + } text={t`My Positions`} to={APP_PATHS.EARN_POSITIONS} /> + + + )} + + + ) +} + +export default PositionDetail diff --git a/src/pages/Earns/PositionDetail/styles.tsx b/src/pages/Earns/PositionDetail/styles.tsx new file mode 100644 index 0000000000..1ee758effe --- /dev/null +++ b/src/pages/Earns/PositionDetail/styles.tsx @@ -0,0 +1,141 @@ +import styled from 'styled-components' + +import { ReactComponent as IconArrowLeftSvg } from 'assets/svg/ic_left_arrow.svg' + +export const IconArrowLeft = styled(IconArrowLeftSvg)` + cursor: pointer; + position: relative; + top: 5px; + color: ${({ theme }) => theme.subText}; + + :hover { + filter: brightness(1.5); + } +` + +export const DexInfo = styled.div<{ openable: boolean }>` + display: flex; + align-items: center; + gap: 6px; + color: ${({ theme }) => theme.text}; + + ${({ openable }) => openable && 'cursor: pointer;'} + :hover { + ${({ openable }) => openable && 'filter: brightness(1.2);'} + } +` + +export const PositionDetailWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 36px; + padding: 36px; + border-radius: 20px; + background-color: ${({ theme }) => theme.background}; + width: 100%; + + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 24px; + `} + + ${({ theme }) => theme.mediaWidth.upToSmall` + border-radius: 0; + margin: 0 -16px; + width: calc(100% + 32px); + padding: 20px 16px; + `} +` + +export const MainSection = styled.div` + display: flex; + gap: 36px; + + ${({ theme }) => theme.mediaWidth.upToMedium` + flex-direction: column; + `} +` + +export const InfoColumn = styled.div` + display: flex; + flex-direction: column; + gap: 16px; +` + +export const InfoLeftColumn = styled(InfoColumn)` + flex: 0 1 35%; +` + +export const InfoRightColumn = styled(InfoColumn)` + flex: 1 1 65%; +` + +export const InfoSection = styled.div` + border-radius: 16px; + padding: 16px 24px; + border: 1px solid ${({ theme }) => theme.tabActive}; +` + +export const InfoSectionFirstFormat = styled(InfoSection)` + display: flex; + align-items: flex-start; + justify-content: space-between; +` + +export const InfoSectionSecondFormat = styled(InfoSection)` + flex: 1 1 50%; + display: flex; + flex-direction: column; + align-items: center; + justify-content: center; +` + +export const InfoRight = styled.div` + display: flex; + flex-direction: column; + align-items: flex-end; + gap: 8px; +` + +export const VerticalDivider = styled.div` + width: 1px; + height: 32px; + background: ${({ theme }) => theme.tabActive}; +` + +export const RevertIconWrapper = styled.div` + cursor: pointer; + + :hover { + filter: brightness(0.9); + } +` + +export const PositionActionWrapper = styled.div` + display: flex; + align-items: center; + justify-content: flex-end; + gap: 24px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + gap: 16px; + `} +` + +export const PositionAction = styled.button<{ outline?: boolean }>` + border-radius: 24px; + padding: 10px 18px; + background-color: ${({ theme }) => theme.primary}; + border: 1px solid ${({ theme }) => theme.primary}; + cursor: pointer; + + ${({ outline }) => outline && 'background-color: transparent;'} + ${({ outline, theme }) => outline && `color: ${theme.primary};`} + + ${({ theme }) => theme.mediaWidth.upToSmall` + width: 100%; + `} + + :hover { + filter: brightness(1.2); + } +` diff --git a/src/pages/Earns/UserPositions/Filter.tsx b/src/pages/Earns/UserPositions/Filter.tsx new file mode 100644 index 0000000000..297ead3564 --- /dev/null +++ b/src/pages/Earns/UserPositions/Filter.tsx @@ -0,0 +1,61 @@ +import { t } from '@lingui/macro' +import { useMedia } from 'react-use' +import { Flex } from 'rebass' + +import { ReactComponent as RocketIcon } from 'assets/svg/rocket.svg' +import { APP_PATHS } from 'constants/index' +import { MEDIA_WIDTHS } from 'theme' + +import DropdownMenu, { MenuOption } from '../PoolExplorer/DropdownMenu' +import { NavigateButton } from '../PoolExplorer/styles' +import { AllChainsOption, AllProtocolsOption } from '../useSupportedDexesAndChains' + +export default function Filter({ + supportedChains, + supportedDexes, + filters, + onFilterChange, +}: { + supportedChains: MenuOption[] + supportedDexes: MenuOption[] + filters: { + addresses: string + chainIds: string + protocols: string + } + onFilterChange: (key: string, value: string | number) => void +}) { + const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) + + return ( + + + value !== filters.chainIds && onFilterChange('chainIds', value)} + /> + value !== filters.protocols && onFilterChange('protocols', value)} + /> + + } + text={t`Explore Pools`} + to={APP_PATHS.EARN_POOLS} + /> + + ) +} diff --git a/src/pages/Earns/UserPositions/index.tsx b/src/pages/Earns/UserPositions/index.tsx new file mode 100644 index 0000000000..b31449d3be --- /dev/null +++ b/src/pages/Earns/UserPositions/index.tsx @@ -0,0 +1,311 @@ +import { t } from '@lingui/macro' +import { useEffect, useRef, useState } from 'react' +import { Minus, Plus } from 'react-feather' +import { Link, useNavigate } from 'react-router-dom' +import { useMedia } from 'react-use' +import { Flex, Text } from 'rebass' +import { EarnPosition, PositionStatus, useUserPositionsQuery } from 'services/zapEarn' + +import { ReactComponent as IconEarnNotFound } from 'assets/svg/ic_earn_not_found.svg' +import CopyHelper from 'components/Copy' +import LocalLoader from 'components/LocalLoader' +import { MouseoverTooltipDesktopOnly } from 'components/Tooltip' +import { APP_PATHS } from 'constants/index' +import { useActiveWeb3React } from 'hooks' +import useTheme from 'hooks/useTheme' +import { useWalletModalToggle } from 'state/application/hooks' +import { MEDIA_WIDTHS } from 'theme' +import { shortenAddress } from 'utils' +import { formatDisplayNumber } from 'utils/numbers' + +import { CurrencyRoundedImage, CurrencySecondImage, Disclaimer } from '../PoolExplorer/styles' +import { PositionAction as PositionActionBtn } from '../PositionDetail/styles' +import useLiquidityWidget from '../useLiquidityWidget' +import useSupportedDexesAndChains from '../useSupportedDexesAndChains' +import Filter from './Filter' +import { + Badge, + BadgeType, + ChainImage, + DexImage, + Divider, + EmptyPositionText, + ImageContainer, + MyLiquidityWrapper, + PositionAction, + PositionOverview, + PositionPageWrapper, + PositionRow, + PositionValueLabel, + PositionValueWrapper, +} from './styles' +import useFilter from './useFilter' + +const MyPositions = () => { + const theme = useTheme() + const { account } = useActiveWeb3React() + const toggleWalletModal = useWalletModalToggle() + const navigate = useNavigate() + const upToLarge = useMedia(`(max-width: ${MEDIA_WIDTHS.upToLarge}px)`) + const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) + const { filters, onFilterChange } = useFilter() + const { supportedDexes, supportedChains } = useSupportedDexesAndChains(filters) + + const { liquidityWidget, handleOpenZapInWidget, handleOpenZapOut } = useLiquidityWidget() + const firstLoading = useRef(false) + const [loading, setLoading] = useState(false) + + const { data: userPosition, isFetching } = useUserPositionsQuery(filters, { + skip: !filters.addresses, + pollingInterval: 15_000, + }) + + const onOpenIncreaseLiquidityWidget = (e: React.MouseEvent, position: EarnPosition) => { + e.stopPropagation() + handleOpenZapInWidget( + { + exchange: position.pool.project || '', + chainId: position.chainId, + address: position.pool.poolAddress, + }, + position.tokenId, + ) + } + + useEffect(() => { + if (!isFetching) setLoading(false) + else { + if (!firstLoading.current) { + setLoading(true) + firstLoading.current = true + } + } + }, [isFetching]) + + return ( + <> + {liquidityWidget} + +
+ + {t`My Liquidity`} + + + {t`KyberSwap Zap: Instantly and easily add liquidity to high-APY pools using any token or a combination of tokens.`} + +
+ + { + onFilterChange(...args) + setLoading(true) + }} + /> + + + {isFetching && loading ? ( + + ) : account && userPosition && userPosition.length > 0 ? ( + userPosition.map(position => { + console.log('position', position) + const { id, status, chainId: poolChainId } = position + const positionId = position.tokenId + const chainImage = position.chainLogo + const dexImage = position.pool.projectLogo + const dexVersion = position.pool.project?.split(' ')?.[1] || '' + const token0Logo = position.pool.tokenAmounts[0]?.token.logo + const token1Logo = position.pool.tokenAmounts[1]?.token.logo + const token0Symbol = position.pool.tokenAmounts[0]?.token.symbol + const token1Symbol = position.pool.tokenAmounts[1]?.token.symbol + const poolFee = position.pool.fees?.[0] + const poolAddress = position.pool.poolAddress + const totalValue = position.currentPositionValue + const token0TotalProvide = + position.currentAmounts[0]?.quotes.usd.value / position.currentAmounts[0]?.quotes.usd.price + const token1TotalProvide = + position.currentAmounts[1]?.quotes.usd.value / position.currentAmounts[1]?.quotes.usd.price + const token0EarnedAmount = + position.feePending[0]?.quotes.usd.value / position.feePending[0]?.quotes.usd.price + + position.feesClaimed[0]?.quotes.usd.value / position.feesClaimed[0]?.quotes.usd.price + const token1EarnedAmount = + position.feePending[1]?.quotes.usd.value / position.feePending[1]?.quotes.usd.price + + position.feesClaimed[1]?.quotes.usd.value / position.feesClaimed[1]?.quotes.usd.price + const token0TotalAmount = token0TotalProvide + token0EarnedAmount + const token1TotalAmount = token1TotalProvide + token1EarnedAmount + const totalEarnedFee = + position.feePending.reduce((a, b) => a + b.quotes.usd.value, 0) + + position.feesClaimed.reduce((a, b) => a + b.quotes.usd.value, 0) + + return ( + + navigate({ + pathname: APP_PATHS.EARN_POSITION_DETAIL.replace(':chainId', poolChainId.toString()).replace( + ':id', + id, + ), + }) + } + > + + + + + + + + + {token0Symbol}/{token1Symbol} + + {poolFee && {poolFee}%} + + ● {status === PositionStatus.IN_RANGE ? t`In range` : t`Out of range`} + + + + + + + {dexVersion} + + + + #{positionId} + + + {shortenAddress(poolChainId, poolAddress, 4)} + + + + + {upToLarge && !upToSmall && ( + + { + e.stopPropagation() + handleOpenZapOut({ + dex: position.pool.project || '', + chainId: position.chainId, + id: position.tokenId, + poolAddress: position.pool.poolAddress, + }) + }} + > + + + onOpenIncreaseLiquidityWidget(e, position)}> + + + + )} + + {t`Value`} + + + {formatDisplayNumber(token0TotalAmount, { significantDigits: 6 })} {token0Symbol} + + + {formatDisplayNumber(token1TotalAmount, { significantDigits: 6 })} {token1Symbol} + + + } + width="fit-content" + placement="bottom" + > + + {formatDisplayNumber(totalValue, { + style: 'currency', + significantDigits: 4, + })} + + + + + {t`Earned Fee`} + + + {formatDisplayNumber(token0EarnedAmount, { significantDigits: 6 })} {token0Symbol} + + + {formatDisplayNumber(token1EarnedAmount, { significantDigits: 6 })} {token1Symbol} + + + } + width="fit-content" + placement="bottom" + > + {formatDisplayNumber(totalEarnedFee, { style: 'currency', significantDigits: 4 })} + + + + Balance + + + {formatDisplayNumber(token0TotalProvide, { significantDigits: 4 })} {token0Symbol} + + {upToSmall && } + + {formatDisplayNumber(token1TotalProvide, { significantDigits: 4 })} {token1Symbol} + + + + {(upToSmall || !upToLarge) && ( + + + { + e.stopPropagation() + handleOpenZapOut({ + dex: position.pool.project || '', + chainId: position.chainId, + id: position.tokenId, + poolAddress: position.pool.poolAddress, + }) + }} + > + + + + + onOpenIncreaseLiquidityWidget(e, position)}> + + + + + )} + + ) + }) + ) : ( + + + + {t`You don’t have any liquidity positions yet`}. + {t`Explore Liquidity Pools to get started`}! + + {!account && Connect Wallet} + + )} + + + {t`KyberSwap provides tools for tracking & adding liquidity to third-party Protocols. For any pool-related concerns, please contact the respective Liquidity Protocol directly.`} +
+ + ) +} + +export default MyPositions diff --git a/src/pages/Earns/UserPositions/styles.tsx b/src/pages/Earns/UserPositions/styles.tsx new file mode 100644 index 0000000000..0e7cefd3a4 --- /dev/null +++ b/src/pages/Earns/UserPositions/styles.tsx @@ -0,0 +1,201 @@ +import { rgba } from 'polished' +import styled from 'styled-components' + +import { PoolPageWrapper } from '../PoolExplorer/styles' + +export const PositionPageWrapper = styled(PoolPageWrapper)` + padding: 24px 6rem 50px; + + ${({ theme }) => theme.mediaWidth.upToLarge` + padding: 24px 6rem 60px; + `} + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 24px 16px 100px; + `} +` + +export const MyLiquidityWrapper = styled.div` + display: flex; + flex-direction: column; + gap: 1rem; + max-height: 539px; + overflow-y: auto; + + ${({ theme }) => theme.mediaWidth.upToSmall` + max-height: unset; + `} +` + +export const PositionRow = styled.div` + display: grid; + grid-template-columns: 2fr 1fr 1fr 1fr 75px; + grid-template-rows: 1fr; + background-color: ${({ theme }) => rgba(theme.background, 0.8)}; + border-radius: 20px; + padding: 16px 28px; + row-gap: 8px; + + ${({ theme }) => theme.mediaWidth.upToLarge` + justify-content: flex-start; + grid-template-columns: repeat(3, 1fr); + grid-template-rows: 1fr 1fr; + `} + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-direction: column; + row-gap: 16px; + padding: 16px; + `} + + &:hover { + cursor: pointer; + filter: brightness(1.1); + } +` + +export const PositionOverview = styled.div` + display: flex; + flex-direction: column; + gap: 8px; + + ${({ theme }) => theme.mediaWidth.upToLarge` + grid-column: span 2; + `} +` + +export const ImageContainer = styled.div` + position: relative; + top: 2px; +` + +export const ChainImage = styled.img` + width: 12px; + height: 12px; + border-radius: 50%; + position: relative; + left: -14px; + top: 4px; +` + +export const DexImage = styled.img` + width: 16px; + height: 16px; + border-radius: 50%; +` + +export enum BadgeType { + PRIMARY = 'primary', + WARNING = 'warning', + SECONDARY = 'secondary', +} + +export const Badge = styled.div<{ type?: BadgeType }>` + border-radius: 30px; + padding: 4px 12px; + background-color: ${({ theme }) => rgba(theme.white, 0.04)}; + color: ${({ theme }) => theme.subText}; + font-size: 12px; + display: flex; + align-items: center; + + ${({ type, theme }) => { + switch (type) { + case BadgeType.PRIMARY: + return ` + background-color: ${rgba(theme.primary, 0.2)}; + color: ${theme.primary}; + ` + case BadgeType.WARNING: + return ` + background-color: ${rgba(theme.warning, 0.2)}; + color: ${theme.warning}; + ` + case BadgeType.SECONDARY: + return ` + color: #2C9CE4; + ` + default: + return '' + } + }} + + ${({ theme }) => theme.mediaWidth.upToXXSmall` + padding: 4px 9px; + `} +` + +export const PositionValueWrapper = styled.div<{ align?: string }>` + display: flex; + align-items: flex-start; + justify-content: flex-start; + gap: 8px; + padding-top: 8px; + + ${({ align }) => (align ? `justify-content: ${align};` : '')} + + ${({ theme }) => theme.mediaWidth.upToSmall` + justify-content: space-between; + padding-top: 0; + `} +` + +export const PositionValueLabel = styled.p` + font-size: 14px; + margin: 0; + color: ${({ theme }) => theme.subText}; + position: relative; + top: 1px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + font-size: 16px; + top: 0; + `} +` + +export const PositionAction = styled.div<{ primary?: boolean }>` + display: flex; + align-items: center; + justify-content: center; + width: 30px; + height: 30px; + border-radius: 12px; + background-color: ${({ theme, primary }) => (primary ? rgba(theme.primary, 0.2) : theme.tabActive)}; + color: ${({ theme, primary }) => (primary ? theme.primary : theme.subText)}; + + &:hover { + cursor: pointer; + filter: brightness(1.2); + } + + &:active { + filter: brightness(1.05); + } + + ${({ theme }) => theme.mediaWidth.upToSmall` + width: 36px; + height: 36px; + `} +` + +export const Divider = styled.div` + height: 16px; + width: 1px; + background: ${({ theme }) => theme.tabActive}; + margin: 0 14px; + position: relative; + top: 1px; +` + +export const EmptyPositionText = styled.div` + display: flex; + justify-content: center; + align-items: center; + flex-direction: column; + gap: 8px; + color: ${({ theme }) => theme.subText}; + border-radius: 20px; + height: 400px; + margin: 20px 0; +` diff --git a/src/pages/Earns/UserPositions/useFilter.ts b/src/pages/Earns/UserPositions/useFilter.ts new file mode 100644 index 0000000000..d51ebcf894 --- /dev/null +++ b/src/pages/Earns/UserPositions/useFilter.ts @@ -0,0 +1,25 @@ +import { useEffect, useState } from 'react' + +import { useActiveWeb3React } from 'hooks' + +export default function useFilter() { + const { account } = useActiveWeb3React() + const [filters, setFilters] = useState({ + addresses: '', + chainIds: '', + protocols: '', + }) + + const onFilterChange = (key: string, value: string | number) => { + setFilters(prev => ({ + ...prev, + [key]: value, + })) + } + + useEffect(() => { + setFilters(prev => ({ ...prev, addresses: account || '' })) + }, [account]) + + return { filters, onFilterChange } +} diff --git a/src/pages/Earns/index.tsx b/src/pages/Earns/index.tsx new file mode 100644 index 0000000000..5da15181c8 --- /dev/null +++ b/src/pages/Earns/index.tsx @@ -0,0 +1,583 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' +import { rgba } from 'polished' +import { useNavigate } from 'react-router-dom' +import { useMedia } from 'react-use' +import { Box, Flex, Text } from 'rebass' +import { EarnPool, useExplorerLandingQuery } from 'services/zapEarn' +import styled, { keyframes } from 'styled-components' + +import bg from 'assets/images/earn-bg.png' +import CursorIcon from 'assets/svg/cursor.svg' +import FireIcon from 'assets/svg/fire.svg' +import LiquidityPoolIcon from 'assets/svg/liquidity-pools.svg' +import LiquidityPosIcon from 'assets/svg/liquidity-positions.svg' +import LowVolatilityIcon from 'assets/svg/low-volatility.svg' +import PlayIcon from 'assets/svg/play-icon.svg' +import RocketIcon from 'assets/svg/rocket.svg' +import SolidEarningIcon from 'assets/svg/solid-earning.svg' +import StakingIcon from 'assets/svg/staking.svg' +import { ButtonPrimary } from 'components/Button' +import LocalLoader from 'components/LocalLoader' +import { MouseoverTooltipDesktopOnly } from 'components/Tooltip' +import { APP_PATHS } from 'constants/index' +import { useActiveWeb3React } from 'hooks' +import { NETWORKS_INFO } from 'hooks/useChainsConfig' +import useTheme from 'hooks/useTheme' +import { MEDIA_WIDTHS } from 'theme' + +import { FilterTag } from './PoolExplorer' +import useLiquidityWidget from './useLiquidityWidget' +import { formatAprNumber } from './utils' + +const WrapperBg = styled.div` + background-image: url(${bg}); + background-size: 100% auto; + background-repeat: repeat-y; + width: 100vw; +` + +const Container = styled.div` + max-width: 1152px; + padding: 60px 16px; + margin: auto; + text-align: center; + + ${({ theme }) => theme.mediaWidth.upToXXSmall` + padding: 36px 12px; + `} +` + +/* Spin animation */ +const spin = keyframes` + from { + transform: rotate(0deg); + } + to { + transform: rotate(360deg); + } +` + +const BorderWrapper = styled.div` + padding: 1px; + position: relative; + background-clip: padding-box; + border-radius: 20px; + overflow: hidden; + + ::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + padding: 1px; /* Border width */ + background: linear-gradient(306.9deg, #262525 38.35%, rgba(49, 203, 158, 0.06) 104.02%), + radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(49, 203, 158, 0.6) 0%, rgba(0, 0, 0, 0) 100%); + mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); /* Mask to avoid background bleed */ + -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0); /* Mask to avoid background bleed */ + z-index: -1; + } + + :hover::before { + top: -20%; + left: -20%; + right: -20%; + bottom: -20%; + padding: 1px; /* Border width */ + background: linear-gradient(306.9deg, #262525 38.35%, rgba(49, 203, 158, 0.6) 104.02%), + radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(49, 203, 158, 1) 0%, rgba(0, 0, 0, 0) 100%); + + animation: ${spin} 2s linear infinite; /* Spin animation */ + } +` + +const OverviewWrapper = styled.div` + box-sizing: border-box; + margin: 0; + min-width: 0; + display: grid; + grid-template-columns: repeat(3, 1fr); + margin-top: 64px; + gap: 20px; + + ${({ theme }) => theme.mediaWidth.upToSmall` + display: flex; + flex-direction: column; + `} + + ${({ theme }) => theme.mediaWidth.upToXXSmall` + margin-top: 40px; + gap: 16px; + `} +` + +const PoolWrapper = styled.div` + border-radius: 20px; + position: relative; + overflow: hidden; + padding: 1px; + transition: box-shadow 0.3s ease, transform 0.3s ease, background 0.3s ease; + + :hover { + box-shadow: 0px 12px 64px 0px rgba(71, 32, 139, 0.8); + ::before { + background: linear-gradient(215.58deg, #262525 -9.03%, rgba(148, 115, 221, 0.6) 59.21%), + radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(130, 71, 229, 1) 0%, rgba(0, 0, 0, 0) 100%); + } + } + + /* Create the gradient border effect using ::before */ + ::before { + content: ''; + position: absolute; + top: 0; + left: 0; + right: 0; + bottom: 0; + border-radius: 20px; + padding: 1px; + + background: linear-gradient(215.58deg, #262525 -9.03%, rgba(148, 115, 221, 0.2) 59.21%), + radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(130, 71, 229, 0.6) 0%, rgba(0, 0, 0, 0) 100%); + mask-composite: destination-out; + -webkit-mask-composite: destination-out; + z-index: -1; /* Position behind the content */ + } +` + +const CardWrapper = styled.div` + border-radius: 20px; + + background: linear-gradient(119.08deg, rgba(20, 29, 27, 1) -0.89%, rgba(14, 14, 14, 1) 132.3%); + padding: 0 36px 44px 50px; + text-align: left; + min-height: 360px; + display: flex; + flex-direction: column; + overflow: hidden; + height: 100%; + + cursor: url(${CursorIcon}), auto; + button { + cursor: url(${CursorIcon}), auto; + } + + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 0 36px 40px; + `} + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 0 36px 28px; + min-height: 285px; + `} + + ${({ theme }) => theme.mediaWidth.upToXXSmall` + padding: 0 30px 24px; + min-height: unset; + height: fit-content; + `} +` + +const ButtonPrimaryStyled = styled(ButtonPrimary)` + margin-top: auto; + width: 132px; + height: 36px; + + ${({ theme }) => theme.mediaWidth.upToXXSmall` + margin-top: 18px; + `} +` + +const ListPoolWrapper = styled.div` + padding: 20px; + border-radius: 20px; + height: 100%; + background: linear-gradient(119.08deg, rgba(20, 29, 27, 1) -0.89%, rgba(14, 14, 14, 1) 132.3%); + cursor: url(${CursorIcon}), auto; + + ${({ theme }) => theme.mediaWidth.upToMedium` + padding: 12px; + `} + + ${({ theme }) => theme.mediaWidth.upToSmall` + padding: 18px; + `} +` + +const PoolRow = styled(Flex)` + gap: 12px; + align-items: center; + border-radius: 999px; + padding: 8px 16px; + + :hover { + background: #31cb9e1a; + } +` + +const Tag = styled.div` + border-radius: 999px; + background: ${({ theme }) => rgba(theme.text, 0.1)}; + color: ${({ theme }) => theme.subText}; + padding: 4px 8px; + font-size: 12px; +` + +const Icon = ({ icon, size = 'medium' }: { icon: string; size: 'small' | 'medium' }) => { + return ( + + + icon + + + ) +} + +const Card = ({ + title, + icon, + desc, + action, +}: { + title: string + icon: string + desc: string + action: { text: string; disabled?: boolean; onClick: () => void } +}) => { + const theme = useTheme() + const upToMedium = useMedia(`(max-width: ${MEDIA_WIDTHS.upToMedium}px)`) + const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) + + return ( + !action.disabled && action.onClick()}> + + + + + + + + {title} + + + {desc} + + {action.text} + + + ) +} + +export default function Earns() { + const navigate = useNavigate() + const theme = useTheme() + const { account } = useActiveWeb3React() + const { isLoading, data } = useExplorerLandingQuery({ userAddress: account }) + + const title = (title: string, tooltip: string, icon: string) => ( + <> + + + + {title} + + + + + ) + + const highlightedPools = (data?.data?.highlightedPools || []).slice(0, 9) + const highAprPool = (data?.data?.highAPR || []).slice(0, 5) + const lowVolatilityPool = [...(data?.data?.lowVolatility || [])].sort((a, b) => b.apr - a.apr).slice(0, 5) + const solidEarningPool = (data?.data?.solidEarning || []).slice(0, 5) + + const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`) + const upToXXSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`) + + return ( + + + + Maximize Your Earnings in DeFi + + + Unlock the full potential of your assets. Offering data, tools, and utilities—centered around Zap + technology—to help you maximize earnings from your liquidity across various DeFi protocols. + + + + navigate({ pathname: APP_PATHS.EARN_POOLS }), + }} + /> + navigate({ pathname: APP_PATHS.EARN_POSITIONS }), + }} + /> + {}, + disabled: true, + }} + /> + + + + { + navigate({ + pathname: APP_PATHS.EARN_POOLS, + search: `tag=${FilterTag.HIGHLIGHTED_POOL}`, + }) + }} + > + {title( + 'Highlighted Pools', + 'Pools matching your wallet tokens or top 24h volume pools if no wallet is connected', + FireIcon, + )} + {isLoading ? ( + + ) : ( + + {highlightedPools.map(pool => ( + + ))} + + )} + + + + + + { + navigate({ + pathname: APP_PATHS.EARN_POOLS, + search: `tag=${FilterTag.HIGH_APR}`, + }) + }} + > + {title('High APR', 'Top 100 Pools with assets that offer exceptionally high APYs', RocketIcon)} + {isLoading ? ( + + ) : ( + + {highAprPool.map(pool => ( + + ))} + + )} + + + + + { + navigate({ + pathname: APP_PATHS.EARN_POOLS, + search: `tag=${FilterTag.LOW_VOLATILITY}`, + }) + }} + > + {title( + 'Low Volatility', + 'Top 100 highest TVL Pools consisting of stable coins or correlated pairs', + LowVolatilityIcon, + )} + {isLoading ? ( + + ) : ( + + {lowVolatilityPool.map(pool => ( + + ))} + + )} + + + + + { + navigate({ + pathname: APP_PATHS.EARN_POOLS, + search: `tag=${FilterTag.SOLID_EARNING}`, + }) + }} + > + {title( + 'Solid Earning', + 'Top 100 pools that have the high total earned fee in the last 7 days', + SolidEarningIcon, + )} + {isLoading ? ( + + ) : ( + + {solidEarningPool.map(pool => ( + + ))} + + )} + + + + + { + navigate({ + pathname: APP_PATHS.EARN_POOLS, + }) + }} + sx={{ + cursor: 'pointer', + border: `1px solid ${theme.primary}`, + margin: 'auto', + marginTop: '40px', + borderRadius: '999px', + height: '56px', + background: rgba(theme.primary, 0.2), + fontSize: '16px', + fontWeight: 500, + color: theme.primary, + alignItems: 'center', + padding: '1rem 2rem', + width: 'fit-content', + }} + > + EXPLORE POOLS + play + + + + ) +} + +const PoolItem = ({ pool }: { pool: EarnPool }) => { + const theme = useTheme() + const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget() + + return ( + { + e.stopPropagation() + handleOpenZapInWidget({ + exchange: pool.exchange, + chainId: pool.chainId, + address: pool.address, + }) + }} + > + {liquidityWidget} + + + + + + {pool.tokens?.[0].symbol} /{' '} + + {pool.tokens?.[1].symbol} + + + {pool.feeTier}% + + + {formatAprNumber(pool.apr)}% + + ) +} diff --git a/src/pages/Earns/useLiquidityWidget.tsx b/src/pages/Earns/useLiquidityWidget.tsx new file mode 100644 index 0000000000..3a83df66f9 --- /dev/null +++ b/src/pages/Earns/useLiquidityWidget.tsx @@ -0,0 +1,367 @@ +import { ChainId, LiquidityWidget, PoolType, ZapOut } from '@kyberswap/liquidity-widgets' +import '@kyberswap/liquidity-widgets/dist/style.css' +import { Dex, ChainId as MigrateChainId, ZapMigration } from '@kyberswap/zap-migration-widgets' +import '@kyberswap/zap-migration-widgets/dist/style.css' +import { useCallback, useEffect, useMemo, useState } from 'react' +import { useNavigate } from 'react-router-dom' +import { usePreviousDistinct } from 'react-use' + +import { NotificationType } from 'components/Announcement/type' +import Modal from 'components/Modal' +import { NETWORKS_INFO } from 'constants/networks' +import { useActiveWeb3React, useWeb3React } from 'hooks' +import { useChangeNetwork } from 'hooks/web3/useChangeNetwork' +import { useNotify, useWalletModalToggle } from 'state/application/hooks' + +import useFilter from './PoolExplorer/useFilter' + +interface AddLiquidityPureParams { + poolAddress: string + chainId: ChainId + poolType: PoolType + positionId?: string +} + +interface AddLiquidityParams extends AddLiquidityPureParams { + source: string + connectedAccount: { + address?: string | undefined + chainId: number + } + onClose: () => void + onConnectWallet: () => void + onSwitchChain: () => void + onOpenZapMigration?: (position: { exchange: string; poolId: string; positionId: string | number }) => void + onSubmitTx: (txData: { from: string; to: string; value: string; data: string; gasLimit: string }) => Promise +} + +interface MigrateLiquidityPureParams { + from: { + dex: Dex + poolId: string + positionId: string | number + } + to: { + dex: Dex + poolId: string + positionId?: string | number + } + chainId: MigrateChainId +} + +interface MigrateLiquidityParams extends MigrateLiquidityPureParams { + client: string + connectedAccount: { + address: string | undefined + chainId: MigrateChainId + } + onClose: () => void + onConnectWallet: () => void + onSwitchChain: () => void + onSubmitTx: (txData: { from: string; to: string; value: string; data: string }) => Promise +} + +enum SupporttedExchange { + UniswapV3 = 'Uniswap V3', + Pancakev3 = 'PancakeSwap V3', + Sushiv3 = 'SushiSwap V3', +} + +const dexFormatter = { + [PoolType.DEX_UNISWAPV3]: Dex.Uniswapv3, + [PoolType.DEX_PANCAKESWAPV3]: Dex.Pancakev3, + [PoolType.DEX_SUSHISWAPV3]: Dex.Sushiv3, + [PoolType.DEX_UNISWAPV2]: null, + [PoolType.DEX_PANCAKESWAPV2]: null, + [PoolType.DEX_SUSHISWAPV2]: null, + [PoolType.DEX_QUICKSWAPV2]: null, + [PoolType.DEX_PANGOLINSTANDARD]: null, + [PoolType.DEX_THRUSTERV2]: null, + [PoolType.DEX_SWAPMODEV2]: null, + [PoolType.DEX_METAVAULTV3]: null, + [PoolType.DEX_LINEHUBV3]: null, + [PoolType.DEX_SWAPMODEV3]: null, + [PoolType.DEX_KOICL]: null, + [PoolType.DEX_THRUSTERV3]: null, + [PoolType.DEX_QUICKSWAPV3UNI]: null, + [SupporttedExchange.UniswapV3]: Dex.Uniswapv3, + [SupporttedExchange.Pancakev3]: Dex.Pancakev3, + [SupporttedExchange.Sushiv3]: Dex.Sushiv3, +} + +const useLiquidityWidget = () => { + const toggleWalletModal = useWalletModalToggle() + const notify = useNotify() + const { library } = useWeb3React() + const { account, chainId } = useActiveWeb3React() + const { filters } = useFilter() + + const [addLiquidityPureParams, setAddLiquidityPureParams] = useState(null) + const [migrateLiquidityPureParams, setMigrateLiquidityPureParams] = useState(null) + + const handleOpenZapMigrationWidget = useCallback( + (position: { exchange: string; poolId: string; positionId: string | number }) => { + if (!addLiquidityPureParams) return + if (!dexFormatter[position.exchange as SupporttedExchange]) { + notify( + { + title: `Open liquidity migration widget failed`, + summary: `Protocol ${position.exchange} is not supported`, + type: NotificationType.ERROR, + }, + 8000, + ) + return + } + if (!dexFormatter[addLiquidityPureParams.poolType]) { + notify( + { + title: `Open liquidity migration widget failed`, + summary: `Protocol ${addLiquidityPureParams.poolType} is not supported`, + type: NotificationType.ERROR, + }, + 8000, + ) + return + } + const paramsToSet = { + from: { + dex: dexFormatter[position.exchange as SupporttedExchange], + poolId: position.poolId, + positionId: position.positionId, + }, + to: { + dex: dexFormatter[addLiquidityPureParams.poolType] as Dex, + poolId: addLiquidityPureParams.poolAddress, + positionId: addLiquidityPureParams.positionId, + }, + chainId: addLiquidityPureParams.chainId as MigrateChainId, + } + setMigrateLiquidityPureParams(paramsToSet) + }, + [addLiquidityPureParams, notify], + ) + + const handleOpenZapInWidget = ( + pool: { exchange: string; chainId?: number; address: string }, + positionId?: string, + ) => { + const supportedDexs = Object.keys(PoolType).map(item => item.replace('DEX_', '').replace('V3', '').toLowerCase()) + const formattedExchange = pool.exchange.toLowerCase().replaceAll('_', '').replaceAll('-', '').replaceAll('v3', '') + const dex = supportedDexs.find(item => formattedExchange.includes(item) || item.includes(formattedExchange)) + if (!dex) { + notify( + { + title: `Open pool detail failed`, + summary: `Protocol ${pool.exchange} on ${ + NETWORKS_INFO[String(pool?.chainId || filters.chainId) as unknown as keyof typeof NETWORKS_INFO]?.name || + 'this network' + } is not supported`, + type: NotificationType.ERROR, + }, + 8000, + ) + return + } + setAddLiquidityPureParams({ + poolAddress: pool.address, + chainId: (pool.chainId || filters.chainId) as ChainId, + poolType: PoolType[`DEX_${dex.toUpperCase()}V3` as keyof typeof PoolType], + positionId, + }) + } + + const { changeNetwork } = useChangeNetwork() + const navigate = useNavigate() + + const addLiquidityParams: AddLiquidityParams | null = useMemo( + () => + addLiquidityPureParams + ? { + ...addLiquidityPureParams, + source: 'KyberSwap-Earn', + onViewPosition: () => { + setAddLiquidityPureParams(null) + navigate(`/earns/positions`) + }, + connectedAccount: { + address: account, + chainId: chainId, + }, + onClose: () => setAddLiquidityPureParams(null), + onConnectWallet: toggleWalletModal, + onSwitchChain: () => changeNetwork(addLiquidityPureParams.chainId as number), + onOpenZapMigration: handleOpenZapMigrationWidget, + onSubmitTx: async (txData: { from: string; to: string; data: string; value: string; gasLimit: string }) => { + try { + if (!library) throw new Error('Library is not ready!') + const res = await library?.getSigner().sendTransaction(txData) + if (!res) throw new Error('Transaction failed') + return res.hash + } catch (e) { + console.log(e) + throw e + } + }, + } + : null, + [ + addLiquidityPureParams, + account, + chainId, + toggleWalletModal, + handleOpenZapMigrationWidget, + library, + changeNetwork, + navigate, + ], + ) + + const migrateLiquidityParams: MigrateLiquidityParams | null = useMemo( + () => + migrateLiquidityPureParams + ? { + ...migrateLiquidityPureParams, + client: 'KyberSwap-Earn', + connectedAccount: { + address: account, + chainId: chainId as unknown as MigrateChainId, + }, + onViewPosition: () => { + setMigrateLiquidityPureParams(null) + navigate(`/earns/positions`) + }, + + onClose: () => { + setMigrateLiquidityPureParams(null) + setAddLiquidityPureParams(null) + }, + onBack: () => setMigrateLiquidityPureParams(null), + onConnectWallet: toggleWalletModal, + onSwitchChain: () => changeNetwork(migrateLiquidityPureParams.chainId as number), + onSubmitTx: async (txData: { from: string; to: string; value: string; data: string }) => { + try { + if (!library) throw new Error('Library is not ready!') + const res = await library?.getSigner().sendTransaction(txData) + if (!res) throw new Error('Transaction failed') + return res.hash + } catch (e) { + console.log(e) + throw e + } + }, + } + : null, + [account, chainId, library, migrateLiquidityPureParams, changeNetwork, toggleWalletModal, navigate], + ) + + const [zapOutPureParams, setZapOutPureParams] = useState<{ + positionId: string + poolType: PoolType + poolAddress: string + chainId: ChainId + } | null>(null) + const zapOutParams = useMemo( + () => + zapOutPureParams + ? { + ...zapOutPureParams, + source: 'KyberSwap-Earn', + connectedAccount: { + address: account, + chainId: chainId as unknown as MigrateChainId, + }, + onClose: () => setZapOutPureParams(null), + onConnectWallet: toggleWalletModal, + onSwitchChain: () => changeNetwork(zapOutPureParams.chainId as number), + onSubmitTx: async (txData: { from: string; to: string; value: string; data: string }) => { + try { + if (!library) throw new Error('Library is not ready!') + const res = await library?.getSigner().sendTransaction(txData) + if (!res) throw new Error('Transaction failed') + return res.hash + } catch (e) { + console.log(e) + throw e + } + }, + } + : null, + [account, chainId, changeNetwork, library, toggleWalletModal, zapOutPureParams], + ) + + const handleOpenZapOut = (position: { dex: string; chainId: number; poolAddress: string; id: string }) => { + const poolType = (() => { + switch (position?.dex) { + case 'Uniswap V3': + return PoolType.DEX_UNISWAPV3 + case 'SushiSwap V3': + return PoolType.DEX_SUSHISWAPV3 + case 'PancakeSwap V3': + return PoolType.DEX_PANCAKESWAPV3 + default: + return null + } + })() + if (!poolType) { + notify( + { + type: NotificationType.ERROR, + title: 'Pool Type is supported', + }, + 5_000, + ) + return + } + + setZapOutPureParams({ + poolType, + chainId: position.chainId as ChainId, + poolAddress: position.poolAddress, + positionId: position.id, + }) + } + + const previousAccount = usePreviousDistinct(account) + useEffect(() => { + if (account && previousAccount) { + setAddLiquidityPureParams(null) + setMigrateLiquidityPureParams(null) + setZapOutPureParams(null) + } + }, [account, previousAccount]) + + const liquidityWidget = ( + <> + {addLiquidityParams && ( + setAddLiquidityPureParams(null)}> + + + )} + {migrateLiquidityParams && ( + { + setMigrateLiquidityPureParams(null) + setAddLiquidityPureParams(null) + }} + zindex={9999} + > + + + )} + {zapOutParams && ( + setZapOutPureParams(null)}> + + + )} + + ) + + return { liquidityWidget, handleOpenZapInWidget, handleOpenZapOut } +} + +export default useLiquidityWidget diff --git a/src/pages/Earns/useSupportedDexesAndChains.ts b/src/pages/Earns/useSupportedDexesAndChains.ts new file mode 100644 index 0000000000..a2cc039885 --- /dev/null +++ b/src/pages/Earns/useSupportedDexesAndChains.ts @@ -0,0 +1,69 @@ +import { useMemo } from 'react' +import { PoolQueryParams, PositionQueryParams, useSupportedProtocolsQuery } from 'services/zapEarn' + +import { useActiveWeb3React } from 'hooks' +import useChainsConfig from 'hooks/useChainsConfig' + +import { MenuOption } from './PoolExplorer/DropdownMenu' + +export const AllChainsOption = { label: 'All Chains', value: '' } +export const AllProtocolsOption = { label: 'All Protocols', value: '' } + +const useSupportedDexesAndChains = (filters: PoolQueryParams | PositionQueryParams) => { + const { chainId: currentChainId } = useActiveWeb3React() + const { supportedChains } = useChainsConfig() + const { data: supportedProtocols } = useSupportedProtocolsQuery() + + const chains = useMemo(() => { + const parsedChains = supportedChains + .map(chain => ({ + label: chain.name, + value: chain.chainId.toString(), + icon: chain.icon, + })) + .filter(chain => supportedProtocols?.data?.chains?.[chain.value]) + + const allowAllChains = 'chainIds' in filters + return allowAllChains ? [AllChainsOption].concat(parsedChains) : parsedChains + }, [filters, supportedChains, supportedProtocols?.data?.chains]) + + const selectedChainId = useMemo(() => { + if ('chainId' in filters) + return filters.chainId || chains.some(chain => chain.value === currentChainId.toString()) + ? currentChainId + : chains[0]?.value + else if ('chainIds' in filters) return filters.chainIds + + return '' + }, [chains, currentChainId, filters]) + + const supportedDexes = useMemo(() => { + if (!supportedProtocols?.data?.chains) return [] + + let parsedProtocols: MenuOption[] = [] + + if (selectedChainId) + parsedProtocols = + supportedProtocols.data.chains[selectedChainId]?.protocols?.map(item => ({ + label: item.name, + value: item.id.toString(), + })) || [] + else + Object.keys(supportedProtocols.data.chains) + .map(chain => supportedProtocols.data.chains[chain].protocols) + .reduce((a, b) => a.concat(b), []) + .map(item => ({ + label: item.name, + value: item.id.toString(), + })) + .forEach(item => { + if (!parsedProtocols.some(protocol => protocol.value === item.value)) parsedProtocols.push(item) + }) + + return [AllProtocolsOption].concat(parsedProtocols) + }, [selectedChainId, supportedProtocols]) + + return { supportedDexes, supportedChains: chains } +} + +export default useSupportedDexesAndChains diff --git a/src/pages/Earns/utils.ts b/src/pages/Earns/utils.ts new file mode 100644 index 0000000000..94fdfce635 --- /dev/null +++ b/src/pages/Earns/utils.ts @@ -0,0 +1,12 @@ +import { formatDisplayNumber } from 'utils/numbers' + +export const formatAprNumber = (apr: string | number): string => { + const formattedApr = Number(apr) + let n = 0 + while (n < 4) { + if (formattedApr - 10 ** n < 0) break + n++ + } + + return formatDisplayNumber(formattedApr, { significantDigits: n + 2 }) +} diff --git a/src/services/ksSetting.ts b/src/services/ksSetting.ts index 9a45b370a0..57cc9f0460 100644 --- a/src/services/ksSetting.ts +++ b/src/services/ksSetting.ts @@ -211,6 +211,7 @@ export const { useLazyGetKyberswapConfigurationQuery, useGetKyberswapGlobalConfigurationQuery, useLazyGetTokenListQuery, + useGetDexListQuery, useGetTokenListQuery, useImportTokenMutation, useLazyGetTopTokensQuery, diff --git a/src/services/poolService.ts b/src/services/poolService.ts new file mode 100644 index 0000000000..8c47c9d8b2 --- /dev/null +++ b/src/services/poolService.ts @@ -0,0 +1,53 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export interface PoolDetail { + address: string + reserveUsd: string + amplifiedTvl: string + swapFee: number + exchange: string + type: string + reserves: Array + tokens: Array<{ + address: string + name: string + symbol: string + decimals: number + weight: number + swappable: boolean + }> + positionInfo: { + liquidity: string + sqrtPriceX96: string + tickSpacing: number + tick: number + ticks: Array<{ + index: number + liquidityGross: number + liquidityNet: number + }> + } +} + +const poolServiceApi = createApi({ + reducerPath: 'poolServiceApi', + baseQuery: fetchBaseQuery({ baseUrl: import.meta.env.VITE_BFF_API }), + keepUnusedDataFor: 1, + endpoints: builder => ({ + poolDetail: builder.query({ + query: params => ({ + url: `/v1/pools`, + params, + }), + transformResponse: (response: { + data: { + pools: Array + } + }) => response.data.pools?.[0] || {}, + }), + }), +}) + +export const { usePoolDetailQuery } = poolServiceApi + +export default poolServiceApi diff --git a/src/services/zapEarn.ts b/src/services/zapEarn.ts new file mode 100644 index 0000000000..46e96bea5d --- /dev/null +++ b/src/services/zapEarn.ts @@ -0,0 +1,295 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +interface ExplorerLandingResponse { + data: { + highlightedPools: Array + solidEarning: Array + highAPR: Array + lowVolatility: Array + } +} + +interface SupportedChainsResponse { + code: number + message: string + data: { + chains: { + [chainId: string]: { + chainId: number + protocols: Array<{ id: string; name: string }> + } + } + } + requestId: string +} + +export interface PoolQueryParams { + chainId: ChainId + page?: number + limit?: number + interval: string + protocol: string + userAddress?: string + tag?: string + sortBy?: string + orderBy?: string + q?: string +} + +export interface EarnPool { + address: string + earnFee: number + exchange: string + type: string + feeTier: number + volume: number + apr: number + liquidity: number + tvl: number + chainId?: number + favorite?: { + chainId: number + isFavorite: boolean + } + tokens: Array<{ + address: string + logoURI: string + symbol: string + }> +} + +export enum EarnSupportedProtocols { + UNISWAP_V3 = 'Uniswap V3', + PANCAKESWAP_V3 = 'PancakeSwap V3', + SUSHISWAP_V3 = 'SushiSwap V3', +} +export const earnSupportedChains = [ChainId.MAINNET, ChainId.BASE] +export const earnSupportedProtocols = [ + EarnSupportedProtocols.UNISWAP_V3, + EarnSupportedProtocols.PANCAKESWAP_V3, + EarnSupportedProtocols.SUSHISWAP_V3, +] + +export enum PositionStatus { + IN_RANGE = 'IN_RANGE', + OUT_RANGE = 'OUT_RANGE', +} + +export interface PositionAmount { + token: { + address: string + symbol: string + name: string + decimals: number + logo: string + tag: string + price: number + } + tokenType: string + tokenID: string + balance: string + quotes: { + usd: { + symbol: string + marketPrice: number + price: number + priceChange24hPercentage: number + value: number + timestamp: number + } + } +} + +export interface PositionQueryParams { + chainIds?: string + addresses: string + positionId?: string + protocols?: string +} + +export interface EarnPosition { + [x: string]: any + chainName: 'eth' + chainId: number + chainLogo: string + userAddress: string + id: string + tokenAddress: string + tokenId: string + liquidity: string + minPrice: number + maxPrice: number + currentAmounts: Array + providedAmounts: Array + feePending: Array + feesClaimed: Array + farmRewardsPending: Array + farmRewardsClaimed: Array + feeEarned24h: Array + farmReward24h: Array + createdTime: number + lastUpdateBlock: number + openedBlock: number + openedTime: number + closedBlock: number + closedTime: number + closedPrice: number + farming: boolean + impermanentLoss: number + apr: number + feeApr: number + farmApr: number + pnl: number + initialUnderlyingValue: number + currentUnderlyingValue: number + currentPositionValue: number + compareWithHodl: number + returnOnInvestment: number + totalDepositValue: number + totalWithdrawValue: number + yesterdayEarning: number + earning24h: number + earning7d: number + status: PositionStatus + avgConvertPrice: number + isConvertedFromToken0: boolean + gasUsed: number + isSupportAutomation: boolean + hasAutomationOrder: boolean + pool: { + id: string + poolAddress: string + price: number + tokenAmounts: Array + farmRewardTokens: Array + fees: Array + rewards24h: Array + tickSpacing: number + project: string + projectLogo: string + projectAddress: string + showWarning: boolean + tvl: number + farmAddress: string + tag: string + } +} + +export interface PositionEarning { + date: string + timestamp: number + totalFeeEarning: number + totalFarmEarning: number + totalEarning: number + earningByDay: number +} + +interface PoolsExplorerResponse { + code: number + message: string + data: { + pools: Array + pagination: { + totalItems: number + } + } + requestId: string +} + +interface AddRemoveFavoriteParams { + chainId: ChainId + message: string + signature: string + poolAddress: string + userAddress: string +} + +const zapEarnServiceApi = createApi({ + reducerPath: 'zapEarnServiceApi', + baseQuery: fetchBaseQuery({ + baseUrl: import.meta.env.VITE_ZAP_EARN_URL, + }), + keepUnusedDataFor: 1, + endpoints: builder => ({ + explorerLanding: builder.query({ + query: params => ({ + url: `/v1/explorer/landing-page`, + params, + }), + }), + supportedProtocols: builder.query({ + query: () => ({ + url: `/v1/protocol`, + }), + }), + poolsExplorer: builder.query({ + query: params => ({ + url: `/v1/explorer/pools`, + params: { + ...params, + orderBy: params.orderBy?.toUpperCase() || '', + }, + }), + async onQueryStarted(agr, { dispatch, queryFulfilled }) { + try { + await queryFulfilled + } catch { + dispatch( + zapEarnServiceApi.util.upsertQueryData('poolsExplorer', agr, { + data: { pools: [], pagination: { totalItems: 0 } }, + code: 0, + message: '', + requestId: '', + }), + ) + } + }, + }), + userPositions: builder.query, PositionQueryParams>({ + query: params => ({ + url: `/v1/userPositions`, + params: { + ...params, + chainIds: params.chainIds || earnSupportedChains, + protocols: params.protocols || earnSupportedProtocols, + quoteSymbol: 'usd', + offset: 0, + orderBy: 'liquidity', + orderASC: false, + positionStatus: 'open', + }, + }), + transformResponse: (response: { + data: { + positions: Array + } + }) => response.data.positions, + }), + addFavorite: builder.mutation({ + query: body => ({ + method: 'POST', + body, + url: `/v1/favorite`, + }), + }), + removeFavorite: builder.mutation({ + query: body => ({ + method: 'DELETE', + body, + url: `/v1/favorite`, + }), + }), + }), +}) + +export const { + useExplorerLandingQuery, + useSupportedProtocolsQuery, + usePoolsExplorerQuery, + useUserPositionsQuery, + useAddFavoriteMutation, + useRemoveFavoriteMutation, +} = zapEarnServiceApi + +export default zapEarnServiceApi diff --git a/src/state/index.ts b/src/state/index.ts index d7a9ab8863..a554d7022b 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -15,11 +15,13 @@ import ksSettingApi from 'services/ksSetting' import kyberDAO from 'services/kyberDAO' import limitOrderApi from 'services/limitOrder' import marketOverviewApi from 'services/marketOverview' +import poolServiceApi from 'services/poolService' import priceAlertApi from 'services/priceAlert' import referralApi from 'services/referral' import routeApi from 'services/route' import socialApi from 'services/social' import tokenApi from 'services/token' +import zapEarnServiceApi from 'services/zapEarn' import { ENV_LEVEL } from 'constants/env' import { ENV_TYPE } from 'constants/type' @@ -108,6 +110,8 @@ const store = configureStore({ topTokens, [routeApi.reducerPath]: routeApi.reducer, [tokenApi.reducerPath]: tokenApi.reducer, + [zapEarnServiceApi.reducerPath]: zapEarnServiceApi.reducer, + [poolServiceApi.reducerPath]: poolServiceApi.reducer, [referralApi.reducerPath]: referralApi.reducer, [campaignApi.reducerPath]: campaignApi.reducer, [commonServiceApi.reducerPath]: commonServiceApi.reducer, @@ -133,6 +137,8 @@ const store = configureStore({ .concat(routeApi.middleware) .concat(socialApi.middleware) .concat(tokenApi.middleware) + .concat(zapEarnServiceApi.middleware) + .concat(poolServiceApi.middleware) .concat(referralApi.middleware) .concat(campaignApi.middleware) .concat(commonServiceApi.middleware) diff --git a/yarn.lock b/yarn.lock index c975dfa881..bcee02b556 100644 --- a/yarn.lock +++ b/yarn.lock @@ -3765,6 +3765,27 @@ tiny-invariant "^1.1.0" tiny-warning "^1.0.3" +"@kyberswap/liquidity-widgets@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@kyberswap/liquidity-widgets/-/liquidity-widgets-1.1.0.tgz#4d498247fa24ee2c3b987d1e356a45782bee4cae" + integrity sha512-x0HP60neiaVuAUFarghGpbJcY5TVEGMRqRWaznp/UPn9i+xRfy01bl+8RhltyPmNt3tHfhDIqVhdWnJPS8REZg== + dependencies: + "@popperjs/core" "^2.11.8" + "@radix-ui/react-accordion" "^1.2.1" + "@radix-ui/react-icons" "^1.3.0" + "@radix-ui/react-scroll-area" "^1.1.0" + "@radix-ui/react-slot" "^1.1.0" + class-variance-authority "^0.7.0" + clsx "^2.1.1" + d3 "^7.9.0" + jsbi "^3.2.5" + lodash.partition "^4.6.0" + numeral "^2.0.6" + polished "^4.3.1" + react-popper "^2.3.0" + zod "^3.23.8" + zustand "^5.0.0" + "@kyberswap/oauth2@1.0.2": version "1.0.2" resolved "https://registry.yarnpkg.com/@kyberswap/oauth2/-/oauth2-1.0.2.tgz#4e0fdfa9722ba2f185a104293b85b6ca58be775b" @@ -3773,6 +3794,16 @@ axios "1.2.1" client-oauth2 "^4.3.3" +"@kyberswap/zap-migration-widgets@1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@kyberswap/zap-migration-widgets/-/zap-migration-widgets-1.0.0.tgz#2824ed0b52884dfc80abc7792386ea38db25986a" + integrity sha512-27Q9EEgfk6HopaBbWKahPXjfPUfhFrpLRDYqTllk71PvclC4xiqMu3nXRD+yyUyNjNBqVNiQyFn0otDQXDHNEw== + dependencies: + "@popperjs/core" "^2.11.8" + react-popper "^2.3.0" + zod "^3.23.8" + zustand "^5.0.0" + "@leichtgewicht/ip-codec@^2.0.1": version "2.0.4" resolved "https://registry.yarnpkg.com/@leichtgewicht/ip-codec/-/ip-codec-2.0.4.tgz#b2ac626d6cb9c8718ab459166d4bb405b8ffa78b" @@ -4397,6 +4428,11 @@ resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.6.tgz#cee20bd55e68a1720bdab363ecf0c821ded4cd45" integrity sha512-50/17A98tWUfQ176raKiOGXuYpLyyVMkxxG6oylzL3BPOlA6ADGdK7EYunSa4I064xerltq9TGXs8HmOk5E+vw== +"@popperjs/core@^2.11.8": + version "2.11.8" + resolved "https://registry.yarnpkg.com/@popperjs/core/-/core-2.11.8.tgz#6b79032e760a0899cd4204710beede972a3a185f" + integrity sha512-P1st0aksCrn9sGZhp8GMYwBnQsbvAWsZAX44oXNNvLHGqAOcoVxmjZiohstwQ7SqKnbR47akdNi+uleWD8+g6A== + "@protobufjs/aspromise@^1.1.1", "@protobufjs/aspromise@^1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@protobufjs/aspromise/-/aspromise-1.1.2.tgz#9b8b0cc663d669a7d8f6f5d0893a14d348f30fbf" @@ -4457,6 +4493,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/number@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/number/-/number-1.1.0.tgz#1e95610461a09cdf8bb05c152e76ca1278d5da46" + integrity sha512-V3gRzhVNU1ldS5XhAPTom1fOIo4ccrjjJgmE+LI2h/WaFpHmx0MQApT+KZHnx8abG6Avtfcz4WoEciMnpFT3HQ== + "@radix-ui/primitive@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.0.1.tgz#e46f9958b35d10e9f6dc71c497305c22e3e55dbd" @@ -4464,6 +4505,26 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/primitive@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/primitive/-/primitive-1.1.0.tgz#42ef83b3b56dccad5d703ae8c42919a68798bbe2" + integrity sha512-4Z8dn6Upk0qk4P74xBhZ6Hd/w0mPEzOOLxy4xiPXOXqjF7jZS0VAKk7/x/H6FyY2zCkYJqePf1G5KmkmNJ4RBA== + +"@radix-ui/react-accordion@^1.2.1": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-accordion/-/react-accordion-1.2.1.tgz#5c942c42c24267376b26204ec6847b17d15659b3" + integrity sha512-bg/l7l5QzUjgsh8kjwDFommzAshnUsuVMV5NM56QVCm+7ZckYdd9P/ExR8xG/Oup0OajVxNLaHJ1tb8mXk+nzQ== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-collapsible" "1.1.1" + "@radix-ui/react-collection" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-arrow@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-arrow/-/react-arrow-1.0.3.tgz#c24f7968996ed934d57fe6cde5d6ec7266e1d25d" @@ -4472,6 +4533,20 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-collapsible@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collapsible/-/react-collapsible-1.1.1.tgz#1382cc9ec48f8b473c14f3779d317f0cdf6da5e9" + integrity sha512-1///SnrfQHJEofLokyczERxQbWfCGQlQ2XsCZMucVs6it+lq9iw4vXy+uDn1edlb58cOZOWSldnfPAYcT4O/Yg== + dependencies: + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-id" "1.1.0" + "@radix-ui/react-presence" "1.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-controllable-state" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-collection@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.0.3.tgz#9595a66e09026187524a36c6e7e9c7d286469159" @@ -4483,6 +4558,16 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-collection@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-collection/-/react-collection-1.1.0.tgz#f18af78e46454a2360d103c2251773028b7724ed" + integrity sha512-GZsZslMJEyo1VKm5L1ZJY8tGDxZNPAoUeQUIbKeJfoi7Q4kmig5AsgLMYYuyYbfjd8fBmFORAIwYAkXMnXZgZw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.0" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-compose-refs@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.0.1.tgz#7ed868b66946aa6030e580b1ffca386dd4d21989" @@ -4490,6 +4575,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-compose-refs@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-compose-refs/-/react-compose-refs-1.1.0.tgz#656432461fc8283d7b591dcf0d79152fae9ecc74" + integrity sha512-b4inOtiaOnYf9KWyO3jAeeCG6FeyfY6ldiEPanbUjWd+xIk5wZeHa8yVwmrJ2vderhu/BQvzCrJI0lHd+wIiqw== + "@radix-ui/react-context@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.0.1.tgz#fe46e67c96b240de59187dcb7a1a50ce3e2ec00c" @@ -4497,6 +4587,16 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-context@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.0.tgz#6df8d983546cfd1999c8512f3a8ad85a6e7fcee8" + integrity sha512-OKrckBy+sMEgYM/sMmqmErVn0kZqrHPJze+Ql3DzYsDDp0hl0L62nx/2122/Bvps1qz645jlcu2tD9lrRSdf8A== + +"@radix-ui/react-context@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-context/-/react-context-1.1.1.tgz#82074aa83a472353bb22e86f11bcbd1c61c4c71a" + integrity sha512-UASk9zi+crv9WteK/NU4PLvOoL3OuE6BWVKNF6hPRBtYBDXQ2u5iu3O59zUlJiTVvkyuycnqrztsHVJwcK9K+Q== + "@radix-ui/react-direction@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.0.1.tgz#9cb61bf2ccf568f3421422d182637b7f47596c9b" @@ -4504,6 +4604,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-direction@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-direction/-/react-direction-1.1.0.tgz#a7d39855f4d077adc2a1922f9c353c5977a09cdc" + integrity sha512-BUuBvgThEiAXh2DWu93XsT+a3aWrGqolGlqqw5VU1kG7p/ZH2cuDlM1sRLNnY3QcBS69UIz2mcKhMxDsdewhjg== + "@radix-ui/react-dismissable-layer@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-dismissable-layer/-/react-dismissable-layer-1.0.4.tgz#883a48f5f938fa679427aa17fcba70c5494c6978" @@ -4533,6 +4638,11 @@ "@radix-ui/react-primitive" "1.0.3" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-icons@^1.3.0": + version "1.3.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-icons/-/react-icons-1.3.1.tgz#462c85fd726a77854cd5956e29eb19a575aa7ce5" + integrity sha512-QvYompk0X+8Yjlo/Fv4McrzxohDdM5GgLHyQcPpcsPvlOSXCGFjdbuyGL5dzRbg0GpknAjQJJZzdiRK7iWVuFQ== + "@radix-ui/react-id@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.0.1.tgz#73cdc181f650e4df24f0b6a5b7aa426b912c88c0" @@ -4541,6 +4651,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-layout-effect" "1.0.1" +"@radix-ui/react-id@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-id/-/react-id-1.1.0.tgz#de47339656594ad722eb87f94a6b25f9cffae0ed" + integrity sha512-EJUrI8yYh7WOjNOqpoJaf1jlFIH2LvtgAl+YcFqNCa+4hj64ZXmPkAKOFs/ukjz3byN6bdb/AVUqHkI8/uWWMA== + dependencies: + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-popper@1.1.2": version "1.1.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-popper/-/react-popper-1.1.2.tgz#4c0b96fcd188dc1f334e02dba2d538973ad842e9" @@ -4566,6 +4683,14 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-primitive" "1.0.3" +"@radix-ui/react-presence@1.1.1": + version "1.1.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-presence/-/react-presence-1.1.1.tgz#98aba423dba5e0c687a782c0669dcd99de17f9b1" + integrity sha512-IeFXVi4YS1K0wVZzXNrbaaUvIJ3qdY+/Ih4eHFhWA9SwGR9UDX7Ck8abvL57C4cv3wwMvUE0OG69Qc3NCcTe/A== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-primitive@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-1.0.3.tgz#d49ea0f3f0b2fe3ab1cb5667eb03e8b843b914d0" @@ -4574,6 +4699,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-slot" "1.0.2" +"@radix-ui/react-primitive@2.0.0": + version "2.0.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-primitive/-/react-primitive-2.0.0.tgz#fe05715faa9203a223ccc0be15dc44b9f9822884" + integrity sha512-ZSpFm0/uHa8zTvKBDjLFWLo8dkr4MBsiDLz0g3gMUwqgLHz9rTaRRGYDgvZPtBJgYCBKXkS9fzmoySgr8CO6Cw== + dependencies: + "@radix-ui/react-slot" "1.1.0" + "@radix-ui/react-roving-focus@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-roving-focus/-/react-roving-focus-1.0.4.tgz#e90c4a6a5f6ac09d3b8c1f5b5e81aab2f0db1974" @@ -4590,6 +4722,21 @@ "@radix-ui/react-use-callback-ref" "1.0.1" "@radix-ui/react-use-controllable-state" "1.0.1" +"@radix-ui/react-scroll-area@^1.1.0": + version "1.2.1" + resolved "https://registry.yarnpkg.com/@radix-ui/react-scroll-area/-/react-scroll-area-1.2.1.tgz#610c53e07d017e24b62bd73a0a6eb23fa7331b3b" + integrity sha512-FnM1fHfCtEZ1JkyfH/1oMiTcFBQvHKl4vD9WnpwkLgtF+UmnXMCad6ECPTaAjcDjam+ndOEJWgHyKDGNteWSHw== + dependencies: + "@radix-ui/number" "1.1.0" + "@radix-ui/primitive" "1.1.0" + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-context" "1.1.1" + "@radix-ui/react-direction" "1.1.0" + "@radix-ui/react-presence" "1.1.1" + "@radix-ui/react-primitive" "2.0.0" + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-layout-effect" "1.1.0" + "@radix-ui/react-select@^1.2.2": version "1.2.2" resolved "https://registry.yarnpkg.com/@radix-ui/react-select/-/react-select-1.2.2.tgz#caa981fa0d672cf3c1b2a5240135524e69b32181" @@ -4634,6 +4781,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-compose-refs" "1.0.1" +"@radix-ui/react-slot@1.1.0", "@radix-ui/react-slot@^1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-slot/-/react-slot-1.1.0.tgz#7c5e48c36ef5496d97b08f1357bb26ed7c714b84" + integrity sha512-FUCf5XMfmW4dtYl69pdS4DbxKy8nj4M7SafBgPllysxmdachynNflAdp/gCsnYWNDnge6tI9onzMp5ARYc1KNw== + dependencies: + "@radix-ui/react-compose-refs" "1.1.0" + "@radix-ui/react-toggle-group@1.0.4": version "1.0.4" resolved "https://registry.yarnpkg.com/@radix-ui/react-toggle-group/-/react-toggle-group-1.0.4.tgz#f5b5c8c477831b013bec3580c55e20a68179d6ec" @@ -4679,6 +4833,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-callback-ref@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-callback-ref/-/react-use-callback-ref-1.1.0.tgz#bce938ca413675bc937944b0d01ef6f4a6dc5bf1" + integrity sha512-CasTfvsy+frcFkbXtSJ2Zu9JHpN8TYKxkgJGWbjiZhFivxaeW7rMeZt7QELGVLaYVfFMsKHjb7Ak0nMEe+2Vfw== + "@radix-ui/react-use-controllable-state@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.0.1.tgz#ecd2ced34e6330caf89a82854aa2f77e07440286" @@ -4687,6 +4846,13 @@ "@babel/runtime" "^7.13.10" "@radix-ui/react-use-callback-ref" "1.0.1" +"@radix-ui/react-use-controllable-state@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-controllable-state/-/react-use-controllable-state-1.1.0.tgz#1321446857bb786917df54c0d4d084877aab04b0" + integrity sha512-MtfMVJiSr2NjzS0Aa90NPTnvTSg6C/JLCV7ma0W6+OMV78vd8OyRpID+Ng9LxzsPbLeuBnWBA1Nq30AtBIDChw== + dependencies: + "@radix-ui/react-use-callback-ref" "1.1.0" + "@radix-ui/react-use-escape-keydown@1.0.3": version "1.0.3" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-escape-keydown/-/react-use-escape-keydown-1.0.3.tgz#217b840c250541609c66f67ed7bab2b733620755" @@ -4702,6 +4868,11 @@ dependencies: "@babel/runtime" "^7.13.10" +"@radix-ui/react-use-layout-effect@1.1.0": + version "1.1.0" + resolved "https://registry.yarnpkg.com/@radix-ui/react-use-layout-effect/-/react-use-layout-effect-1.1.0.tgz#3c2c8ce04827b26a39e442ff4888d9212268bd27" + integrity sha512-+FPE0rOdziWSrH9athwI1R0HDVbWlEhd+FR+aSDk4uWGmSJ9Z54sdZVDQPZAinJhJXwfT+qnj969mCsT2gfm5w== + "@radix-ui/react-use-previous@1.0.1": version "1.0.1" resolved "https://registry.yarnpkg.com/@radix-ui/react-use-previous/-/react-use-previous-1.0.1.tgz#b595c087b07317a4f143696c6a01de43b0d0ec66" @@ -9249,6 +9420,13 @@ citty@^0.1.5, citty@^0.1.6: dependencies: consola "^3.2.3" +class-variance-authority@^0.7.0: + version "0.7.0" + resolved "https://registry.yarnpkg.com/class-variance-authority/-/class-variance-authority-0.7.0.tgz#1c3134d634d80271b1837452b06d821915954522" + integrity sha512-jFI8IQw4hczaL4ALINxqLEXQbWcNjoSkloa4IaufXCJr6QawJyw7tuRysRsrE8w2p/4gGaxKIt/hX3qz/IbD1A== + dependencies: + clsx "2.0.0" + classlist-polyfill@^1.0.3: version "1.2.0" resolved "https://registry.yarnpkg.com/classlist-polyfill/-/classlist-polyfill-1.2.0.tgz#935bc2dfd9458a876b279617514638bcaa964a2e" @@ -9375,11 +9553,21 @@ clone@^1.0.2: resolved "https://registry.yarnpkg.com/clone/-/clone-1.0.4.tgz#da309cc263df15994c688ca902179ca3c7cd7c7e" integrity sha512-JQHZ2QMW6l3aH/j6xCqQThY/9OH4D/9ls34cgkUBiEeocRTU04tHfKPBsUK1PqZCUQM7GiA0IIXJSuXHI64Kbg== +clsx@2.0.0: + version "2.0.0" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.0.0.tgz#12658f3fd98fafe62075595a5c30e43d18f3d00b" + integrity sha512-rQ1+kcj+ttHG0MKVGBUXwayCCF1oh39BF5COIpRzuCEv8Mwjv0XucrI2ExNTOn9IlLifGClWQcU9BrZORvtw6Q== + clsx@^1.1.1, clsx@^1.2.1: version "1.2.1" resolved "https://registry.yarnpkg.com/clsx/-/clsx-1.2.1.tgz#0ddc4a20a549b59c93a4116bb26f5294ca17dc12" integrity sha512-EcR6r5a8bj6pu3ycsa/E/cKVGuTgZJZdsyUYHOksG/UHIiKfjxzRxYJpyVBwYaQeOvghal9fcc4PidlgzugAQg== +clsx@^2.1.1: + version "2.1.1" + resolved "https://registry.yarnpkg.com/clsx/-/clsx-2.1.1.tgz#eed397c9fd8bd882bfb18deab7102049a2f32999" + integrity sha512-eYm0QWBtUrBWZWG0d386OGAw16Z995PiOVo2B7bjWSbHedGl5e0ZWaq65kOGgUSNesEIDkB9ISbTg/JK9dhCZA== + color-convert@^1.9.0: version "1.9.3" resolved "https://registry.yarnpkg.com/color-convert/-/color-convert-1.9.3.tgz#bb71850690e1f136567de629d2d5471deda4c1e8" @@ -10121,6 +10309,42 @@ d3@^7.6.1: d3-transition "3" d3-zoom "3" +d3@^7.9.0: + version "7.9.0" + resolved "https://registry.yarnpkg.com/d3/-/d3-7.9.0.tgz#579e7acb3d749caf8860bd1741ae8d371070cd5d" + integrity sha512-e1U46jVP+w7Iut8Jt8ri1YsPOvFpg46k+K8TpCb0P+zjCkjkPnV7WzfDJzMHy1LnA+wj5pLT1wjO901gLXeEhA== + dependencies: + d3-array "3" + d3-axis "3" + d3-brush "3" + d3-chord "3" + d3-color "3" + d3-contour "4" + d3-delaunay "6" + d3-dispatch "3" + d3-drag "3" + d3-dsv "3" + d3-ease "3" + d3-fetch "3" + d3-force "3" + d3-format "3" + d3-geo "3" + d3-hierarchy "3" + d3-interpolate "3" + d3-path "3" + d3-polygon "3" + d3-quadtree "3" + d3-random "3" + d3-scale "4" + d3-scale-chromatic "3" + d3-selection "3" + d3-shape "3" + d3-time "3" + d3-time-format "4" + d3-timer "3" + d3-transition "3" + d3-zoom "3" + damerau-levenshtein@^1.0.8: version "1.0.8" resolved "https://registry.yarnpkg.com/damerau-levenshtein/-/damerau-levenshtein-1.0.8.tgz#b43d286ccbd36bc5b2f7ed41caf2d0aba1f8a6e7" @@ -14156,6 +14380,11 @@ lodash.once@^4.1.1: resolved "https://registry.yarnpkg.com/lodash.once/-/lodash.once-4.1.1.tgz#0dd3971213c7c56df880977d504c88fb471a97ac" integrity sha512-Sb487aTOCr9drQVL8pIxOzVhafOjZN9UU54hiN8PU3uAiSV7lx1yYNpbNmex2PK6dSJoNTSJUUswT651yww3Mg== +lodash.partition@^4.6.0: + version "4.6.0" + resolved "https://registry.yarnpkg.com/lodash.partition/-/lodash.partition-4.6.0.tgz#a38e46b73469e0420b0da1212e66d414be364ba4" + integrity sha512-35L3dSF3Q6V1w5j6V3NhNlQjzsRDC/pYKCTdYTmwqSib+Q8ponkAmt/PwEOq3EmI38DSCl+SkIVwLd+uSlVdrg== + lodash.pick@^4.4.0: version "4.4.0" resolved "https://registry.yarnpkg.com/lodash.pick/-/lodash.pick-4.4.0.tgz#52f05610fff9ded422611441ed1fc123a03001b3" @@ -15980,6 +16209,13 @@ polished@^4.2.2: dependencies: "@babel/runtime" "^7.17.8" +polished@^4.3.1: + version "4.3.1" + resolved "https://registry.yarnpkg.com/polished/-/polished-4.3.1.tgz#5a00ae32715609f83d89f6f31d0f0261c6170548" + integrity sha512-OBatVyC/N7SCW/FaDHrSd+vn0o5cS855TOmYi4OkdWUMSJCET/xip//ch8xGUvtr3i44X9LVyWwQlRMTN3pwSA== + dependencies: + "@babel/runtime" "^7.17.8" + pony-cause@^2.1.10: version "2.1.11" resolved "https://registry.yarnpkg.com/pony-cause/-/pony-cause-2.1.11.tgz#d69a20aaccdb3bdb8f74dd59e5c68d8e6772e4bd" @@ -16687,7 +16923,7 @@ react-native-webview@^11.26.0: escape-string-regexp "2.0.0" invariant "2.2.4" -react-popper@^2.2.3: +react-popper@^2.2.3, react-popper@^2.3.0: version "2.3.0" resolved "https://registry.yarnpkg.com/react-popper/-/react-popper-2.3.0.tgz#17891c620e1320dce318bad9fede46a5f71c70ba" integrity sha512-e1hj8lL3uM+sgSR4Lxzn5h1GxBlpa4CQz0XLF8kx4MDrDRWY0Ena4c97PUeSX9i5W3UAfDP0z0FXCTQkoXUl3Q== @@ -19928,11 +20164,21 @@ zksync-web3@^0.14.3: resolved "https://registry.yarnpkg.com/zksync-web3/-/zksync-web3-0.14.4.tgz#0b70a7e1a9d45cc57c0971736079185746d46b1f" integrity sha512-kYehMD/S6Uhe1g434UnaMN+sBr9nQm23Ywn0EUP5BfQCsbjcr3ORuS68PosZw8xUTu3pac7G6YMSnNHk+fwzvg== +zod@^3.23.8: + version "3.24.1" + resolved "https://registry.yarnpkg.com/zod/-/zod-3.24.1.tgz#27445c912738c8ad1e9de1bea0359fa44d9d35ee" + integrity sha512-muH7gBL9sI1nciMZV67X5fTKKBLtwpZ5VBp1vsOQzj1MhrBZ4wlVCm3gedKZWLp0Oyel8sIGfeiz54Su+OVT+A== + zustand@5.0.0: version "5.0.0" resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.0.tgz#71f8aaecf185592a3ba2743d7516607361899da9" integrity sha512-LE+VcmbartOPM+auOjCCLQOsQ05zUTp8RkgwRzefUk+2jISdMMFnxvyTjA4YNWr5ZGXYbVsEMZosttuxUBkojQ== +zustand@^5.0.0: + version "5.0.2" + resolved "https://registry.yarnpkg.com/zustand/-/zustand-5.0.2.tgz#f7595ada55a565f1fd6464f002a91e701ee0cfca" + integrity sha512-8qNdnJVJlHlrKXi50LDqqUNmUbuBjoKLrYQBnoChIbVph7vni+sY+YpvdjXG9YLd/Bxr6scMcR+rm5H3aSqPaw== + zwitch@^2.0.0: version "2.0.4" resolved "https://registry.yarnpkg.com/zwitch/-/zwitch-2.0.4.tgz#c827d4b0acb76fc3e685a4c6ec2902d51070e9d7"