import { TOKEN_MAP, SMART_WALLET } from './const.js' const API_URL = 'https://app-byreal-table.trrlzk.easypanel.host/api/top-positions' const DISCORD_WEBHOOK_URL = 'https://discord.com/api/webhooks/1449354624225120378/7gOkPXzZkaoQF7XWKFZRsevVSKXcUOuim2Rhtx_gWzBd9b7wphFMQiAIqBnRT3Gmkog4' const DISCORD_WEBHOOK_URL_SMART_WALLET = 'https://discord.com/api/webhooks/1450301829064954040/zNYc-mnYPDFgnAPLZxY40aicERwdL4TsvXk9VyiqWgyjRqJMXKIBzVHEvBiHbpbJrmtu' const DEFAULT_POOL_ADDRESS = TOKEN_MAP['MON'] const INTERVAL_MS = 1 * 60 * 1000 // 1分钟 const MAX_AGE_MS = 10 * 60 * 1000 // 10分钟 // 存储已发送的 positionAddress const sentPositions = new Set() /** * 获取 top-positions 数据 */ async function fetchTopPositions(token_symbol) { try { const response = await fetch(API_URL, { method: 'POST', headers: { 'content-type': 'application/json', }, body: JSON.stringify({ poolAddress: TOKEN_MAP[token_symbol], page: 1, pageSize: 200, sortField: 'liquidity', status: 0, }), }) if (!response.ok) { throw new Error(`HTTP error! status: ${response.status}`) } const result = await response.json() if (result.retCode !== 0 || !result.result?.data?.records) { console.error('API 返回错误:', result.retMsg || '未知错误') return [] } const poolMap = Object.values(result.result.data.poolMap)[0] return { records: result.result.data.records, symbol: `${poolMap.mintA.symbol}/${poolMap.mintB.symbol}`, } } catch (error) { console.error('获取数据失败:', error) return [] } } /** * 发送消息到 Discord */ async function sendToDiscord(position, symbol) { try { const ageMinutes = Math.floor(position.positionAgeMs / 1000 / 60) const ageSeconds = Math.floor((position.positionAgeMs / 1000) % 60) const embed = { title: '🆕 新仓位发现', color: 0x00b098, // 绿色 fields: [ { name: '创建地址', value: `\`${position.walletAddress}\``, inline: true, }, { name: '仓位地址', value: `\`${position.positionAddress}\``, inline: false, }, { name: '地址', value: `https://www.byreal.io/en/portfolio?userAddress=${position.walletAddress}&tab=current&positionAddress=${position.positionAddress}`, inline: false, }, { name: '交易对', value: `\`${symbol}\``, inline: true, }, { name: '流动性', value: `$${Number(position.liquidityUsd).toFixed(2)}`, inline: true, }, { name: '复制数', value: `${position.copies}`, inline: true, }, { name: '奖励', value: `$${Number(position.bonusUsd).toFixed(2)}`, inline: true, }, { name: 'FEE', value: `$${Number(position.earnedUsd).toFixed(2)}`, inline: true, }, { name: 'PNL', value: `$${Number(position.pnlUsd).toFixed(2)}`, inline: true, }, { name: '创建时间', value: `${ageMinutes}分${ageSeconds}秒`, inline: true, }, { name: 'APR', value: calculateAPR(position), inline: true, }, ], timestamp: new Date().toISOString(), url: `https://www.byreal.io/en/portfolio?userAddress=${position.walletAddress}&tab=current&positionAddress=${position.positionAddress}`, } let DISCORD_WEBHOOK = SMART_WALLET.includes(position.walletAddress) ? DISCORD_WEBHOOK_URL_SMART_WALLET : DISCORD_WEBHOOK_URL if (SMART_WALLET.includes(position.walletAddress)) { embed.title = '💎 发现重点监控仓位' } const response = await fetch(DISCORD_WEBHOOK, { method: 'POST', headers: { 'content-type': 'application/json', }, body: JSON.stringify({ embeds: [embed], }), }) if (!response.ok) { throw new Error(`Discord webhook 失败: ${response.status}`) } console.log(`✅ 已发送到 Discord: ${position.positionAddress}`) return true } catch (error) { console.error('发送到 Discord 失败:', error) return false } } /** * 计算 APR */ function calculateAPR(position) { const earnedPerSecond = Number(position.earnedUsd) / (Number(position.positionAgeMs) / 1000) const apr = (earnedPerSecond * 60 * 60 * 24 * 365) / Number(position.liquidityUsd) return `${(apr * 100).toFixed(2)}%` } /** * 处理新仓位 */ async function processPositions(token_symbol) { console.log(`\n[${new Date().toLocaleString('zh-CN')}] 开始检查新仓位...`) const { records, symbol } = await fetchTopPositions(token_symbol) if (!records) { console.log('获取数据失败') return } // 过滤出10分钟以内的仓位 const newPositions = records.filter((record) => { const ageMs = Number(record.positionAgeMs) return ageMs < MAX_AGE_MS && ageMs >= 0 }) console.log(`找到 ${newPositions.length} 个10分钟内的仓位`) // 过滤出未发送过的仓位 const unsentPositions = newPositions.filter((position) => { const address = position.positionAddress return address && !sentPositions.has(address) }) console.log(`其中 ${unsentPositions.length} 个未发送过`) // 发送到 Discord for (const position of unsentPositions) { const address = position.positionAddress if (address) { const success = await sendToDiscord(position, symbol) if (success) { sentPositions.add(address) } // 避免发送过快,稍微延迟 await new Promise((resolve) => setTimeout(resolve, 500)) } } console.log(`处理完成,已发送 ${unsentPositions.length} 个新仓位`) } // 启动定时任务 console.log('🚀 监控程序已启动') console.log(`- 检查间隔: ${INTERVAL_MS / 1000 / 60} 分钟`) console.log(`- 监控池地址: ${DEFAULT_POOL_ADDRESS}`) console.log(`- 最大年龄: ${MAX_AGE_MS / 1000 / 60} 分钟`) console.log(`- Discord Webhook: ${DISCORD_WEBHOOK_URL}`) // 立即执行一次 processPositions('MON') processPositions('WET') // 每5分钟执行一次 setInterval(() => { processPositions('MON') processPositions('WET') }, INTERVAL_MS)