diff --git a/src/assets/images/earn_background_large.png b/src/assets/images/earn_background_large.png
new file mode 100644
index 0000000000..0fb42c55eb
Binary files /dev/null and b/src/assets/images/earn_background_large.png differ
diff --git a/src/assets/images/earn_background_small.png b/src/assets/images/earn_background_small.png
new file mode 100644
index 0000000000..8f8d62233d
Binary files /dev/null and b/src/assets/images/earn_background_small.png differ
diff --git a/src/components/EarnBanner/index.tsx b/src/components/EarnBanner/index.tsx
new file mode 100644
index 0000000000..3be254ecdf
--- /dev/null
+++ b/src/components/EarnBanner/index.tsx
@@ -0,0 +1,237 @@
+import { useEffect, useMemo, useState } from 'react'
+import { useNavigate } from 'react-router-dom'
+import { Flex, Text } from 'rebass'
+import { useExplorerLandingQuery } from 'services/zapEarn'
+import styled, { css, keyframes } from 'styled-components'
+
+import earnLargeBg from 'assets/images/earn_background_large.png'
+import earnSmallBg from 'assets/images/earn_background_small.png'
+import { APP_PATHS } from 'constants/index'
+import { useActiveWeb3React } from 'hooks'
+import { formatAprNumber } from 'pages/Earns/utils'
+
+const EarnBannerContainer = styled.div`
+ padding: 1px;
+ position: relative;
+ background-clip: padding-box;
+ overflow: hidden;
+ margin-bottom: 20px;
+ border-radius: 12px;
+ border: 1px solid transparent;
+ cursor: pointer;
+
+ ::before {
+ content: '';
+ position: absolute;
+ top: 0;
+ left: 0;
+ right: 0;
+ bottom: 0;
+ padding: 1px;
+ background: linear-gradient(306.9deg, #262525 38.35%, rgba(148, 117, 203, 0.2) 104.02%),
+ radial-gradient(58.61% 54.58% at 30.56% 0%, rgba(130, 71, 229, 0.6) 0%, rgba(130, 71, 229, 0) 100%);
+ mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
+ -webkit-mask: linear-gradient(#fff 0 0) padding-box, linear-gradient(#fff 0 0);
+ z-index: -1;
+ }
+`
+
+const EarnBannerWrapper = styled.div`
+ width: 100%;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 1rem;
+ padding: 22px 18px 22px 68px;
+
+ background-image: url(${earnLargeBg});
+ background-position: center;
+ background-size: cover;
+
+ ${({ theme }) => theme.mediaWidth.upToXL`
+ padding: 20px 18px;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToLarge`
+ flex-direction: column;
+ background-image: url(${earnSmallBg});
+ padding: 20px 24px;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToMedium`
+ flex-direction: row;
+ background-image: url(${earnLargeBg});
+ padding: 20px 18px 20px 60px;
+ `}
+
+ @media screen and (max-width: 900px) {
+ padding: 20px 18px;
+ }
+
+ ${({ theme }) => theme.mediaWidth.upToSmall`
+ flex-direction: column;
+ background-image: url(${earnSmallBg});
+ padding: 20px 24px;
+ `}
+`
+
+const Description = styled(Text)`
+ width: 400px;
+
+ ${({ theme }) => theme.mediaWidth.upToXL`
+ width: unset;
+ `}
+`
+
+const PrimaryText = styled.span`
+ color: ${({ theme }) => theme.primary};
+`
+
+const pulse = keyframes`
+ 0% {
+ opacity: 1;
+ }
+ 50% {
+ opacity: 0;
+ }
+ 100% {
+ opacity: 1;
+ }
+`
+
+const PoolButton = styled.div<{ animate: boolean }>`
+ border-radius: 40px;
+ padding: 10px 20px;
+ background: #1d5b49cc;
+ display: flex;
+ align-items: center;
+ justify-content: space-between;
+ gap: 8px;
+ border: 1px solid rgba(25, 103, 80, 1);
+
+ ${({ animate }) =>
+ animate &&
+ css`
+ animation: ${pulse} 0.6s;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToLarge`
+ width: 100%;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToMedium`
+ width: unset;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToSmall`
+ width: 100%;
+ padding: 10px 16px;
+ `}
+
+ ${({ theme }) => theme.mediaWidth.upToExtraSmall`
+ padding: 10px 12px;
+ `}
+`
+
+const TokenImage = styled.img`
+ width: 24px;
+ height: 24px;
+ border-radius: 50%;
+ box-shadow: 0 4px 8px 0 rgba(11, 46, 36, 1);
+
+ &:nth-child(1) {
+ margin-right: -8px;
+ }
+`
+
+const PoolAprWrapper = styled.div`
+ border-radius: 20px;
+ box-shadow: 0 8px 8px 0 rgba(0, 0, 0, 0.3);
+ padding-bottom: 1px;
+ width: auto;
+ overflow: hidden;
+ background-image: linear-gradient(
+ to right,
+ rgba(102, 102, 102, 0),
+ rgba(102, 102, 102, 0),
+ rgba(162, 233, 212, 1),
+ rgba(102, 102, 102, 0),
+ rgba(102, 102, 102, 0)
+ );
+`
+
+const PoolApr = styled.div`
+ display: flex;
+ font-weight: 600;
+ background-color: #000;
+ color: ${({ theme }) => theme.primary};
+ padding: 4px 16px;
+ width: max-content;
+`
+
+const AprText = styled.span`
+ margin-left: 6px;
+ ${({ theme }) => theme.mediaWidth.upToXXSmall`
+ display: none;
+ `}
+`
+
+let indexInterval: NodeJS.Timeout
+
+export default function EarnBanner() {
+ const navigate = useNavigate()
+ const { account } = useActiveWeb3React()
+ const { data } = useExplorerLandingQuery({ userAddress: account })
+
+ const [index, setIndex] = useState(0)
+ const [animate, setAnimate] = useState(false)
+
+ const pool = useMemo(() => data?.data.highlightedPools[index] || null, [data, index])
+
+ useEffect(() => {
+ const handleIndexChange = () => {
+ setAnimate(true)
+ setTimeout(() => setIndex(prev => (prev >= 9 ? 0 : prev + 1)), 200)
+ setTimeout(() => setAnimate(false), 1000)
+ }
+ indexInterval = setInterval(handleIndexChange, 4000)
+
+ return () => clearInterval(indexInterval)
+ }, [])
+
+ return (
+
+ navigate({ pathname: APP_PATHS.EARN })}>
+
+ Explore and Add Liquidity to High-APR Pools Instantly with{' '}
+ Any Token(s) or Position you choose!
+
+ {
+ if (!pool) return
+ e.stopPropagation()
+ navigate({ pathname: APP_PATHS.EARN, search: `?openPool=${index}` })
+ }}
+ >
+ {!!pool && (
+ <>
+
+
+
+
+ {pool.tokens[0].symbol}/{pool.tokens[1].symbol}
+
+
+
+
+ {formatAprNumber(pool.apr)}% APR
+
+
+ >
+ )}
+
+
+
+ )
+}
diff --git a/src/constants/index.ts b/src/constants/index.ts
index d93e90868e..33fbe7e3b2 100644
--- a/src/constants/index.ts
+++ b/src/constants/index.ts
@@ -170,10 +170,13 @@ export const APP_PATHS = {
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',
+ EARN: '/earn',
+ EARN_POOLS: '/earn/pools',
+ EARN_POSITIONS: '/earn/positions',
+ EARN_POSITION_DETAIL: '/earn/position/:chainId/:id',
+ EARNS: '/earns',
+ EARNS_POOLS: '/earns/pools',
+ EARNS_POSITIONS: '/earns/positions',
} as const
export const TERM_FILES_PATH = {
diff --git a/src/pages/App.tsx b/src/pages/App.tsx
index 3bd8dd277a..0e29d5ea27 100644
--- a/src/pages/App.tsx
+++ b/src/pages/App.tsx
@@ -326,6 +326,10 @@ export default function App() {
} />
} />
+ } />
+ } />
+ } />
+
} />
diff --git a/src/pages/Earns/PoolExplorer/index.tsx b/src/pages/Earns/PoolExplorer/index.tsx
index 9fac72e63f..434058cd63 100644
--- a/src/pages/Earns/PoolExplorer/index.tsx
+++ b/src/pages/Earns/PoolExplorer/index.tsx
@@ -1,7 +1,7 @@
import { t } from '@lingui/macro'
import { useEffect, useState } from 'react'
import { Info, Star } from 'react-feather'
-import { useSearchParams } from 'react-router-dom'
+import { useNavigate, useSearchParams } from 'react-router-dom'
import { useMedia } from 'react-use'
import { Flex, Text } from 'rebass'
import { usePoolsExplorerQuery } from 'services/zapEarn'
@@ -20,6 +20,7 @@ import useTheme from 'hooks/useTheme'
import SortIcon, { Direction } from 'pages/MarketOverview/SortIcon'
import { MEDIA_WIDTHS } from 'theme'
+import { IconArrowLeft } from '../PositionDetail/styles'
import useLiquidityWidget from '../useLiquidityWidget'
import useSupportedDexesAndChains from '../useSupportedDexesAndChains'
import DropdownMenu, { MenuOption } from './DropdownMenu'
@@ -88,6 +89,7 @@ const Earn = () => {
const [search, setSearch] = useState('')
const deboundedSearch = useDebounce(search, 300)
const [searchParams] = useSearchParams()
+ const navigate = useNavigate()
const theme = useTheme()
const { filters, updateFilters } = useFilter(setSearch)
const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget()
@@ -139,9 +141,12 @@ const Earn = () => {
{liquidityWidget}
-
- {t`Earning with Smart Liquidity Providing`}
-
+
+ navigate(-1)} />
+
+ {t`Earning with Smart Liquidity Providing`}
+
+
{t`KyberSwap Zap: Instantly add liquidity to high-APY pools using any token(s) or your existing liquidity position with KyberZap`}
diff --git a/src/pages/Earns/PositionDetail/styles.tsx b/src/pages/Earns/PositionDetail/styles.tsx
index 1ee758effe..b31050b6ef 100644
--- a/src/pages/Earns/PositionDetail/styles.tsx
+++ b/src/pages/Earns/PositionDetail/styles.tsx
@@ -6,7 +6,7 @@ export const IconArrowLeft = styled(IconArrowLeftSvg)`
cursor: pointer;
position: relative;
top: 5px;
- color: ${({ theme }) => theme.subText};
+ color: rgba(250, 250, 250, 1);
:hover {
filter: brightness(1.5);
diff --git a/src/pages/Earns/UserPositions/index.tsx b/src/pages/Earns/UserPositions/index.tsx
index 1dc7462d31..1d819a83c6 100644
--- a/src/pages/Earns/UserPositions/index.tsx
+++ b/src/pages/Earns/UserPositions/index.tsx
@@ -19,7 +19,7 @@ import { shortenAddress } from 'utils'
import { formatDisplayNumber } from 'utils/numbers'
import { CurrencyRoundedImage, CurrencySecondImage, Disclaimer } from '../PoolExplorer/styles'
-import { PositionAction as PositionActionBtn } from '../PositionDetail/styles'
+import { IconArrowLeft, PositionAction as PositionActionBtn } from '../PositionDetail/styles'
import useLiquidityWidget from '../useLiquidityWidget'
import useSupportedDexesAndChains from '../useSupportedDexesAndChains'
import Filter from './Filter'
@@ -87,9 +87,12 @@ const MyPositions = () => {
{liquidityWidget}
-
- {t`My Liquidity`}
-
+
+ navigate(-1)} />
+
+ {t`My Liquidity`}
+
+
{t`KyberSwap Zap: Instantly and easily add liquidity to high-APY pools using any token or a combination of tokens.`}
diff --git a/src/pages/Earns/index.tsx b/src/pages/Earns/index.tsx
index 5da15181c8..0fa142af5c 100644
--- a/src/pages/Earns/index.tsx
+++ b/src/pages/Earns/index.tsx
@@ -1,6 +1,7 @@
import { ChainId } from '@kyberswap/ks-sdk-core'
import { rgba } from 'polished'
-import { useNavigate } from 'react-router-dom'
+import { useEffect } from 'react'
+import { useNavigate, useSearchParams } from 'react-router-dom'
import { useMedia } from 'react-use'
import { Box, Flex, Text } from 'rebass'
import { EarnPool, useExplorerLandingQuery } from 'services/zapEarn'
@@ -285,9 +286,11 @@ const Card = ({
export default function Earns() {
const navigate = useNavigate()
+ const [searchParams, setSearchParams] = useSearchParams()
const theme = useTheme()
const { account } = useActiveWeb3React()
const { isLoading, data } = useExplorerLandingQuery({ userAddress: account })
+ const { liquidityWidget, handleOpenZapInWidget } = useLiquidityWidget()
const title = (title: string, tooltip: string, icon: string) => (
<>
@@ -316,8 +319,25 @@ export default function Earns() {
const upToSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToSmall}px)`)
const upToXXSmall = useMedia(`(max-width: ${MEDIA_WIDTHS.upToXXSmall}px)`)
+ useEffect(() => {
+ const poolsToOpen = data?.data?.highlightedPools || []
+ const openPool = searchParams.get('openPool')
+ const openPoolIndex = parseInt(openPool || '', 10)
+
+ if (!isNaN(openPoolIndex) && poolsToOpen.length && poolsToOpen[openPoolIndex]) {
+ searchParams.delete('openPool')
+ setSearchParams(searchParams)
+ handleOpenZapInWidget({
+ exchange: poolsToOpen[openPoolIndex].exchange,
+ chainId: poolsToOpen[openPoolIndex].chainId,
+ address: poolsToOpen[openPoolIndex].address,
+ })
+ }
+ }, [handleOpenZapInWidget, data, searchParams, setSearchParams])
+
return (
+ {liquidityWidget}
Maximize Your Earnings in DeFi
diff --git a/src/pages/SwapV3/index.tsx b/src/pages/SwapV3/index.tsx
index b02b3f72db..879f87b3ed 100644
--- a/src/pages/SwapV3/index.tsx
+++ b/src/pages/SwapV3/index.tsx
@@ -7,6 +7,7 @@ import styled from 'styled-components'
import { ReactComponent as RoutingIcon } from 'assets/svg/routing-icon.svg'
import Banner from 'components/Banner'
+import EarnBanner from 'components/EarnBanner'
import { SwitchLocaleLink } from 'components/SwitchLocaleLink'
import { TutorialIds } from 'components/Tutorial/TutorialSwap/constant'
import GasTokenSetting from 'components/swapv2/GasTokenSetting'
@@ -117,6 +118,7 @@ export default function Swap() {
const isSwapPage = pathname.startsWith(APP_PATHS.SWAP)
const isLimitPage = pathname.startsWith(APP_PATHS.LIMIT)
const isCrossChainPage = pathname.startsWith(APP_PATHS.CROSS_CHAIN)
+ const isPartnerSwap = pathname.startsWith(APP_PATHS.PARTNER_SWAP)
const enableDegenMode = searchParams.get('enableDegenMode') === 'true'
@@ -188,6 +190,7 @@ export default function Swap() {
+ {(isSwapPage || isLimitPage) && !isPartnerSwap && }
{isShowTradeRoutes && isSwapPage && (