|
@@ -0,0 +1,145 @@
|
|
|
+import cron from 'node-cron'
|
|
|
+// init dotenv
|
|
|
+import dotenv from 'dotenv'
|
|
|
+import { ethers, TransactionResponse } from 'ethers6'
|
|
|
+import BnMonitor from '../db/models/BnMonitor'
|
|
|
+import BnWhiteList from '../db/models/BnWhiteList'
|
|
|
+import sequelize from '../db'
|
|
|
+import BnTransaction from '../db/models/TransferInfo'
|
|
|
+
|
|
|
+dotenv.config()
|
|
|
+let networkInfo: ethers.Network
|
|
|
+const provider = new ethers.JsonRpcProvider('https://binance.llamarpc.com')
|
|
|
+let monitorRunning = false
|
|
|
+export async function bnMonitor(): Promise<void> {
|
|
|
+ await initMonitor()
|
|
|
+ const blockMonitorTask = cron.schedule(
|
|
|
+ '* * * * * *',
|
|
|
+ () => {
|
|
|
+ if (monitorRunning) return // 如果正在执行,则跳过当前任务
|
|
|
+ monitorRunning = true
|
|
|
+ startMonitor()
|
|
|
+ .catch((err) => {
|
|
|
+ console.error('Monitor error occurred:', err)
|
|
|
+ })
|
|
|
+ .finally(() => {
|
|
|
+ monitorRunning = false
|
|
|
+ })
|
|
|
+ },
|
|
|
+ { runOnInit: true }
|
|
|
+ )
|
|
|
+
|
|
|
+ // stop cron job when server is stopped
|
|
|
+ process.on('SIGINT', () => {
|
|
|
+ blockMonitorTask.stop()
|
|
|
+ process.exit(0)
|
|
|
+ })
|
|
|
+}
|
|
|
+
|
|
|
+// eslint-disable-next-line @typescript-eslint/explicit-function-return-type
|
|
|
+async function startMonitor() {
|
|
|
+ console.log(`current time: ${new Date().toLocaleString()}`)
|
|
|
+ const onchainBlockNumber = await provider.getBlockNumber()
|
|
|
+ const monitorStatus = await BnMonitor.findOne({
|
|
|
+ where: { chainId: networkInfo.chainId },
|
|
|
+ })
|
|
|
+ if (!monitorStatus) {
|
|
|
+ throw new Error('create monitor raw first')
|
|
|
+ }
|
|
|
+ // 上一次的块号
|
|
|
+ const syncBlockNumber = monitorStatus.syncBlockNumber
|
|
|
+ // 下一个块号
|
|
|
+ const nextBlockNumber = syncBlockNumber + 1
|
|
|
+
|
|
|
+ // 如果下一个块号小于当前链上块号,说明有新块产生
|
|
|
+ if (nextBlockNumber < onchainBlockNumber) {
|
|
|
+ const blockInfo = await provider.getBlock(nextBlockNumber, true)
|
|
|
+ if (!blockInfo) {
|
|
|
+ console.warn(
|
|
|
+ 'No block information found for block number:',
|
|
|
+ nextBlockNumber
|
|
|
+ )
|
|
|
+ return
|
|
|
+ }
|
|
|
+ const whiteList = await BnWhiteList.findAll()
|
|
|
+ const addressList: string[] = whiteList.map((item) => item.address)
|
|
|
+ const transactions: TransactionResponse[] = blockInfo.prefetchedTransactions
|
|
|
+
|
|
|
+ const transferTransaction = transactions.filter(
|
|
|
+ (tx) => addressList.includes(tx.to!) || addressList.includes(tx.from)
|
|
|
+ )
|
|
|
+ const transferInfo = transferTransaction
|
|
|
+ .map((tx) => {
|
|
|
+ if (tx.data === '0x') {
|
|
|
+ return {
|
|
|
+ from: tx.from,
|
|
|
+ to: tx.to!,
|
|
|
+ amount: tx.value.toString(),
|
|
|
+ hash: tx.hash,
|
|
|
+ maker: tx.to!,
|
|
|
+ block: tx.blockNumber!,
|
|
|
+ type: 0,
|
|
|
+ }
|
|
|
+ } else if (tx.to === '0x1A0A18AC4BECDDbd6389559687d1A73d8927E416') {
|
|
|
+ return {
|
|
|
+ from: tx.from,
|
|
|
+ to: tx.to,
|
|
|
+ amount: tx.value.toString(),
|
|
|
+ hash: tx.hash,
|
|
|
+ maker: tx.from,
|
|
|
+ block: tx.blockNumber!,
|
|
|
+ type: 1,
|
|
|
+ }
|
|
|
+ }
|
|
|
+ return null
|
|
|
+ })
|
|
|
+ .filter((tx) => tx !== null)
|
|
|
+ if (transferInfo.length > 0) {
|
|
|
+ await sequelize.transaction(async (tx) => {
|
|
|
+ // 更新监控状态
|
|
|
+ await BnMonitor.update(
|
|
|
+ { syncBlockNumber: nextBlockNumber },
|
|
|
+ { where: { chainId: networkInfo.chainId }, transaction: tx }
|
|
|
+ )
|
|
|
+
|
|
|
+ // 批量插入交易信息
|
|
|
+ await BnTransaction.bulkCreate(transferInfo as any[], {
|
|
|
+ ignoreDuplicates: true,
|
|
|
+ transaction: tx,
|
|
|
+ })
|
|
|
+ })
|
|
|
+ console.log(`Processed block number: ${nextBlockNumber}`)
|
|
|
+ } else {
|
|
|
+ await BnMonitor.update(
|
|
|
+ { syncBlockNumber: nextBlockNumber },
|
|
|
+ { where: { chainId: networkInfo.chainId } }
|
|
|
+ )
|
|
|
+ console.log(
|
|
|
+ `No relevant transactions found in block number: ${nextBlockNumber}`
|
|
|
+ )
|
|
|
+ }
|
|
|
+ } else {
|
|
|
+ console.log('No new block found')
|
|
|
+ }
|
|
|
+}
|
|
|
+// 45322143
|
|
|
+async function initMonitor(): Promise<void> {
|
|
|
+ const blockNumber = await provider.getBlockNumber()
|
|
|
+ networkInfo = await provider.getNetwork()
|
|
|
+
|
|
|
+ await BnMonitor.findOrCreate({
|
|
|
+ where: {
|
|
|
+ chainId: networkInfo.chainId,
|
|
|
+ },
|
|
|
+ defaults: {
|
|
|
+ chainId: networkInfo.chainId,
|
|
|
+ syncBlockNumber: blockNumber,
|
|
|
+ },
|
|
|
+ })
|
|
|
+
|
|
|
+ console.log('monitor status for chainId: %s created', networkInfo.chainId)
|
|
|
+}
|
|
|
+
|
|
|
+bnMonitor()
|
|
|
+ .then((r) => console.log(r))
|
|
|
+ .catch((e) => console.error(e))
|