-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
webkubor
authored and
webkubor
committed
Jan 8, 2025
1 parent
b631017
commit 9a49002
Showing
11 changed files
with
273 additions
and
292 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1 +1,2 @@ | ||
VITE_BASE_WEB_VERSION = v0.1.0 | ||
VITE_BASE_WEB_VERSION = v0.1.0 | ||
VITE_INFURA_KEY = YOUR_INFURA_PROJECT_ID |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,10 +1,7 @@ | ||
import { createApp } from 'vue' | ||
import App from './App.vue' | ||
import router from './router' | ||
import { startMonitoring } from './services/monitorService' | ||
|
||
const app = createApp(App); | ||
app.use(router).mount('#app') | ||
|
||
// 启动 USDT 交易监控 | ||
startMonitoring() |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,76 +1,67 @@ | ||
import { ref } from 'vue' | ||
import { ethers } from 'ethers' | ||
import ERC20 from '@/abis/ERC20.json' | ||
import { USDT_CONTRACTS } from '@/utils/networks' | ||
import { getTransactions as getEthTransactions } from '@/contract/eth' | ||
import { getTransactions as getBscTransactions } from '@/contract/bsc' | ||
|
||
// 监控地址列表 | ||
const monitoredAddresses = ref<string[]>([]) | ||
|
||
// 交易记录 | ||
const transactionRecords = ref<any[]>([]) | ||
|
||
// USDT 合约地址 | ||
const USDT_CONTRACT_ADDRESS = '0xdAC17F958D2ee523a2206206994597C13D831ec7' | ||
interface Transaction { | ||
hash: string | ||
timestamp: number | ||
amount: string | ||
status: string | ||
from: string | ||
to: string | ||
} | ||
|
||
// 初始化 provider | ||
const provider = new ethers.providers.InfuraProvider('mainnet', process.env.VITE_INFURA_PROJECT_ID) | ||
/** | ||
* 获取监控服务实例 | ||
*/ | ||
export const useMonitorService = () => { | ||
const loading = ref(false) | ||
const error = ref(null) | ||
|
||
// 初始化 USDT 合约 | ||
const usdtContract = new ethers.Contract( | ||
USDT_CONTRACT_ADDRESS, | ||
ERC20.abi, | ||
provider | ||
) | ||
/** | ||
* 获取指定地址的 USDT 交易记录 | ||
* @param {string} address - 监控地址 | ||
* @param {string} chain - 链类型 (ETH/BSC) | ||
* @returns {Promise<Transaction[]>} 交易记录数组 | ||
*/ | ||
const getTransactions = async (address: string, chain: string): Promise<Transaction[]> => { | ||
try { | ||
loading.value = true | ||
error.value = null | ||
|
||
// 添加监控地址 | ||
export function addMonitoredAddress(address: string) { | ||
if (!monitoredAddresses.value.includes(address)) { | ||
monitoredAddresses.value.push(address) | ||
startWatchingAddress(address) | ||
} | ||
} | ||
const contractAddress = USDT_CONTRACTS[chain] | ||
if (!contractAddress) { | ||
throw new Error('不支持的链类型') | ||
} | ||
|
||
// 获取监控地址列表 | ||
export function getMonitoredAddresses() { | ||
return monitoredAddresses.value | ||
} | ||
let transactions: Transaction[] = [] | ||
switch (chain) { | ||
case 'ETH': | ||
transactions = await getEthTransactions(address, contractAddress) | ||
break | ||
case 'BSC': | ||
transactions = await getBscTransactions(address, contractAddress) | ||
break | ||
default: | ||
throw new Error('不支持的链类型') | ||
} | ||
|
||
// 获取交易记录 | ||
export function getTransactionRecords() { | ||
return transactionRecords.value | ||
} | ||
|
||
// 启动地址监控 | ||
function startWatchingAddress(address: string) { | ||
// 监听 Transfer 事件 | ||
const filter = usdtContract.filters.Transfer(null, address) | ||
|
||
usdtContract.on(filter, (from, to, value, event) => { | ||
const record = { | ||
txHash: event.transactionHash, | ||
from: from, | ||
to: to, | ||
value: ethers.utils.formatUnits(value, 6), // USDT 6 decimals | ||
timestamp: Math.floor(Date.now() / 1000) | ||
return transactions.map(tx => ({ | ||
...tx, | ||
status: tx.status ? '成功' : '失败' | ||
})) | ||
} catch (err) { | ||
error.value = err.message | ||
throw err | ||
} finally { | ||
loading.value = false | ||
} | ||
|
||
transactionRecords.value.unshift(record) | ||
}) | ||
} | ||
|
||
// 启动监控服务 | ||
export function startMonitoring() { | ||
// 加载已配置的地址 | ||
const storedAddresses = localStorage.getItem('monitoredAddresses') | ||
if (storedAddresses) { | ||
monitoredAddresses.value = JSON.parse(storedAddresses) | ||
monitoredAddresses.value.forEach(startWatchingAddress) | ||
} | ||
|
||
// 定期保存地址列表 | ||
setInterval(() => { | ||
localStorage.setItem( | ||
'monitoredAddresses', | ||
JSON.stringify(monitoredAddresses.value) | ||
) | ||
}, 10000) | ||
return { | ||
loading, | ||
error, | ||
getTransactions | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,96 +1,30 @@ | ||
import _ from 'lodash' | ||
|
||
/** | ||
* @description: 纯数量格式化 | ||
* @param {number} num 数字 | ||
* @param {number} decimal 小数 | ||
* @param {boolean} unit 是否带单位 | ||
* @return {number} | ||
*/ | ||
export function formatNum (num, decimal = 2, unit = false) { | ||
if (!num) return num | ||
const units = ['', 'K', 'M', 'B', 'T'] | ||
let unitIndex | ||
let result = num | ||
if (num < 1000 * 10) { | ||
// nothing | ||
unitIndex = 0 | ||
} else if (num < Math.pow(1000, 2) * 10) { | ||
// K | ||
unitIndex = 1 | ||
} else if (num < Math.pow(1000, 3) * 10) { | ||
// M | ||
unitIndex = 2 | ||
} else if (num < Math.pow(1000, 4) * 10) { | ||
// B | ||
unitIndex = 3 | ||
} else { | ||
// T | ||
unitIndex = 4 | ||
} | ||
if (unit) { | ||
result = _.divide(result, Math.pow(1000, unitIndex)) | ||
result = retain(result, decimal) | ||
result = toCurrency(result) | ||
result = result + units[unitIndex] | ||
} else { | ||
result = retain(result, decimal) | ||
result = toCurrency(result) | ||
} | ||
return result | ||
|
||
function toCurrency (num) { | ||
const parts = num.toString().split('.') | ||
parts[0] = parts[0].replace(/\B(?=(\d{3})+(?!\d))/g, ',') | ||
return parts.join('.') | ||
} | ||
function retain (num, decimal) { | ||
const magnification = Math.pow(10, decimal) | ||
return Math.floor(num * magnification) / magnification | ||
} | ||
} | ||
|
||
/** | ||
* @description: 按照精度截取数据(小数,科学记数法,非四舍五入) | ||
* @param {*} | ||
* @return {*} string | ||
*/ | ||
export function dealDecimals (value, decimals) { | ||
let amount = String(value) | ||
let isDealPosint = amount.split('.') | ||
if (!isDealPosint && value * 1 < 0 && isDealPosint.length < 2) { | ||
amount = _toNonExponential(value * 1) | ||
isDealPosint = amount.split('.') | ||
} | ||
if (isDealPosint && isDealPosint.length === 2) { | ||
const integer = isDealPosint[0] | ||
const point = isDealPosint[1] | ||
const maxLength = decimals * 1 + integer.length + 1 | ||
if (point && point.length) return amount.substring(0, maxLength) | ||
} | ||
return amount | ||
} | ||
|
||
/** | ||
* @description: 将科学记数法转化正常计数 | ||
* @param {*} num | ||
* @return {*} | ||
* 格式化时间戳为本地时间字符串 | ||
* @param {number|string} timestamp - 时间戳 | ||
* @returns {string} 格式化后的时间字符串 | ||
*/ | ||
function _toNonExponential (num) { | ||
const m = num.toExponential().match(/\d(?:\.(\d*))?e([+-]\d+)/) | ||
return num.toFixed(Math.max(0, (m[1] || '').length - m[2])) | ||
export const formatDate = (timestamp) => { | ||
const date = new Date(Number(timestamp) * 1000) | ||
return date.toLocaleString('zh-CN', { | ||
year: 'numeric', | ||
month: '2-digit', | ||
day: '2-digit', | ||
hour: '2-digit', | ||
minute: '2-digit', | ||
second: '2-digit', | ||
hour12: false | ||
}) | ||
} | ||
|
||
/** | ||
* @description: 长文本省略中间部分 | ||
* @param {string} str 文本内容 | ||
* @param {number} start 保留文本头部长度 | ||
* @param {number} end 保留文本尾部长度 | ||
* @return {string} | ||
* 缩短哈希字符串显示 | ||
* @param {string} hash - 原始哈希字符串 | ||
* @param {number} [startLen=6] - 开头保留长度 | ||
* @param {number} [endLen=4] - 结尾保留长度 | ||
* @returns {string} 缩短后的哈希字符串 | ||
*/ | ||
export function ellipsisStr (str, start = 6, end = 4) { | ||
if (typeof str === 'string') { | ||
return str.slice(0, start) + '...' + str.slice(-end) | ||
} | ||
return str | ||
export const shortenHash = (hash, startLen = 6, endLen = 4) => { | ||
if (!hash || typeof hash !== 'string') return '' | ||
if (hash.length <= startLen + endLen) return hash | ||
return `${hash.slice(0, startLen)}...${hash.slice(-endLen)}` | ||
} |
Oops, something went wrong.