lushdog@outlook.com před 3 týdny
rodič
revize
cb19de0fda
2 změnil soubory, kde provedl 169 přidání a 16 odebrání
  1. 52 0
      src/app/api/my-lp/copyInfo/route.ts
  2. 117 16
      src/app/my-lp/page.tsx

+ 52 - 0
src/app/api/my-lp/copyInfo/route.ts

@@ -0,0 +1,52 @@
+import { NextRequest, NextResponse } from 'next/server'
+
+interface RequestBody {
+	parentPositionAddress: string
+	poolAddress: string
+	sortField: string
+	page: number
+	pageSize: number
+}
+
+export async function GET(request: NextRequest) {
+	try {
+		const searchParams = request.nextUrl.searchParams
+		const parentPositionAddress = searchParams.get('parentPositionAddress')
+		const poolAddress = searchParams.get('poolAddress')
+
+		const params: RequestBody = {
+			parentPositionAddress: parentPositionAddress || '',
+			poolAddress: poolAddress || '',
+			sortField: 'liquidity',
+			page: 1,
+			pageSize: 5,
+		}
+
+		const response = await fetch(
+			'https://api2.byreal.io/byreal/api/dex/v2/copyfarmer/top-positions',
+			{
+				method: 'POST',
+				headers: {
+					accept: 'application/json',
+					'content-type': 'application/json',
+				},
+				body: JSON.stringify(params),
+			}
+		)
+		if (!response.ok) {
+			return NextResponse.json(
+				{ retCode: response.status, retMsg: '外部 API 请求失败' },
+				{ status: response.status }
+			)
+		}
+		const data = await response.json()
+
+		return NextResponse.json(data)
+	} catch (error) {
+		console.error('API Route Error:', error)
+		return NextResponse.json(
+			{ retCode: 500, retMsg: '服务器内部错误' },
+			{ status: 500 }
+		)
+	}
+}

+ 117 - 16
src/app/my-lp/page.tsx

@@ -1,6 +1,6 @@
 'use client'
 
-import { useEffect, useState } from 'react'
+import { useEffect, useState, useRef } from 'react'
 import {
 	Button,
 	Modal,
@@ -11,6 +11,7 @@ import {
 	App,
 	Typography,
 	Tag,
+	InputNumber,
 } from 'antd'
 import type { ColumnsType, TablePaginationConfig } from 'antd/es/table'
 import dayjs from 'dayjs'
@@ -40,7 +41,10 @@ interface RecordInfo {
 	totalDeposit: string
 	earnedUsd: string
 	earnedUsdPercent: string
+	allCopyAmount: string
+	allCopys: number
 	openTime: number
+	status: number
 	hasDetail?: boolean
 	priceOutType?: string
 	priceLower?: string
@@ -66,7 +70,7 @@ function MyLPPageContent() {
 	const [poolMap, setPoolMap] = useState<Record<string, LPInfo>>({})
 	const [selectedRowKeys, setSelectedRowKeys] = useState<React.Key[]>([])
 	const [batchClosing, setBatchClosing] = useState(false)
-
+	const inputNumberRef = useRef<React.ComponentRef<typeof InputNumber>>(null)
 	const fetchLPDetail = async (positions: RecordInfo[]) => {
 		message.loading(`正在查询当前页面仓位详细信息,请稍等...`)
 
@@ -81,9 +85,31 @@ function MyLPPageContent() {
 						`/api/my-lp/detail?address=${position.positionAddress}`
 					)
 					const data = await response.json()
+					const detailData = data.result.data
+					const { pool, bonusInfo } = detailData
+
+					const copyInfo = await fetch(
+						`/api/my-lp/copyInfo?parentPositionAddress=${bonusInfo.fromCreatorPosition}&poolAddress=${pool.poolAddress}`
+					)
+					let allCopyAmount = 0
+					const copyInfoData = await copyInfo.json()
+					if (copyInfoData.retCode === 0 && copyInfoData.result) {
+						allCopyAmount = copyInfoData.result.data.records
+							.filter((item: RecordInfo) => item.status === 0)
+							.reduce(
+								(acc: number, curr: RecordInfo) =>
+									acc + Number(curr.liquidityUsd),
+								0
+							)
+					}
+					const newData = {
+						...data.result.data,
+						allCopyAmount: allCopyAmount,
+						allCopys: copyInfoData.result.data.total,
+					}
 					return {
 						positionAddress: position.positionAddress,
-						data: data.result.data,
+						data: newData,
 					}
 				} catch (error) {
 					console.error(`获取仓位 ${position.positionAddress} 详情失败:`, error)
@@ -231,6 +257,52 @@ function MyLPPageContent() {
 			})
 	}
 
+	function handleAddPosition(record: RecordInfo) {
+		message.loading({
+			key: 'addPosition',
+			content: '正在加仓...',
+			duration: 0,
+		})
+		Modal.confirm({
+			title: '加仓金额',
+			content: (
+				<InputNumber
+					placeholder="请输入加仓金额"
+					style={{ width: '100%' }}
+					ref={inputNumberRef}
+				/>
+			),
+			onOk: async () => {
+				const quickCopyAmount = inputNumberRef.current?.value
+				message.destroy('addPosition')
+				return fetch('/api/lp-copy', {
+					method: 'POST',
+					body: JSON.stringify({
+						positionAddress: record.positionAddress,
+						nftMintAddress: record.nftMintAddress,
+						maxUsdValue: quickCopyAmount,
+					}),
+				})
+					.then((res) => res.json())
+					.then((data) => {
+						message.destroy('addPosition')
+						if (data.success) {
+							message.success('加仓成功')
+						} else {
+							message.error(data.error || '加仓失败')
+						}
+					})
+					.catch((err) => {
+						console.error('Error adding position:', err)
+						message.error('加仓失败')
+					})
+			},
+			onCancel: () => {
+				message.destroy('addPosition')
+			},
+		})
+	}
+
 	const columns: ColumnsType<RecordInfo> = [
 		{
 			title: 'LP Token',
@@ -271,11 +343,28 @@ function MyLPPageContent() {
 		// 	),
 		// },
 		{
-			title: 'Liquidity',
+			title: '复制金额/占比',
 			dataIndex: 'liquidityUsd',
 			key: 'liquidityUsd',
-			render: (text: string) => (
-				<span className="font-mono text-sm">${Number(text).toFixed(2)}</span>
+			render: (text: string, record: RecordInfo) => (
+				<span className="font-mono text-sm">
+					${Number(text).toFixed(2)}
+					<span className="text-sm text-blue-500">
+						({((Number(text) / Number(record.allCopyAmount)) * 100).toFixed(2)}
+						%)
+					</span>
+				</span>
+			),
+		},
+		{
+			title: '总复制金额/复制数',
+			dataIndex: 'allCopyAmount',
+			key: 'allCopyAmount',
+			render: (text: string, record: RecordInfo) => (
+				<span className="font-mono text-sm text-orange-500">
+					${Number(text).toFixed(2)}
+					<span className="text-sm text-blue-500">({record.allCopys})</span>
+				</span>
 			),
 		},
 		{
@@ -326,16 +415,16 @@ function MyLPPageContent() {
 				<span className="font-mono text-sm">${Number(text).toFixed(2)}</span>
 			),
 		},
-		{
-			title: 'Earned Percent',
-			dataIndex: 'earnedUsdPercent',
-			key: 'earnedUsdPercent',
-			render: (text: string) => (
-				<span className="font-mono text-sm">
-					{(Number(text) * 100).toFixed(2)}%
-				</span>
-			),
-		},
+		// {
+		// 	title: 'Earned Percent',
+		// 	dataIndex: 'earnedUsdPercent',
+		// 	key: 'earnedUsdPercent',
+		// 	render: (text: string) => (
+		// 		<span className="font-mono text-sm">
+		// 			{(Number(text) * 100).toFixed(2)}%
+		// 		</span>
+		// 	),
+		// },
 		{
 			title: '当前价格',
 			dataIndex: 'price',
@@ -436,6 +525,15 @@ function MyLPPageContent() {
 			key: 'bonusInfo',
 			render: (text: string, record: RecordInfo) => (
 				<div className="flex items-center gap-2">
+					{record?.bonusInfo?.fromCreatorWallet &&
+						record?.bonusInfo?.fromCreatorPosition && (
+							<Typography.Link
+								href={`https://www.byreal.io/en/portfolio?userAddress=${record.bonusInfo.fromCreatorWallet}&tab=current&positionAddress=${record.bonusInfo.fromCreatorPosition}&tokenAddress=${getPoolInfo(record.poolAddress).tokenAAddress}&tokenAddress=${getPoolInfo(record.poolAddress).tokenBAddress}`}
+								target="_blank"
+							>
+								查看上级
+							</Typography.Link>
+						)}
 					<Typography.Link
 						href={`https://www.byreal.io/en/portfolio?userAddress=${record.walletAddress}&tab=current&positionAddress=${record.positionAddress}&tokenAddress=${getPoolInfo(record.poolAddress).tokenAAddress}&tokenAddress=${getPoolInfo(record.poolAddress).tokenBAddress}`}
 						target="_blank"
@@ -445,6 +543,9 @@ function MyLPPageContent() {
 					<Typography.Link onClick={() => handleClosePosition(record)}>
 						快速关仓
 					</Typography.Link>
+					<Typography.Link onClick={() => handleAddPosition(record)}>
+						快速加仓
+					</Typography.Link>
 				</div>
 			),
 		},