diff --git a/.env b/.env index 1a752e7625..f56ab031b2 100644 --- a/.env +++ b/.env @@ -47,3 +47,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://pre-zap-earn-service.kyberengineering.io/api diff --git a/.env.dev b/.env.dev index 1aa6773ffa..74ea22a571 100644 --- a/.env.dev +++ b/.env.dev @@ -48,3 +48,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://pre-zap-earn-service.kyberengineering.io/api diff --git a/.env.production b/.env.production index 474c13ba1a..7bf1468f11 100644 --- a/.env.production +++ b/.env.production @@ -47,3 +47,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://pre-zap-earn-service.kyberengineering.io/api diff --git a/.env.stg b/.env.stg index eebf91f8ec..324cedd5db 100644 --- a/.env.stg +++ b/.env.stg @@ -45,3 +45,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://pre-zap-earn-service.kyberengineering.io/api 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/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/pages/Earns/index.tsx b/src/pages/Earns/index.tsx index 8bea6a50b0..b0e2fa63da 100644 --- a/src/pages/Earns/index.tsx +++ b/src/pages/Earns/index.tsx @@ -1,12 +1,21 @@ +import { ChainId } from '@kyberswap/ks-sdk-core' +import { rgba } from 'polished' import { Box, Flex, Text } from 'rebass' -import styled from 'styled-components' +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 { NETWORKS_INFO } from 'hooks/useChainsConfig' import useTheme from 'hooks/useTheme' const WrapperBg = styled.div` @@ -23,18 +32,92 @@ const Container = styled.div` text-align: center; ` +/* 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%); + -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 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%); + -webkit-mask-composite: destination-out; + z-index: -1; /* Position behind the content */ + } +` + const CardWrapper = styled.div` border-radius: 20px; - border: 1px solid; - border-image-source: 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%); - background: linear-gradient(119.08deg, rgba(20, 29, 27, 0.8) -0.89%, rgba(14, 14, 14, 0.8) 132.3%); - padding-left: 36px; - padding-bottom: 44px; + + 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; cursor: url(${CursorIcon}), auto; button { @@ -42,6 +125,56 @@ const CardWrapper = styled.div` } ` +const ListPoolWrapper = styled.div` + padding: 20px; + border-radius: 20px; + background: linear-gradient(119.08deg, rgba(20, 29, 27, 1) -0.89%, rgba(14, 14, 14, 1) 132.3%); + cursor: url(${CursorIcon}), auto; +` + +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, @@ -55,40 +188,48 @@ const Card = ({ }) => { const theme = useTheme() return ( - - - - - - icon - + + + + + - - - {title} - - - {desc} - - - {action.text} - - + + {title} + + + {desc} + + + {action.text} + + + ) } export default function Earns() { const theme = useTheme() + const { data } = useExplorerLandingQuery() + console.log(data) + + const title = (_title: string, icon: string) => ( + <> + + + {_title} + + + + ) return ( @@ -138,7 +279,164 @@ export default function Earns() { }} /> + + + { + // TODO:: go to explorer page + }} + > + {title('Highlighted Pools', FireIcon)} + + {data?.data?.highlightedPools.map(pool => ( + + ))} + + + + + + + { + // TODO:: go to explorer page + }} + > + {title('High APR', RocketIcon)} + + {data?.data?.highAPR.map(pool => ( + + ))} + + + + + + { + // TODO:: go to explorer page + }} + > + {title('Low Volatility', LowVolatilityIcon)} + + {data?.data?.lowVolatility.map(pool => ( + + ))} + + + + + + { + // TODO:: go to explorer page + }} + > + {title('Solid Earning', SolidEarningIcon)} + + {data?.data?.solidEarning.map(pool => ( + + ))} + + + + + + { + // TODO: go to explorer page + }} + 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() + return ( + { + e.stopPropagation() + // TODO: open zap in widget + }} + > + + + + + + {pool.tokens[0].symbol} /{' '} + + {pool.tokens[1].symbol} + + + {pool.feeTier * 100}% + + {pool.apr}% + + ) +} diff --git a/src/services/zapEarn.ts b/src/services/zapEarn.ts new file mode 100644 index 0000000000..fe9386b08e --- /dev/null +++ b/src/services/zapEarn.ts @@ -0,0 +1,43 @@ +import { createApi, fetchBaseQuery } from '@reduxjs/toolkit/query/react' + +export interface EarnPool { + exchange: string + address: string + type: string + feeTier: number // need mulpliple with 100 to percent + chainId: number + apr: number + tokens: Array<{ + address: string + logoURI: string + symbol: string + }> +} + +interface Response { + data: { + highlightedPools: Array + solidEarning: Array + highAPR: Array + lowVolatility: Array + } +} + +const zapEarnServiceApi = createApi({ + reducerPath: 'zapEarnServiceApi ', + baseQuery: fetchBaseQuery({ + baseUrl: import.meta.env.VITE_ZAP_EARN_URL, + }), + keepUnusedDataFor: 1, + endpoints: builder => ({ + explorerLanding: builder.query({ + query: () => ({ + url: `/v1/explorer/landing-page`, + }), + }), + }), +}) + +export const { useExplorerLandingQuery } = zapEarnServiceApi + +export default zapEarnServiceApi diff --git a/src/state/index.ts b/src/state/index.ts index d7a9ab8863..3b54ed47ba 100644 --- a/src/state/index.ts +++ b/src/state/index.ts @@ -20,6 +20,7 @@ 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 +109,7 @@ const store = configureStore({ topTokens, [routeApi.reducerPath]: routeApi.reducer, [tokenApi.reducerPath]: tokenApi.reducer, + [zapEarnServiceApi.reducerPath]: zapEarnServiceApi.reducer, [referralApi.reducerPath]: referralApi.reducer, [campaignApi.reducerPath]: campaignApi.reducer, [commonServiceApi.reducerPath]: commonServiceApi.reducer, @@ -133,6 +135,7 @@ const store = configureStore({ .concat(routeApi.middleware) .concat(socialApi.middleware) .concat(tokenApi.middleware) + .concat(zapEarnServiceApi.middleware) .concat(referralApi.middleware) .concat(campaignApi.middleware) .concat(commonServiceApi.middleware)