diff --git a/apps/next/pages/account/rewards/lock-and-earn.tsx b/apps/next/pages/account/rewards/lock-and-earn.tsx
new file mode 100644
index 000000000..71474a30d
--- /dev/null
+++ b/apps/next/pages/account/rewards/lock-and-earn.tsx
@@ -0,0 +1,29 @@
+import { LockAndEarnScreen } from 'app/features/account/rewards/lock-and-earn/screen'
+import Head from 'next/head'
+import { userProtectedGetSSP } from 'utils/userProtected'
+import type { NextPageWithLayout } from 'next-app/pages/_app'
+import { HomeLayout } from 'app/features/home/layout.web'
+import { ButtonOption, TopNav } from 'app/components/TopNav'
+import { MobileButtonRowLayout } from 'app/components/MobileButtonRowLayout'
+
+export const Page: NextPageWithLayout = () => {
+ return (
+ <>
+
+ Send | Lock and Earn
+
+
+ >
+ )
+}
+
+export const getServerSideProps = userProtectedGetSSP()
+Page.getLayout = (children) => (
+
+ }>
+ {children}
+
+
+)
+
+export default Page
diff --git a/packages/app/components/MobileButtonRowLayout.tsx b/packages/app/components/MobileButtonRowLayout.tsx
index 6b3940642..20054c5e8 100644
--- a/packages/app/components/MobileButtonRowLayout.tsx
+++ b/packages/app/components/MobileButtonRowLayout.tsx
@@ -23,6 +23,7 @@ import { useProfileScreenParams, useRewardsScreenParams } from 'app/routers/para
import { useMonthlyDistributions } from 'app/utils/distributions'
import { DistributionClaimButton } from 'app/features/account/rewards/components/DistributionClaimButton'
import formatAmount from 'app/utils/formatAmount'
+import { LockAndEarnButtons } from 'app/features/account/rewards/lock-and-earn/LockAndEarnButtons'
const Row = styled(XStack, {
w: '100%',
@@ -243,8 +244,60 @@ export const ActivityRewards = ({ children, ...props }: XStackProps) => {
)
}
+export const LockAndEarn = ({ children, ...props }: XStackProps) => {
+ const isPwa = usePwa()
+ const { isLoading } = useUser()
+
+ const { direction } = useScrollDirection()
+
+ return (
+ <>
+ {children}
+
+ {!isLoading && direction !== 'down' && (
+
+
+
+
+
+
+
+
+ )}
+
+ >
+ )
+}
+
export const MobileButtonRowLayout = {
Home: Home,
Profile: Profile,
ActivityRewards: ActivityRewards,
+ LockAndEarn: LockAndEarn,
}
diff --git a/packages/app/components/icons/IconCoin.tsx b/packages/app/components/icons/IconCoin.tsx
index 0f11c4572..1eff46e58 100644
--- a/packages/app/components/icons/IconCoin.tsx
+++ b/packages/app/components/icons/IconCoin.tsx
@@ -3,14 +3,15 @@ import { IconEthereum } from './IconEthereum'
import { IconSend } from './IconSend'
import { IconUSDC } from './IconUSDC'
import { IconSPX6900 } from './IconSPX6900'
+import type { SizeTokens } from 'tamagui'
-const coinSymbolToIcons: Record = {
- USDC: ,
- ETH: ,
- SEND: ,
- SPX: ,
+const coinSymbolToIcons: Record JSX.Element> = {
+ USDC: (size) => ,
+ ETH: (size) => ,
+ SEND: (size) => ,
+ SPX: (size) => ,
}
-export const IconCoin = ({ coin }: { coin: coin }) => {
- return coinSymbolToIcons[coin.symbol]
+export const IconCoin = ({ coin, size }: { coin: coin; size?: SizeTokens }) => {
+ return coinSymbolToIcons[coin.symbol]?.(size || '$2.5')
}
diff --git a/packages/app/components/icons/IconExclamationCircle.tsx b/packages/app/components/icons/IconExclamationCircle.tsx
new file mode 100644
index 000000000..93249a6d2
--- /dev/null
+++ b/packages/app/components/icons/IconExclamationCircle.tsx
@@ -0,0 +1,25 @@
+import type { ColorTokens } from '@my/ui/types'
+import { type IconProps, themed } from '@tamagui/helpers-icon'
+import { memo } from 'react'
+import { Path, Svg } from 'react-native-svg'
+
+const ExclamationCircle = (props) => {
+ const { size, color, ...rest } = props
+ return (
+
+ )
+}
+const IconExclamationCircle = memo(themed(ExclamationCircle))
+export { IconExclamationCircle }
diff --git a/packages/app/components/icons/IconPlusCircle.tsx b/packages/app/components/icons/IconPlusCircle.tsx
new file mode 100644
index 000000000..1ef485b66
--- /dev/null
+++ b/packages/app/components/icons/IconPlusCircle.tsx
@@ -0,0 +1,25 @@
+import type { ColorTokens } from '@my/ui/types'
+import { type IconProps, themed } from '@tamagui/helpers-icon'
+import { memo } from 'react'
+import { Path, Svg } from 'react-native-svg'
+
+const PlusCircle = (props) => {
+ const { size, color, ...rest } = props
+ return (
+
+ )
+}
+const IconPlusCircle = memo(themed(PlusCircle))
+export { IconPlusCircle }
diff --git a/packages/app/components/icons/index.tsx b/packages/app/components/icons/index.tsx
index 2eefa125b..7b111abdc 100644
--- a/packages/app/components/icons/index.tsx
+++ b/packages/app/components/icons/index.tsx
@@ -57,3 +57,5 @@ export { IconInfoCircle } from './IconInfoCircle'
export { IconLeaderboard } from './IconLeaderboard'
export { IconStarOutline } from './IconStarOutline'
export { IconQuestionCircle } from './IconQuestionCircle'
+export { IconPlusCircle } from './IconPlusCircle'
+export { IconExclamationCircle } from './IconExclamationCircle'
diff --git a/packages/app/features/account/rewards/lock-and-earn/LockAndEarnButtons.tsx b/packages/app/features/account/rewards/lock-and-earn/LockAndEarnButtons.tsx
new file mode 100644
index 000000000..c7012a293
--- /dev/null
+++ b/packages/app/features/account/rewards/lock-and-earn/LockAndEarnButtons.tsx
@@ -0,0 +1,13 @@
+import { Button } from '@my/ui'
+
+export const OpenButton = () => (
+
+)
+
+export const LockAndEarnButtons = {
+ OpenButton: OpenButton,
+}
diff --git a/packages/app/features/account/rewards/lock-and-earn/screen.tsx b/packages/app/features/account/rewards/lock-and-earn/screen.tsx
new file mode 100644
index 000000000..607460461
--- /dev/null
+++ b/packages/app/features/account/rewards/lock-and-earn/screen.tsx
@@ -0,0 +1,400 @@
+import {
+ YStack,
+ Card,
+ Paragraph,
+ H2,
+ XStack,
+ Spinner,
+ Tooltip,
+ type TooltipProps,
+ LinkableButton,
+ Button,
+ isWeb,
+ Stack,
+ AnimatePresence,
+ useMedia,
+} from '@my/ui'
+import {
+ IconDollar,
+ IconError,
+ IconExclamationCircle,
+ IconPlus,
+ IconTriangleDown,
+} from 'app/components/icons'
+import { coins, type coin } from 'app/data/coins'
+import { useSendAccount } from 'app/utils/send-accounts'
+import { useBalance, type UseBalanceReturnType } from 'wagmi'
+import { baseMainnet } from '@my/wagmi'
+import formatAmount from 'app/utils/formatAmount'
+import { IconCoin } from 'app/components/icons/IconCoin'
+import { useState } from 'react'
+import { useThemeSetting } from 'app/provider/theme'
+
+export function LockAndEarnScreen() {
+ return (
+
+
+
+
+
+
+
+
+ )
+}
+
+const BalancesSection = () => {
+ return (
+
+
+ Your Balances
+
+
+ {coins.map((coin) => {
+ return
+ })}
+
+
+ )
+}
+
+const TokenBalanceCard = ({
+ coin,
+}: {
+ coin: coin
+}) => {
+ const { data: sendAccount } = useSendAccount()
+ const { gtMd } = useMedia()
+ const balance = useBalance({
+ address: sendAccount?.address,
+ token: coin.token === 'eth' ? undefined : coin.token,
+ query: { enabled: !!sendAccount },
+ chainId: baseMainnet.id,
+ })
+ const iconSize = gtMd ? '$size.3.5' : '$size.1.5'
+
+ return (
+
+
+
+
+
+ {coin.label}
+
+
+
+
+
+ )
+}
+
+const TokenBalance = ({ balance }: { balance: UseBalanceReturnType }) => {
+ if (balance.isError) {
+ return (
+
+
+ --
+
+ }>
+ Error occurred while fetching balance. {balance?.error?.message}
+
+
+ )
+ }
+
+ if (balance.isFetching && balance.isPending) {
+ return
+ }
+ if (balance?.data?.value === undefined) {
+ return <>>
+ }
+ return (
+
+ {formatAmount(
+ (Number(balance.data?.value) / 10 ** (balance.data?.decimals ?? 0)).toString(),
+ 10,
+ 5
+ )}
+
+ )
+}
+
+// @TODO: This is duplicated from TokenBalanceList
+const ErrorTooltip = ({ Icon, children, ...props }: TooltipProps & { Icon?: JSX.Element }) => {
+ return (
+
+ {Icon}
+
+
+ {children}
+
+
+
+ )
+}
+
+const PositionsSection = () => {
+ const [positionsCount, setPositionsCount] = useState(0)
+
+ return (
+
+
+
+ Your Positions [{positionsCount}]
+
+
+
+
+
+ {positionsCount > 0 ? (
+ [...Array(positionsCount)].map((n) => {
+ return
+ })
+ ) : (
+
+ You don't have any open positions.
+
+ )}
+
+
+
+
+ )
+}
+
+const PositionCard = () => {
+ const [isOpen, setIsOpen] = useState(false)
+ const [isClaimed, setIsClaimed] = useState(false)
+ const { resolvedTheme } = useThemeSetting()
+
+ return (
+
+
+
+
+ USDC/SEND
+
+
+
+
+
+
+
+
+
+ {isOpen && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+
+
+ {isOpen && (
+ <>
+
+
+ >
+ )}
+
+
+
+
+ )
+}
+
+const AddLiquidityButton = () => {
+ return (
+
+ )
+}
+
+const ClosePositionButton = () => {
+ return (
+
+ )
+}
+
+const PositionCardLineText = ({ left, right }: { left: string; right: string }) => {
+ return (
+
+
+ {left}
+
+ {right}
+
+ )
+}
+
+const OpenPosition = () => {
+ return (
+
+
+
+
+
+
+
+
+
+ Open a New Position
+
+
+
+ )
+}
diff --git a/packages/app/features/account/rewards/screen.tsx b/packages/app/features/account/rewards/screen.tsx
index a574be879..4ff57ae8e 100644
--- a/packages/app/features/account/rewards/screen.tsx
+++ b/packages/app/features/account/rewards/screen.tsx
@@ -46,7 +46,7 @@ export function RewardsScreen() {
- {/* @TODO: href, reward */}
+ {/* @TODO: reward */}