2026-01-04 12:49:20 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 格式化数字,自动去除尾随零
|
|
|
|
|
|
* @param value - 数字值(字符串或数字)
|
|
|
|
|
|
* @param maxDecimals - 最大小数位数(默认不限制)
|
|
|
|
|
|
* @returns 格式化后的字符串,如果值为空或无效则返回 ''
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* formatNumber(100.00) => "100"
|
|
|
|
|
|
* formatNumber(100.50) => "100.5"
|
|
|
|
|
|
* formatNumber(100.55) => "100.55"
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const formatNumber = (value: string | number | undefined | null, maxDecimals?: number): string => {
|
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const num = typeof value === 'string' ? parseFloat(value) : value
|
|
|
|
|
|
if (isNaN(num)) {
|
|
|
|
|
|
return ''
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果有最大小数位数限制,先截断
|
|
|
|
|
|
if (maxDecimals !== undefined) {
|
|
|
|
|
|
const multiplier = Math.pow(10, maxDecimals)
|
|
|
|
|
|
const truncated = Math.floor(num * multiplier) / multiplier
|
|
|
|
|
|
return truncated.toFixed(maxDecimals).replace(/\.?0+$/, '')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 直接转换为字符串,然后去除尾随零
|
|
|
|
|
|
return num.toString().replace(/\.?0+$/, '')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2025-12-01 23:22:18 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 格式化 USDC 金额
|
|
|
|
|
|
* 最多显示 4 位小数,自动去除尾随零(截断,不四舍五入)
|
|
|
|
|
|
* @param value - 金额值(字符串或数字)
|
|
|
|
|
|
* @returns 格式化后的字符串,如果值为空或无效则返回 '-'
|
|
|
|
|
|
* @example
|
|
|
|
|
|
* formatUSDC(1.23) => "1.23"
|
|
|
|
|
|
* formatUSDC(1.23456) => "1.2345"
|
|
|
|
|
|
* formatUSDC(1.2) => "1.2"
|
|
|
|
|
|
* formatUSDC(1) => "1"
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const formatUSDC = (value: string | number | undefined | null): string => {
|
|
|
|
|
|
if (value === undefined || value === null || value === '') {
|
|
|
|
|
|
return '-'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
const num = typeof value === 'string' ? parseFloat(value) : value
|
|
|
|
|
|
if (isNaN(num)) {
|
|
|
|
|
|
return '-'
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 Math.floor 截断到4位小数(不四舍五入)
|
|
|
|
|
|
const multiplier = Math.pow(10, 4)
|
|
|
|
|
|
const truncated = Math.floor(num * multiplier) / multiplier
|
|
|
|
|
|
|
|
|
|
|
|
// 使用 toFixed(4) 确保格式一致,然后去除尾随零和小数点
|
|
|
|
|
|
return truncated.toFixed(4).replace(/\.?0+$/, '')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 统一导出 ethers 相关工具函数
|
|
|
|
|
|
export {
|
|
|
|
|
|
getAddressFromPrivateKey,
|
|
|
|
|
|
getAddressFromMnemonic,
|
|
|
|
|
|
getPrivateKeyFromMnemonic,
|
|
|
|
|
|
isValidMnemonic,
|
|
|
|
|
|
isValidWalletAddress,
|
|
|
|
|
|
isValidPrivateKey
|
|
|
|
|
|
} from './ethers'
|
|
|
|
|
|
|
2025-12-03 00:24:46 +08:00
|
|
|
|
// 统一导出 auth 相关工具函数
|
|
|
|
|
|
export {
|
|
|
|
|
|
getToken,
|
|
|
|
|
|
setToken,
|
|
|
|
|
|
removeToken,
|
|
|
|
|
|
hasToken
|
|
|
|
|
|
} from './auth'
|
|
|
|
|
|
|
2025-12-07 16:46:18 +08:00
|
|
|
|
// 统一导出 version 相关工具函数
|
|
|
|
|
|
export {
|
|
|
|
|
|
getVersionInfo,
|
|
|
|
|
|
getVersionText,
|
|
|
|
|
|
getGitHubTagUrl
|
|
|
|
|
|
} from './version'
|
|
|
|
|
|
|
2026-01-08 11:56:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 检查订单ID是否为自动生成的
|
|
|
|
|
|
* 自动生成的订单ID通常以 "AUTO_" 或 "AUTO_FIFO_" 开头
|
|
|
|
|
|
* @param orderId - 订单ID
|
|
|
|
|
|
* @returns 如果是自动生成的订单ID,返回 true,否则返回 false
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const isAutoGeneratedOrderId = (orderId: string | undefined | null): boolean => {
|
|
|
|
|
|
if (!orderId) return false
|
|
|
|
|
|
return orderId.startsWith('AUTO_') || orderId.startsWith('AUTO_FIFO_')
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 12:39:09 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 构建 Polymarket 市场 URL
|
2026-01-08 13:19:52 +08:00
|
|
|
|
* 对于 moneyline 市场,跳转到 moneyline 页面
|
|
|
|
|
|
* 注意:目前无法自动判断市场是否为 moneyline,需要后端提供标识
|
2026-01-08 17:47:00 +08:00
|
|
|
|
* @param marketSlug - 市场 slug(用于显示)
|
|
|
|
|
|
* @param eventSlug - 跳转用的 slug(从 events[0].slug 获取,优先使用)
|
2026-01-08 12:39:09 +08:00
|
|
|
|
* @param marketCategory - 市场分类(sports, crypto 等)
|
|
|
|
|
|
* @param marketId - 市场ID(作为后备)
|
2026-01-08 13:19:52 +08:00
|
|
|
|
* @param isMoneyline - 是否为 moneyline 市场(需要后端提供)
|
2026-01-08 12:39:09 +08:00
|
|
|
|
* @returns Polymarket URL,如果无法构建则返回 null
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const getPolymarketUrl = (
|
|
|
|
|
|
marketSlug?: string | null,
|
2026-01-08 17:47:00 +08:00
|
|
|
|
eventSlug?: string | null, // 跳转用的 slug(优先使用)
|
2026-01-08 13:19:52 +08:00
|
|
|
|
_marketCategory?: string | null, // 保留参数以便未来使用
|
|
|
|
|
|
marketId?: string | null,
|
|
|
|
|
|
isMoneyline?: boolean
|
2026-01-08 12:39:09 +08:00
|
|
|
|
): string | null => {
|
2026-01-08 17:47:00 +08:00
|
|
|
|
// 优先使用 eventSlug(跳转用的 slug)
|
|
|
|
|
|
const slug = eventSlug || marketSlug
|
|
|
|
|
|
|
|
|
|
|
|
if (slug) {
|
2026-01-08 13:19:52 +08:00
|
|
|
|
// 如果是 moneyline 市场,跳转到 moneyline 页面
|
|
|
|
|
|
if (isMoneyline === true) {
|
2026-01-08 17:47:00 +08:00
|
|
|
|
return `https://polymarket.com/event/${slug}/moneyline`
|
2026-01-08 12:39:09 +08:00
|
|
|
|
}
|
2026-01-08 13:19:52 +08:00
|
|
|
|
// 其他市场跳转到普通市场页面
|
2026-01-08 17:47:00 +08:00
|
|
|
|
return `https://polymarket.com/event/${slug}`
|
2026-01-08 12:39:09 +08:00
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
// 如果没有 slug,使用 marketId(作为后备)
|
|
|
|
|
|
if (marketId && marketId.startsWith('0x')) {
|
|
|
|
|
|
return `https://polymarket.com/condition/${marketId}`
|
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
|
|
return null
|
|
|
|
|
|
}
|
|
|
|
|
|
|
2026-01-08 11:56:52 +08:00
|
|
|
|
/**
|
|
|
|
|
|
* 复制文本到剪贴板
|
|
|
|
|
|
* @param text - 要复制的文本
|
|
|
|
|
|
* @returns Promise<boolean> - 复制成功返回 true,失败返回 false
|
|
|
|
|
|
*/
|
|
|
|
|
|
export const copyToClipboard = async (text: string): Promise<boolean> => {
|
|
|
|
|
|
try {
|
|
|
|
|
|
if (navigator.clipboard && navigator.clipboard.writeText) {
|
|
|
|
|
|
await navigator.clipboard.writeText(text)
|
|
|
|
|
|
return true
|
|
|
|
|
|
} else {
|
|
|
|
|
|
// 降级方案:使用传统的 document.execCommand
|
|
|
|
|
|
const textArea = document.createElement('textarea')
|
|
|
|
|
|
textArea.value = text
|
|
|
|
|
|
textArea.style.position = 'fixed'
|
|
|
|
|
|
textArea.style.left = '-999999px'
|
|
|
|
|
|
textArea.style.top = '-999999px'
|
|
|
|
|
|
document.body.appendChild(textArea)
|
|
|
|
|
|
textArea.focus()
|
|
|
|
|
|
textArea.select()
|
|
|
|
|
|
const successful = document.execCommand('copy')
|
|
|
|
|
|
document.body.removeChild(textArea)
|
|
|
|
|
|
return successful
|
|
|
|
|
|
}
|
|
|
|
|
|
} catch (error) {
|
|
|
|
|
|
console.error('复制到剪贴板失败:', error)
|
|
|
|
|
|
return false
|
|
|
|
|
|
}
|
|
|
|
|
|
}
|
|
|
|
|
|
|