upgrade eslint, reformat code
This commit is contained in:
parent
36621a9206
commit
4b45ccfce3
15
package.json
15
package.json
@ -54,21 +54,20 @@
|
|||||||
},
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"@types/dotenv": "^8.2.0",
|
"@types/dotenv": "^8.2.0",
|
||||||
"@types/node": "^14.14.20",
|
"@types/node": "^16.18.68",
|
||||||
"@types/node-fetch": "2.x",
|
"@types/node-fetch": "2.x",
|
||||||
"@types/node-schedule": "^2.1.0",
|
"@types/node-schedule": "^2.1.0",
|
||||||
"@types/nodemailer": "^6.4.7",
|
"@types/nodemailer": "^6.4.7",
|
||||||
"@types/redis": "^2.8.28",
|
"@types/redis": "^2.8.28",
|
||||||
"@typescript-eslint/eslint-plugin": "^4.25.0",
|
"@typescript-eslint/eslint-plugin": "^6.19.0",
|
||||||
"@typescript-eslint/parser": "^4.25.0",
|
"@typescript-eslint/parser": "^6.19.0",
|
||||||
"eslint": "^7.27.0",
|
"eslint-config-prettier": "^9.1.0",
|
||||||
"eslint-config-prettier": "^8.3.0",
|
"eslint-plugin-prettier": "^5.1.3",
|
||||||
"eslint-plugin-prettier": "^3.4.0",
|
"prettier": "^3.2.4",
|
||||||
"prettier": "^2.3.0",
|
|
||||||
"ts-node": "^10.9.1",
|
"ts-node": "^10.9.1",
|
||||||
"ts-node-dev": "^2.0.0",
|
"ts-node-dev": "^2.0.0",
|
||||||
"tsconfig-paths": "^3.9.0",
|
"tsconfig-paths": "^3.9.0",
|
||||||
"tslint": "^6.1.1",
|
"tslint": "^6.1.1",
|
||||||
"typescript": "^4.1.3"
|
"typescript": "^5.3.3"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,4 +1,4 @@
|
|||||||
import fetch, {Response} from "node-fetch"
|
import fetch, { Response } from 'node-fetch'
|
||||||
import { retry } from 'utils/promise.util'
|
import { retry } from 'utils/promise.util'
|
||||||
// AbortController was added in node v14.17.0 globally
|
// AbortController was added in node v14.17.0 globally
|
||||||
const AbortController = globalThis.AbortController
|
const AbortController = globalThis.AbortController
|
||||||
@ -6,36 +6,35 @@ const AbortController = globalThis.AbortController
|
|||||||
const request = async (url: string, options: any) => {
|
const request = async (url: string, options: any) => {
|
||||||
const controller = new AbortController()
|
const controller = new AbortController()
|
||||||
const timeout = setTimeout(() => controller.abort(), 30000)
|
const timeout = setTimeout(() => controller.abort(), 30000)
|
||||||
let res: Response;
|
let res: Response
|
||||||
try {
|
try {
|
||||||
res = await fetch(url, { ...options, signal: controller.signal })
|
res = await fetch(url, { ...options, signal: controller.signal })
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
clearTimeout(timeout)
|
clearTimeout(timeout)
|
||||||
throw err;
|
throw err
|
||||||
}
|
}
|
||||||
return res
|
return res
|
||||||
}
|
}
|
||||||
|
|
||||||
const requestChain = async (rpc: string, method: string, params: any) => {
|
const requestChain = async (rpc: string, method: string, params: any) => {
|
||||||
const data = {
|
const data = {
|
||||||
id: Date.now(),
|
id: Date.now(),
|
||||||
jsonrpc: "2.0",
|
jsonrpc: '2.0',
|
||||||
method,
|
method,
|
||||||
params
|
params,
|
||||||
}
|
}
|
||||||
return request(rpc, {
|
return request(rpc, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(data)
|
body: JSON.stringify(data),
|
||||||
})
|
}).then(res => res.json())
|
||||||
.then((res) => res.json())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ethBlockNumber = async (rpc: string) => {
|
export const ethBlockNumber = async (rpc: string) => {
|
||||||
return requestChain(rpc, "eth_blockNumber", [])
|
return requestChain(rpc, 'eth_blockNumber', [])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const retryEthBlockNumber = async (rpc: string) => {
|
export const retryEthBlockNumber = async (rpc: string) => {
|
||||||
@ -47,61 +46,58 @@ export const retryEthBlockNumber = async (rpc: string) => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
export const ethGetBlockByNumber = async (rpc: string, blockNumber: string) => {
|
export const ethGetBlockByNumber = async (rpc: string, blockNumber: string) => {
|
||||||
return requestChain(rpc, "eth_getBlockByNumber", [blockNumber, true])
|
return requestChain(rpc, 'eth_getBlockByNumber', [blockNumber, true])
|
||||||
}
|
}
|
||||||
|
|
||||||
export const ethGetLogs = async (rpc: string, params: any) => {
|
export const ethGetLogs = async (rpc: string, params: any) => {
|
||||||
return requestChain(rpc, "eth_getLogs", params)
|
return requestChain(rpc, 'eth_getLogs', params)
|
||||||
}
|
}
|
||||||
|
|
||||||
export const _batchEthBlocks = async (rpc: string, blockNumber: number, amount: number) => {
|
export const _batchEthBlocks = async (rpc: string, blockNumber: number, amount: number) => {
|
||||||
let batch = []
|
let batch = []
|
||||||
for (let i = 0; i < amount; i++) {
|
for (let i = 0; i < amount; i++) {
|
||||||
batch.push({
|
batch.push({
|
||||||
jsonrpc: "2.0",
|
jsonrpc: '2.0',
|
||||||
method: "eth_getBlockByNumber",
|
method: 'eth_getBlockByNumber',
|
||||||
params: ["0x" + (blockNumber + i).toString(16), true],
|
params: ['0x' + (blockNumber + i).toString(16), true],
|
||||||
id: blockNumber + i
|
id: blockNumber + i,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return request(rpc, {
|
return request(rpc, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(batch)
|
body: JSON.stringify(batch),
|
||||||
})
|
}).then(res => res.json())
|
||||||
.then((res) => res.json())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const batchEthBlocks = async (rpc: string, blockNumbers: number[]) => {
|
export const batchEthBlocks = async (rpc: string, blockNumbers: number[]) => {
|
||||||
let batch = []
|
let batch = []
|
||||||
for (let blockNum of blockNumbers) {
|
for (let blockNum of blockNumbers) {
|
||||||
batch.push({
|
batch.push({
|
||||||
jsonrpc: "2.0",
|
jsonrpc: '2.0',
|
||||||
method: "eth_getBlockByNumber",
|
method: 'eth_getBlockByNumber',
|
||||||
params: ["0x" + blockNum.toString(16), true],
|
params: ['0x' + blockNum.toString(16), true],
|
||||||
id: blockNum
|
id: blockNum,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
return request(rpc, {
|
return request(rpc, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(batch)
|
body: JSON.stringify(batch),
|
||||||
})
|
}).then(res => res.json())
|
||||||
.then((res) => res.json())
|
|
||||||
}
|
}
|
||||||
|
|
||||||
export const batchEthLogs = async (rpc: string, params: any) => {
|
export const batchEthLogs = async (rpc: string, params: any) => {
|
||||||
return request(rpc, {
|
return request(rpc, {
|
||||||
method: "POST",
|
method: 'POST',
|
||||||
headers: {
|
headers: {
|
||||||
"Content-Type": "application/json; charset=utf-8"
|
'Content-Type': 'application/json; charset=utf-8',
|
||||||
},
|
},
|
||||||
body: JSON.stringify(params)
|
body: JSON.stringify(params),
|
||||||
})
|
}).then(res => res.json())
|
||||||
.then((res) => res.json())
|
|
||||||
}
|
}
|
||||||
|
@ -5,7 +5,6 @@ import { CheckIn } from 'models/CheckIn'
|
|||||||
import { NftHolder } from 'models/NftHolder'
|
import { NftHolder } from 'models/NftHolder'
|
||||||
import { getMonthBegin, getNDayAgo } from 'utils/date.util'
|
import { getMonthBegin, getNDayAgo } from 'utils/date.util'
|
||||||
|
|
||||||
|
|
||||||
class TaskController extends BaseController {
|
class TaskController extends BaseController {
|
||||||
@role('anon')
|
@role('anon')
|
||||||
@router('post /task/check_in')
|
@router('post /task/check_in')
|
||||||
@ -18,23 +17,23 @@ class TaskController extends BaseController {
|
|||||||
if (!limit) {
|
if (!limit) {
|
||||||
if (typeof days === 'number') {
|
if (typeof days === 'number') {
|
||||||
let begin = getNDayAgo(days, true)
|
let begin = getNDayAgo(days, true)
|
||||||
query.blockTime = {$gt: begin.getTime() / 1000 | 0}
|
query.blockTime = { $gt: (begin.getTime() / 1000) | 0 }
|
||||||
} else if (typeof days === 'string') {
|
} else if (typeof days === 'string') {
|
||||||
if (days === '1month') {
|
if (days === '1month') {
|
||||||
let date = getMonthBegin(new Date())
|
let date = getMonthBegin(new Date())
|
||||||
query.blockTime = {$gt: date.getTime() / 1000 | 0}
|
query.blockTime = { $gt: (date.getTime() / 1000) | 0 }
|
||||||
} else {
|
} else {
|
||||||
query.dateTag = days
|
query.dateTag = days
|
||||||
}
|
}
|
||||||
} else if (Array.isArray(days)) {
|
} else if (Array.isArray(days)) {
|
||||||
query.dateTag = {$in: days}
|
query.dateTag = { $in: days }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let records
|
let records
|
||||||
if (limit) {
|
if (limit) {
|
||||||
records = await CheckIn.find(query).sort({_id: -1}).limit(limit)
|
records = await CheckIn.find(query).sort({ _id: -1 }).limit(limit)
|
||||||
} else {
|
} else {
|
||||||
records = await CheckIn.find(query).sort({_id: -1})
|
records = await CheckIn.find(query).sort({ _id: -1 })
|
||||||
}
|
}
|
||||||
let result = []
|
let result = []
|
||||||
for (let record of records) {
|
for (let record of records) {
|
||||||
@ -42,7 +41,7 @@ class TaskController extends BaseController {
|
|||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
|
|
||||||
@role('anon')
|
@role('anon')
|
||||||
@router('post /task/check_in/max_seq')
|
@router('post /task/check_in/max_seq')
|
||||||
async maxSeqCheckin(req, res) {
|
async maxSeqCheckin(req, res) {
|
||||||
@ -50,7 +49,7 @@ class TaskController extends BaseController {
|
|||||||
if (!address) {
|
if (!address) {
|
||||||
throw new ZError(10, 'params mismatch')
|
throw new ZError(10, 'params mismatch')
|
||||||
}
|
}
|
||||||
const record = await CheckIn.findOne({from: address.toLowerCase()}).sort({count: -1})
|
const record = await CheckIn.findOne({ from: address.toLowerCase() }).sort({ count: -1 })
|
||||||
return record.toJson()
|
return record.toJson()
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -64,7 +63,7 @@ class TaskController extends BaseController {
|
|||||||
address = address.toLowerCase()
|
address = address.toLowerCase()
|
||||||
user = user.toLowerCase()
|
user = user.toLowerCase()
|
||||||
|
|
||||||
let records = await NftHolder.find({address, chain, user, burn: true }).sort({blockNumber: -1})
|
let records = await NftHolder.find({ address, chain, user, burn: true }).sort({ blockNumber: -1 })
|
||||||
let result = []
|
let result = []
|
||||||
for (let record of records) {
|
for (let record of records) {
|
||||||
result.push({
|
result.push({
|
||||||
@ -74,7 +73,7 @@ class TaskController extends BaseController {
|
|||||||
tokenId: record.tokenId,
|
tokenId: record.tokenId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
return result;
|
return result
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
export default TaskController
|
export default TaskController
|
||||||
|
@ -14,13 +14,12 @@ import { IEventCfg } from 'interface/IEventCfg'
|
|||||||
let svrs: any[] = []
|
let svrs: any[] = []
|
||||||
let lock = false
|
let lock = false
|
||||||
|
|
||||||
|
|
||||||
async function initEventSvrs() {
|
async function initEventSvrs() {
|
||||||
const cfgMap: Map<IChain, IEventCfg[]> = new Map();
|
const cfgMap: Map<IChain, IEventCfg[]> = new Map()
|
||||||
for (let cfg of events) {
|
for (let cfg of events) {
|
||||||
cfg.address = cfg.address.toLowerCase()
|
cfg.address = cfg.address.toLowerCase()
|
||||||
cfg.abi = require(`../config/event_abis/${cfg.abi}.json`)
|
cfg.abi = require(`../config/event_abis/${cfg.abi}.json`)
|
||||||
const chainCfg = AllChains.find((chain) => chain.id === cfg.chain)
|
const chainCfg = AllChains.find(chain => chain.id === cfg.chain)
|
||||||
if (!chainCfg) {
|
if (!chainCfg) {
|
||||||
logger.error('chainCfg not found: ' + cfg.chain)
|
logger.error('chainCfg not found: ' + cfg.chain)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@ -42,12 +41,12 @@ async function parseAllEvents() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
lock = true
|
lock = true
|
||||||
logger.info('begin sync events: ' + svrs.map((svr) => svr.chainCfg.id).join(',') )
|
logger.info('begin sync events: ' + svrs.map(svr => svr.chainCfg.id).join(','))
|
||||||
for (let svr of svrs) {
|
for (let svr of svrs) {
|
||||||
try {
|
try {
|
||||||
await svr.execute()
|
await svr.execute()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.info('sync events error:: chain: ' + svr.chainCfg.id )
|
logger.info('sync events error:: chain: ' + svr.chainCfg.id)
|
||||||
logger.info(err)
|
logger.info(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -63,5 +62,4 @@ async function parseAllEvents() {
|
|||||||
parseAllEvents()
|
parseAllEvents()
|
||||||
}, 10000)
|
}, 10000)
|
||||||
parseAllEvents()
|
parseAllEvents()
|
||||||
})();
|
})()
|
||||||
|
|
||||||
|
@ -1,9 +1,9 @@
|
|||||||
export interface IEventCfg {
|
export interface IEventCfg {
|
||||||
address: string,
|
address: string
|
||||||
event: string,
|
event: string
|
||||||
abi: any,
|
abi: any
|
||||||
fromBlock: number,
|
fromBlock: number
|
||||||
eventProcesser?: string,
|
eventProcesser?: string
|
||||||
chain: number,
|
chain: number
|
||||||
topic?: string,
|
topic?: string
|
||||||
}
|
}
|
||||||
|
@ -10,7 +10,7 @@ export enum FilterEnum {
|
|||||||
like = 'like',
|
like = 'like',
|
||||||
nlike = 'nlike',
|
nlike = 'nlike',
|
||||||
isNull = 'isNull',
|
isNull = 'isNull',
|
||||||
isNotNull = 'isNotNull'
|
isNotNull = 'isNotNull',
|
||||||
}
|
}
|
||||||
|
|
||||||
export enum FilterValueTypeEnum {
|
export enum FilterValueTypeEnum {
|
||||||
@ -19,20 +19,20 @@ export enum FilterValueTypeEnum {
|
|||||||
boolean = 'boolean',
|
boolean = 'boolean',
|
||||||
utf8_data = 'utf8_data',
|
utf8_data = 'utf8_data',
|
||||||
hex_data = 'hex_data',
|
hex_data = 'hex_data',
|
||||||
address = 'address'
|
address = 'address',
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface IFilter {
|
export interface IFilter {
|
||||||
key: string,
|
key: string
|
||||||
type: string,
|
type: string
|
||||||
value: string,
|
value: string
|
||||||
op: FilterEnum
|
op: FilterEnum
|
||||||
}
|
}
|
||||||
export interface IScriptionCfg {
|
export interface IScriptionCfg {
|
||||||
chain: number,
|
chain: number
|
||||||
rpc?: string,
|
rpc?: string
|
||||||
fromBlock: number,
|
fromBlock: number
|
||||||
filters?: IFilter[],
|
filters?: IFilter[]
|
||||||
filter: (event: any) => boolean,
|
filter: (event: any) => boolean
|
||||||
process: (event: any) => Promise<void>,
|
process: (event: any) => Promise<void>
|
||||||
}
|
}
|
||||||
|
@ -14,11 +14,11 @@ export class BlockDataClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public chainId: number
|
public chainId: number
|
||||||
|
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed})
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
public data: any
|
public data: any
|
||||||
|
|
||||||
public static async saveBlock(event: any) {
|
public static async saveBlock(event: any) {
|
||||||
return BlockData.insertOrUpdate({ hash: event.hash }, {data: event})
|
return BlockData.insertOrUpdate({ hash: event.hash }, { data: event })
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -7,8 +7,8 @@ import { logger } from '@typegoose/typegoose/lib/logSettings'
|
|||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ from: 1 }, { unique: false })
|
@index({ from: 1 }, { unique: false })
|
||||||
@index({ hash: 1 }, { unique: true })
|
@index({ hash: 1 }, { unique: true })
|
||||||
@index({ from: 1, dateTag: 1}, { unique: true })
|
@index({ from: 1, dateTag: 1 }, { unique: true })
|
||||||
@index({ from: 1, blockTime: 1}, { unique: false })
|
@index({ from: 1, blockTime: 1 }, { unique: false })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'check_in_event', timestamps: true },
|
schemaOptions: { collection: 'check_in_event', timestamps: true },
|
||||||
})
|
})
|
||||||
@ -28,7 +28,7 @@ export class CheckInClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public dateTag: string
|
public dateTag: string
|
||||||
// 连签天数
|
// 连签天数
|
||||||
@prop({default: 0})
|
@prop({ default: 0 })
|
||||||
public count: number
|
public count: number
|
||||||
@prop()
|
@prop()
|
||||||
public value: string
|
public value: string
|
||||||
@ -36,7 +36,7 @@ export class CheckInClass extends BaseModule {
|
|||||||
public input: string
|
public input: string
|
||||||
|
|
||||||
public static async saveEvent(event: any) {
|
public static async saveEvent(event: any) {
|
||||||
const preDay = formatDate(yesterday());
|
const preDay = formatDate(yesterday())
|
||||||
const preDayEvent = await CheckIn.findOne({ from: event.from, dateTag: preDay })
|
const preDayEvent = await CheckIn.findOne({ from: event.from, dateTag: preDay })
|
||||||
if (preDayEvent) {
|
if (preDayEvent) {
|
||||||
event.count = preDayEvent.count + 1
|
event.count = preDayEvent.count + 1
|
||||||
@ -49,12 +49,12 @@ export class CheckInClass extends BaseModule {
|
|||||||
}
|
}
|
||||||
|
|
||||||
public toJson() {
|
public toJson() {
|
||||||
return {
|
return {
|
||||||
address: this.from,
|
address: this.from,
|
||||||
day: this.dateTag,
|
day: this.dateTag,
|
||||||
time: this.blockTime,
|
time: this.blockTime,
|
||||||
count: this.count
|
count: this.count,
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -5,7 +5,7 @@ import { TokenHolder } from './TokenHolder'
|
|||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ address: 1 }, { unique: false })
|
@index({ address: 1 }, { unique: false })
|
||||||
@index({ chain: 1, hash: 1, logIndex: 1}, { unique: true })
|
@index({ chain: 1, hash: 1, logIndex: 1 }, { unique: true })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'ft_transfer_event', timestamps: true },
|
schemaOptions: { collection: 'ft_transfer_event', timestamps: true },
|
||||||
})
|
})
|
||||||
@ -45,7 +45,7 @@ export class FtTransferEventClass extends BaseModule {
|
|||||||
if (amount == undefined) {
|
if (amount == undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
const logIndex = parseInt(event.logIndex || '0')
|
const logIndex = parseInt(event.logIndex || '0')
|
||||||
const from = event.decodedData.from.toLowerCase()
|
const from = event.decodedData.from.toLowerCase()
|
||||||
const to = event.decodedData.to.toLowerCase()
|
const to = event.decodedData.to.toLowerCase()
|
||||||
@ -59,10 +59,10 @@ export class FtTransferEventClass extends BaseModule {
|
|||||||
amount,
|
amount,
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 },
|
||||||
}
|
}
|
||||||
|
|
||||||
let record = await FtTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
let record = await FtTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
await TokenHolder.saveData(record)
|
await TokenHolder.saveData(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export const FtTransferEvent = getModelForClass(FtTransferEventClass, {
|
export const FtTransferEvent = getModelForClass(FtTransferEventClass, {
|
||||||
|
@ -4,7 +4,7 @@ import { BaseModule } from './Base'
|
|||||||
import { NftHolder } from './NftHolder'
|
import { NftHolder } from './NftHolder'
|
||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ chain: 1, hash: 1, logIndex: 1}, { unique: true })
|
@index({ chain: 1, hash: 1, logIndex: 1 }, { unique: true })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'general_event', timestamps: true },
|
schemaOptions: { collection: 'general_event', timestamps: true },
|
||||||
options: { allowMixed: Severity.ALLOW },
|
options: { allowMixed: Severity.ALLOW },
|
||||||
@ -28,15 +28,14 @@ export class GeneralEventClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public removed: boolean
|
public removed: boolean
|
||||||
|
|
||||||
@prop({ type: mongoose.Schema.Types.Mixed})
|
@prop({ type: mongoose.Schema.Types.Mixed })
|
||||||
public decodedData: any
|
public decodedData: any
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public blockTime: number
|
public blockTime: number
|
||||||
@prop({ default: 0 })
|
@prop({ default: 0 })
|
||||||
public version: number
|
public version: number
|
||||||
|
|
||||||
|
|
||||||
public static async saveEvent(event: any) {
|
public static async saveEvent(event: any) {
|
||||||
const logIndex = parseInt(event.logIndex || '0')
|
const logIndex = parseInt(event.logIndex || '0')
|
||||||
const hash = event.hash || event.transactionHash
|
const hash = event.hash || event.transactionHash
|
||||||
@ -49,8 +48,8 @@ export class GeneralEventClass extends BaseModule {
|
|||||||
event: event.event,
|
event: event.event,
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 },
|
||||||
}
|
}
|
||||||
|
|
||||||
let record = await GeneralEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
let record = await GeneralEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
return record
|
return record
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -21,14 +21,14 @@ export class NftHolderClass extends BaseModule {
|
|||||||
public blockNumber: number
|
public blockNumber: number
|
||||||
@prop()
|
@prop()
|
||||||
public user: string
|
public user: string
|
||||||
@prop({default: false})
|
@prop({ default: false })
|
||||||
public burn: boolean
|
public burn: boolean
|
||||||
|
|
||||||
public static async parseEvent(event: typeof GeneralEvent) {
|
public static async parseEvent(event: typeof GeneralEvent) {
|
||||||
const address = event.address.toLowerCase();
|
const address = event.address.toLowerCase()
|
||||||
const chain = event.chain;
|
const chain = event.chain
|
||||||
const tokenId = event.decodedData.tokenId;
|
const tokenId = event.decodedData.tokenId
|
||||||
const blockNumer = event.blockNumber;
|
const blockNumer = event.blockNumber
|
||||||
const burn = event.decodedData.to === ZERO_ADDRESS
|
const burn = event.decodedData.to === ZERO_ADDRESS
|
||||||
let user = (burn ? event.decodedData.from : event.decodedData.to).toLowerCase()
|
let user = (burn ? event.decodedData.from : event.decodedData.to).toLowerCase()
|
||||||
user = user.toLowerCase()
|
user = user.toLowerCase()
|
||||||
@ -42,7 +42,7 @@ export class NftHolderClass extends BaseModule {
|
|||||||
record.blockNumber = blockNumer
|
record.blockNumber = blockNumer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await record.save();
|
await record.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -4,13 +4,12 @@ import { BaseModule } from './Base'
|
|||||||
import logger from 'logger/logger'
|
import logger from 'logger/logger'
|
||||||
import { GeneralEvent } from './GeneralEvent'
|
import { GeneralEvent } from './GeneralEvent'
|
||||||
|
|
||||||
|
|
||||||
const STAKE_EVENT = 'Staked'
|
const STAKE_EVENT = 'Staked'
|
||||||
const REDEEM_EVENT = 'Redeem'
|
const REDEEM_EVENT = 'Redeem'
|
||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ chain: 1, nft: 1, tokenId: 1, start: 1 }, { unique: true })
|
@index({ chain: 1, nft: 1, tokenId: 1, start: 1 }, { unique: true })
|
||||||
@index({ chain:1, user: 1, nft: 1}, {unique: false})
|
@index({ chain: 1, user: 1, nft: 1 }, { unique: false })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'nft_stake_info', timestamps: true },
|
schemaOptions: { collection: 'nft_stake_info', timestamps: true },
|
||||||
})
|
})
|
||||||
@ -42,7 +41,6 @@ export class NftStakeClass extends BaseModule {
|
|||||||
@prop({ default: 0 })
|
@prop({ default: 0 })
|
||||||
public version: number
|
public version: number
|
||||||
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
*
|
*
|
||||||
{
|
{
|
||||||
@ -75,30 +73,31 @@ export class NftStakeClass extends BaseModule {
|
|||||||
stakeTime = parseInt(stakeTime)
|
stakeTime = parseInt(stakeTime)
|
||||||
if (isStake) {
|
if (isStake) {
|
||||||
logger.info(`stake nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
logger.info(`stake nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
||||||
let record = new NftStake({
|
let record = new NftStake({
|
||||||
address,
|
address,
|
||||||
chain,
|
chain,
|
||||||
nft,
|
nft,
|
||||||
tokenId,
|
tokenId,
|
||||||
user,
|
user,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
status: 1,
|
status: 1,
|
||||||
start,
|
start,
|
||||||
stakeTime
|
stakeTime,
|
||||||
})
|
})
|
||||||
await record.save()
|
await record.save()
|
||||||
} else {
|
} else {
|
||||||
logger.info(`redeem nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
logger.info(`redeem nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
||||||
await NftStake.insertOrUpdate(
|
await NftStake.insertOrUpdate(
|
||||||
{ chain, nft, tokenId, start},
|
{ chain, nft, tokenId, start },
|
||||||
{
|
{
|
||||||
status: 2,
|
status: 2,
|
||||||
address,
|
address,
|
||||||
user,
|
user,
|
||||||
redeemTime: Date.now() / 1000 | 0,
|
redeemTime: (Date.now() / 1000) | 0,
|
||||||
blockNumber,
|
blockNumber,
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 },
|
||||||
})
|
},
|
||||||
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -6,7 +6,7 @@ import { NftHolder } from './NftHolder'
|
|||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ chain: 1, address: 1, tokenId: 1 }, { unique: false })
|
@index({ chain: 1, address: 1, tokenId: 1 }, { unique: false })
|
||||||
@index({ chain: 1, address: 1, from: 1, to: 1 }, { unique: false })
|
@index({ chain: 1, address: 1, from: 1, to: 1 }, { unique: false })
|
||||||
@index({ chain: 1, hash: 1, logIndex: 1}, { unique: true })
|
@index({ chain: 1, hash: 1, logIndex: 1 }, { unique: true })
|
||||||
@modelOptions({
|
@modelOptions({
|
||||||
schemaOptions: { collection: 'nft_transfer_event', timestamps: true },
|
schemaOptions: { collection: 'nft_transfer_event', timestamps: true },
|
||||||
})
|
})
|
||||||
@ -57,8 +57,8 @@ export class NftTransferEventClass extends BaseModule {
|
|||||||
// blockTime: new Date(event.time).getTime(),
|
// blockTime: new Date(event.time).getTime(),
|
||||||
$inc: { version: 1 },
|
$inc: { version: 1 },
|
||||||
}
|
}
|
||||||
|
|
||||||
await NftTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
await NftTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -10,7 +10,6 @@ import { ZERO_ADDRESS } from 'common/Constants'
|
|||||||
schemaOptions: { collection: 'token_holder', timestamps: true },
|
schemaOptions: { collection: 'token_holder', timestamps: true },
|
||||||
})
|
})
|
||||||
export class TokenHolderClass extends BaseModule {
|
export class TokenHolderClass extends BaseModule {
|
||||||
|
|
||||||
@prop({ required: true })
|
@prop({ required: true })
|
||||||
public chain: string
|
public chain: string
|
||||||
|
|
||||||
@ -26,11 +25,10 @@ export class TokenHolderClass extends BaseModule {
|
|||||||
@prop()
|
@prop()
|
||||||
public blockNumber: number
|
public blockNumber: number
|
||||||
|
|
||||||
|
|
||||||
public static async parseEvent(event: typeof GeneralEvent) {
|
public static async parseEvent(event: typeof GeneralEvent) {
|
||||||
const address = event.address.toLowerCase();
|
const address = event.address.toLowerCase()
|
||||||
const chain = event.chain;
|
const chain = event.chain
|
||||||
const blockNumber = event.blockNumber;
|
const blockNumber = event.blockNumber
|
||||||
let { from, to, value } = event.decodedData
|
let { from, to, value } = event.decodedData
|
||||||
from = from.toLowerCase()
|
from = from.toLowerCase()
|
||||||
to = to.toLowerCase()
|
to = to.toLowerCase()
|
||||||
@ -40,7 +38,7 @@ export class TokenHolderClass extends BaseModule {
|
|||||||
record.amount = BigInt(record.amount) - BigInt(value)
|
record.amount = BigInt(record.amount) - BigInt(value)
|
||||||
record.blockNumber = blockNumber
|
record.blockNumber = blockNumber
|
||||||
}
|
}
|
||||||
await record.save();
|
await record.save()
|
||||||
}
|
}
|
||||||
if (to !== ZERO_ADDRESS) {
|
if (to !== ZERO_ADDRESS) {
|
||||||
let record = await TokenHolder.findOne({ address, chain, user: to })
|
let record = await TokenHolder.findOne({ address, chain, user: to })
|
||||||
@ -50,7 +48,7 @@ export class TokenHolderClass extends BaseModule {
|
|||||||
record.amount = BigInt(record.amount) + BigInt(value)
|
record.amount = BigInt(record.amount) + BigInt(value)
|
||||||
record.blockNumber = blockNumber
|
record.blockNumber = blockNumber
|
||||||
}
|
}
|
||||||
await record.save();
|
await record.save()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -17,7 +17,7 @@ let eventProcessers = {
|
|||||||
FtTransferEvent: FtTransferEvent,
|
FtTransferEvent: FtTransferEvent,
|
||||||
}
|
}
|
||||||
|
|
||||||
const events = require('config/events.json')
|
const events = require('../config/events.json')
|
||||||
|
|
||||||
async function initEventSvrs() {
|
async function initEventSvrs() {
|
||||||
for (let event of events) {
|
for (let event of events) {
|
||||||
|
@ -20,7 +20,7 @@ let eventProcessers = {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async function initEventSvrs() {
|
async function initEventSvrs() {
|
||||||
const cfgMap: Map<IChain, IScriptionCfg[]> = new Map();
|
const cfgMap: Map<IChain, IScriptionCfg[]> = new Map()
|
||||||
for (let cfg of scriptions) {
|
for (let cfg of scriptions) {
|
||||||
if (!cfg.filter && cfg.filters) {
|
if (!cfg.filter && cfg.filters) {
|
||||||
cfg.filter = buildScriptionFilters(cfg)
|
cfg.filter = buildScriptionFilters(cfg)
|
||||||
@ -33,7 +33,7 @@ async function initEventSvrs() {
|
|||||||
}
|
}
|
||||||
await processer.saveEvent(event)
|
await processer.saveEvent(event)
|
||||||
}
|
}
|
||||||
const chainCfg = AllChains.find((chain) => chain.id === cfg.chain)
|
const chainCfg = AllChains.find(chain => chain.id === cfg.chain)
|
||||||
if (!chainCfg) {
|
if (!chainCfg) {
|
||||||
logger.error('chainCfg not found: ' + cfg.chain)
|
logger.error('chainCfg not found: ' + cfg.chain)
|
||||||
process.exit(1)
|
process.exit(1)
|
||||||
@ -55,12 +55,12 @@ async function parseAllEvents() {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
lock = true
|
lock = true
|
||||||
logger.info('begin sync block: ' + svrs.map((svr) => svr.chainCfg.id).join(','))
|
logger.info('begin sync block: ' + svrs.map(svr => svr.chainCfg.id).join(','))
|
||||||
for (let svr of svrs) {
|
for (let svr of svrs) {
|
||||||
try {
|
try {
|
||||||
await svr.execute()
|
await svr.execute()
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.info('sync block with error:: chain: ' + svr.chainCfg.id )
|
logger.info('sync block with error:: chain: ' + svr.chainCfg.id)
|
||||||
logger.info(err)
|
logger.info(err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -76,5 +76,4 @@ async function parseAllEvents() {
|
|||||||
parseAllEvents()
|
parseAllEvents()
|
||||||
}, 20000)
|
}, 20000)
|
||||||
parseAllEvents()
|
parseAllEvents()
|
||||||
})();
|
})()
|
||||||
|
|
||||||
|
@ -1,11 +1,10 @@
|
|||||||
import { IChain } from "chain/allchain";
|
import { IChain } from 'chain/allchain'
|
||||||
import { retryEthBlockNumber } from "chain/chain.api";
|
import { retryEthBlockNumber } from 'chain/chain.api'
|
||||||
import { IScriptionCfg } from "interface/IScriptionCfg";
|
import { IScriptionCfg } from 'interface/IScriptionCfg'
|
||||||
import logger from "logger/logger";
|
import logger from 'logger/logger'
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from 'redis/RedisClient'
|
||||||
import { getPastBlocksIter } from "utils/block.util";
|
import { getPastBlocksIter } from 'utils/block.util'
|
||||||
import { formatDate } from "utils/date.util";
|
import { formatDate } from 'utils/date.util'
|
||||||
|
|
||||||
|
|
||||||
export class BlockSyncSvr {
|
export class BlockSyncSvr {
|
||||||
chainCfg: IChain
|
chainCfg: IChain
|
||||||
@ -13,9 +12,9 @@ export class BlockSyncSvr {
|
|||||||
fromBlock: number = Number.MAX_SAFE_INTEGER
|
fromBlock: number = Number.MAX_SAFE_INTEGER
|
||||||
batchCount: number = 0
|
batchCount: number = 0
|
||||||
redisKey = ''
|
redisKey = ''
|
||||||
rpc = '';
|
rpc = ''
|
||||||
constructor(_chainCfg: IChain, _scriptionCfgs: IScriptionCfg[]) {
|
constructor(_chainCfg: IChain, _scriptionCfgs: IScriptionCfg[]) {
|
||||||
this.chainCfg =_chainCfg
|
this.chainCfg = _chainCfg
|
||||||
this.scriptionCfgs = _scriptionCfgs
|
this.scriptionCfgs = _scriptionCfgs
|
||||||
this.rpc = _chainCfg.rpc.split('|')[0]
|
this.rpc = _chainCfg.rpc.split('|')[0]
|
||||||
for (let cfg of _scriptionCfgs) {
|
for (let cfg of _scriptionCfgs) {
|
||||||
@ -28,22 +27,22 @@ export class BlockSyncSvr {
|
|||||||
}
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
let currentBlock = await retryEthBlockNumber(this.rpc)
|
let currentBlock = await retryEthBlockNumber(this.rpc)
|
||||||
let blockStr = await new RedisClient().get(this.redisKey)
|
let blockStr = await new RedisClient().get(this.redisKey)
|
||||||
if (blockStr) {
|
if (blockStr) {
|
||||||
this.fromBlock = Math.max(parseInt(blockStr), this.fromBlock)
|
this.fromBlock = Math.max(parseInt(blockStr), this.fromBlock)
|
||||||
}
|
}
|
||||||
this.batchCount = parseInt(currentBlock.result, 16) - this.fromBlock
|
this.batchCount = parseInt(currentBlock.result, 16) - this.fromBlock
|
||||||
if (this.batchCount <= 0) {
|
if (this.batchCount <= 0) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
let blocks = getPastBlocksIter({
|
let blocks = getPastBlocksIter({
|
||||||
chainId: this.chainCfg.id,
|
chainId: this.chainCfg.id,
|
||||||
rpc: this.rpc,
|
rpc: this.rpc,
|
||||||
fromBlock: this.fromBlock,
|
fromBlock: this.fromBlock,
|
||||||
amount: this.batchCount
|
amount: this.batchCount,
|
||||||
})
|
})
|
||||||
await this.processBlockDatas(blocks)
|
await this.processBlockDatas(blocks)
|
||||||
}
|
}
|
||||||
|
|
||||||
async processBlockDatas(iterator: any) {
|
async processBlockDatas(iterator: any) {
|
||||||
@ -57,21 +56,21 @@ export class BlockSyncSvr {
|
|||||||
continue
|
continue
|
||||||
}
|
}
|
||||||
for (let i = 0; i < block.transactions.length; i++) {
|
for (let i = 0; i < block.transactions.length; i++) {
|
||||||
const tx = block.transactions[i];
|
const tx = block.transactions[i]
|
||||||
if (block.timestamp) {
|
if (block.timestamp) {
|
||||||
tx.blockTime = parseInt(block.timestamp, 16);
|
tx.blockTime = parseInt(block.timestamp, 16)
|
||||||
tx.dateTag = formatDate(new Date(tx.blockTime * 1000));
|
tx.dateTag = formatDate(new Date(tx.blockTime * 1000))
|
||||||
}
|
}
|
||||||
for (let j = 0; j < this.scriptionCfgs.length; j++) {
|
for (let j = 0; j < this.scriptionCfgs.length; j++) {
|
||||||
const cfg = this.scriptionCfgs[j];
|
const cfg = this.scriptionCfgs[j]
|
||||||
|
|
||||||
if (cfg.filter && cfg.filter(tx) ) {
|
if (cfg.filter && cfg.filter(tx)) {
|
||||||
try {
|
try {
|
||||||
await cfg.process(tx)
|
await cfg.process(tx)
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('process tx error: ', err.message || err)
|
logger.error('process tx error: ', err.message || err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -14,4 +14,4 @@ export async function restartAllUnFinishedTask() {
|
|||||||
chainQueue.addTaskToQueue(subTask)
|
chainQueue.addTaskToQueue(subTask)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,20 +1,20 @@
|
|||||||
import { IChain } from "chain/allchain";
|
import { IChain } from 'chain/allchain'
|
||||||
import { batchEthLogs, ethGetLogs, retryEthBlockNumber } from "chain/chain.api";
|
import { batchEthLogs, ethGetLogs, retryEthBlockNumber } from 'chain/chain.api'
|
||||||
import logger from "logger/logger";
|
import logger from 'logger/logger'
|
||||||
import { GeneralEvent } from "models/GeneralEvent";
|
import { GeneralEvent } from 'models/GeneralEvent'
|
||||||
|
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from 'redis/RedisClient'
|
||||||
import { decodeEvent, getTopics } from "utils/event.util";
|
import { decodeEvent, getTopics } from 'utils/event.util'
|
||||||
|
|
||||||
import { NftHolder } from 'models/NftHolder'
|
import { NftHolder } from 'models/NftHolder'
|
||||||
import { TokenHolder } from 'models/TokenHolder'
|
import { TokenHolder } from 'models/TokenHolder'
|
||||||
import { NftStake } from 'models/NftStake'
|
import { NftStake } from 'models/NftStake'
|
||||||
import { IEventCfg } from "interface/IEventCfg";
|
import { IEventCfg } from 'interface/IEventCfg'
|
||||||
|
|
||||||
let eventProcessers = {
|
let eventProcessers = {
|
||||||
NftHolder: NftHolder,
|
NftHolder: NftHolder,
|
||||||
TokenHolder: TokenHolder,
|
TokenHolder: TokenHolder,
|
||||||
NftStake: NftStake
|
NftStake: NftStake,
|
||||||
}
|
}
|
||||||
|
|
||||||
export class EventBatchSvr {
|
export class EventBatchSvr {
|
||||||
@ -23,9 +23,9 @@ export class EventBatchSvr {
|
|||||||
processer: Map<string, IEventCfg> = new Map()
|
processer: Map<string, IEventCfg> = new Map()
|
||||||
fromBlock: number = Number.MAX_SAFE_INTEGER
|
fromBlock: number = Number.MAX_SAFE_INTEGER
|
||||||
redisKey = ''
|
redisKey = ''
|
||||||
rpc = '';
|
rpc = ''
|
||||||
constructor(_chainCfg: IChain, _eventCfgs: IEventCfg[]) {
|
constructor(_chainCfg: IChain, _eventCfgs: IEventCfg[]) {
|
||||||
this.chainCfg =_chainCfg
|
this.chainCfg = _chainCfg
|
||||||
this.eventCfgs = _eventCfgs
|
this.eventCfgs = _eventCfgs
|
||||||
this.rpc = _chainCfg.rpc.split('|')[0]
|
this.rpc = _chainCfg.rpc.split('|')[0]
|
||||||
for (let cfg of this.eventCfgs) {
|
for (let cfg of this.eventCfgs) {
|
||||||
@ -33,82 +33,81 @@ export class EventBatchSvr {
|
|||||||
if (!cfg.topic) {
|
if (!cfg.topic) {
|
||||||
cfg.topic = getTopics(cfg)
|
cfg.topic = getTopics(cfg)
|
||||||
}
|
}
|
||||||
this.processer.set((cfg.address+cfg.topic).toLowerCase(), cfg)
|
this.processer.set((cfg.address + cfg.topic).toLowerCase(), cfg)
|
||||||
}
|
}
|
||||||
this.eventCfgs.sort((a, b) => a.topic.localeCompare(b.topic))
|
this.eventCfgs.sort((a, b) => a.topic.localeCompare(b.topic))
|
||||||
this.redisKey = `event_${this.chainCfg.id}`
|
this.redisKey = `event_${this.chainCfg.id}`
|
||||||
}
|
}
|
||||||
|
|
||||||
async execute() {
|
async execute() {
|
||||||
|
let currentBlock = await retryEthBlockNumber(this.rpc)
|
||||||
let currentBlock = await retryEthBlockNumber(this.rpc)
|
let toBlock = parseInt(currentBlock.result, 16)
|
||||||
let toBlock = parseInt(currentBlock.result, 16)
|
let blockStr = await new RedisClient().get(this.redisKey)
|
||||||
let blockStr = await new RedisClient().get(this.redisKey)
|
if (blockStr) {
|
||||||
if (blockStr) {
|
this.fromBlock = Math.max(parseInt(blockStr), this.fromBlock)
|
||||||
this.fromBlock = Math.max(parseInt(blockStr), this.fromBlock)
|
}
|
||||||
}
|
logger.info(`sync events with chain: ${this.chainCfg.id}, from: ${this.fromBlock}, to: ${toBlock}`)
|
||||||
logger.info(`sync events with chain: ${this.chainCfg.id}, from: ${this.fromBlock}, to: ${toBlock}`)
|
let params = []
|
||||||
let params = []
|
let uninParams = []
|
||||||
let uninParams = []
|
let topicsSet = new Set()
|
||||||
let topicsSet = new Set()
|
for (let cfg of this.eventCfgs) {
|
||||||
for (let cfg of this.eventCfgs) {
|
await this.fixBlockNumber(cfg)
|
||||||
await this.fixBlockNumber(cfg);
|
if (cfg.fromBlock != this.fromBlock) {
|
||||||
if (cfg.fromBlock != this.fromBlock) {
|
let _param = this.buildQueryParams(cfg, toBlock)
|
||||||
let _param = this.buildQueryParams(cfg, toBlock)
|
params.push(_param)
|
||||||
params.push(_param)
|
} else {
|
||||||
|
let _param = uninParams[uninParams.length - 1]
|
||||||
|
if (!_param || !topicsSet.has(cfg.topic)) {
|
||||||
|
_param = this.buildQueryParams(cfg, toBlock)
|
||||||
|
uninParams.push(_param)
|
||||||
|
topicsSet = new Set()
|
||||||
|
topicsSet.add(cfg.topic)
|
||||||
} else {
|
} else {
|
||||||
let _param = uninParams[uninParams.length - 1]
|
if (!topicsSet.has(cfg.topic)) {
|
||||||
if (!_param || !topicsSet.has(cfg.topic)) {
|
_param.params[0].topics.push(cfg.topic)
|
||||||
_param = this.buildQueryParams(cfg, toBlock)
|
|
||||||
uninParams.push(_param)
|
|
||||||
topicsSet = new Set()
|
|
||||||
topicsSet.add(cfg.topic)
|
topicsSet.add(cfg.topic)
|
||||||
} else {
|
|
||||||
if (!topicsSet.has(cfg.topic)) {
|
|
||||||
_param.params[0].topics.push(cfg.topic)
|
|
||||||
topicsSet.add(cfg.topic)
|
|
||||||
}
|
|
||||||
_param.params[0].address.push(cfg.address)
|
|
||||||
}
|
}
|
||||||
|
_param.params[0].address.push(cfg.address)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
let nextBlock = this.fromBlock
|
}
|
||||||
if (uninParams.length > 1) {
|
let nextBlock = this.fromBlock
|
||||||
logger.debug(`unin params: ${uninParams.length}`)
|
if (uninParams.length > 1) {
|
||||||
let results = await batchEthLogs(this.rpc, uninParams)
|
logger.debug(`unin params: ${uninParams.length}`)
|
||||||
for (let i = 0; i < results.length; i++) {
|
let results = await batchEthLogs(this.rpc, uninParams)
|
||||||
if (results[i].error) {
|
for (let i = 0; i < results.length; i++) {
|
||||||
throw results[i].error
|
if (results[i].error) {
|
||||||
}
|
throw results[i].error
|
||||||
let events = results[i].result
|
|
||||||
await this.processEvents(events)
|
|
||||||
}
|
}
|
||||||
} else if (uninParams.length == 1) {
|
let events = results[i].result
|
||||||
logger.debug(`unin params length 1, use eth_getLogs`)
|
await this.processEvents(events)
|
||||||
let result = await ethGetLogs(this.rpc, uninParams[0].params)
|
}
|
||||||
if (result.error) {
|
} else if (uninParams.length == 1) {
|
||||||
throw result.error
|
logger.debug(`unin params length 1, use eth_getLogs`)
|
||||||
|
let result = await ethGetLogs(this.rpc, uninParams[0].params)
|
||||||
|
if (result.error) {
|
||||||
|
throw result.error
|
||||||
|
}
|
||||||
|
await this.processEvents(result.result)
|
||||||
|
}
|
||||||
|
if (params.length > 0) {
|
||||||
|
logger.debug(`params: ${params.length}`)
|
||||||
|
let results = await batchEthLogs(this.rpc, params)
|
||||||
|
for (let i = 0; i < results.length; i++) {
|
||||||
|
if (results[i].error) {
|
||||||
|
throw results[i].error
|
||||||
}
|
}
|
||||||
await this.processEvents(result.result)
|
let events = results[i].result
|
||||||
|
await this.processEvents(events)
|
||||||
}
|
}
|
||||||
if (params.length > 0) {
|
}
|
||||||
logger.debug(`params: ${params.length}`)
|
nextBlock = toBlock + 1
|
||||||
let results = await batchEthLogs(this.rpc, params)
|
for (let cfg of this.eventCfgs) {
|
||||||
for (let i = 0; i < results.length; i++) {
|
cfg.fromBlock = nextBlock
|
||||||
if (results[i].error) {
|
const redisKey = this.buildRedisKey(cfg)
|
||||||
throw results[i].error
|
await new RedisClient().set(redisKey, cfg.fromBlock + '')
|
||||||
}
|
}
|
||||||
let events = results[i].result
|
await new RedisClient().set(this.redisKey, nextBlock + '')
|
||||||
await this.processEvents(events)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
nextBlock = toBlock + 1;
|
|
||||||
for (let cfg of this.eventCfgs) {
|
|
||||||
cfg.fromBlock = nextBlock
|
|
||||||
const redisKey = this.buildRedisKey(cfg)
|
|
||||||
await new RedisClient().set(redisKey, cfg.fromBlock + '')
|
|
||||||
}
|
|
||||||
await new RedisClient().set(this.redisKey, nextBlock + '')
|
|
||||||
}
|
}
|
||||||
|
|
||||||
buildRedisKey(cfg: IEventCfg) {
|
buildRedisKey(cfg: IEventCfg) {
|
||||||
@ -127,14 +126,14 @@ export class EventBatchSvr {
|
|||||||
const params: any = {
|
const params: any = {
|
||||||
fromBlock: '0x' + cfg.fromBlock.toString(16),
|
fromBlock: '0x' + cfg.fromBlock.toString(16),
|
||||||
toBlock: toBlock ? '0x' + toBlock?.toString(16) : 'latest',
|
toBlock: toBlock ? '0x' + toBlock?.toString(16) : 'latest',
|
||||||
address: [cfg.address]
|
address: [cfg.address],
|
||||||
}
|
}
|
||||||
params.topics = [cfg.topic]
|
params.topics = [cfg.topic]
|
||||||
const result = {
|
const result = {
|
||||||
jsonrpc: "2.0",
|
jsonrpc: '2.0',
|
||||||
method: "eth_getLogs",
|
method: 'eth_getLogs',
|
||||||
params: [params],
|
params: [params],
|
||||||
id: `${cfg.address}_${cfg.event}`
|
id: `${cfg.address}_${cfg.event}`,
|
||||||
}
|
}
|
||||||
return result
|
return result
|
||||||
}
|
}
|
||||||
@ -148,13 +147,13 @@ export class EventBatchSvr {
|
|||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
const address = events[0].address
|
const address = events[0].address
|
||||||
const topic = events[0].topics[0]
|
const topic = events[0].topics[0]
|
||||||
const cfg = this.processer.get((address+topic).toLowerCase())
|
const cfg = this.processer.get((address + topic).toLowerCase())
|
||||||
if (!cfg) {
|
if (!cfg) {
|
||||||
continue;
|
continue
|
||||||
}
|
}
|
||||||
event.chain = this.chainCfg.id + ''
|
event.chain = this.chainCfg.id + ''
|
||||||
event.event = cfg.event
|
event.event = cfg.event
|
||||||
let result = decodeEvent(cfg, event);
|
let result = decodeEvent(cfg, event)
|
||||||
// cfg.fromBlock = Math.max (parseInt(event.blockNumber, 16) + 1, cfg.fromBlock)
|
// cfg.fromBlock = Math.max (parseInt(event.blockNumber, 16) + 1, cfg.fromBlock)
|
||||||
event.decodedData = result
|
event.decodedData = result
|
||||||
const record = await GeneralEvent.saveEvent(event)
|
const record = await GeneralEvent.saveEvent(event)
|
||||||
@ -165,7 +164,7 @@ export class EventBatchSvr {
|
|||||||
} catch (err) {
|
} catch (err) {
|
||||||
logger.error('process event error: ', err.message || err)
|
logger.error('process event error: ', err.message || err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blockNumber
|
return blockNumber
|
||||||
}
|
}
|
||||||
|
@ -1,27 +1,50 @@
|
|||||||
import { batchEthBlocks } from "chain/chain.api";
|
import { batchEthBlocks } from 'chain/chain.api'
|
||||||
import { IScriptionCfg } from "interface/IScriptionCfg";
|
import { IScriptionCfg } from 'interface/IScriptionCfg'
|
||||||
import logger from "logger/logger";
|
import logger from 'logger/logger'
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from 'redis/RedisClient'
|
||||||
import { utf8ToHex } from "./string.util";
|
import { utf8ToHex } from './string.util'
|
||||||
|
|
||||||
const MAX_BATCH_AMOUNT = +process.env.MAX_BLOCK_BATCH_AMOUNT
|
const MAX_BATCH_AMOUNT = +process.env.MAX_BLOCK_BATCH_AMOUNT
|
||||||
const REQUEST_INTERVAL = 0.5 * 1000
|
const REQUEST_INTERVAL = 0.5 * 1000
|
||||||
|
|
||||||
export async function divQueryPassBlocks({chainId, rpc, fromBlock, amount}
|
export async function divQueryPassBlocks({
|
||||||
: {chainId: number, rpc: string, fromBlock: number, amount: number}) {
|
chainId,
|
||||||
|
rpc,
|
||||||
|
fromBlock,
|
||||||
|
amount,
|
||||||
|
}: {
|
||||||
|
chainId: number
|
||||||
|
rpc: string
|
||||||
|
fromBlock: number
|
||||||
|
amount: number
|
||||||
|
}) {
|
||||||
const middleBlock = fromBlock + Math.floor(amount / 2)
|
const middleBlock = fromBlock + Math.floor(amount / 2)
|
||||||
const firstBlocks = await getPastBlocks({chainId, rpc, fromBlock, amount: middleBlock - fromBlock})
|
const firstBlocks = await getPastBlocks({ chainId, rpc, fromBlock, amount: middleBlock - fromBlock })
|
||||||
const secondBlocks = await getPastBlocks({chainId, rpc, fromBlock: middleBlock, amount: amount - (middleBlock - fromBlock)})
|
const secondBlocks = await getPastBlocks({
|
||||||
|
chainId,
|
||||||
|
rpc,
|
||||||
|
fromBlock: middleBlock,
|
||||||
|
amount: amount - (middleBlock - fromBlock),
|
||||||
|
})
|
||||||
return [...firstBlocks, ...secondBlocks]
|
return [...firstBlocks, ...secondBlocks]
|
||||||
}
|
}
|
||||||
|
|
||||||
export async function getPastBlocks({chainId, rpc, fromBlock, amount}
|
export async function getPastBlocks({
|
||||||
: {chainId: number, rpc: string, fromBlock: number, amount: number}) {
|
chainId,
|
||||||
|
rpc,
|
||||||
|
fromBlock,
|
||||||
|
amount,
|
||||||
|
}: {
|
||||||
|
chainId: number
|
||||||
|
rpc: string
|
||||||
|
fromBlock: number
|
||||||
|
amount: number
|
||||||
|
}) {
|
||||||
let blocks = []
|
let blocks = []
|
||||||
logger.info(`chain: ${chainId} from: ${fromBlock} amount: ${amount}`)
|
logger.info(`chain: ${chainId} from: ${fromBlock} amount: ${amount}`)
|
||||||
let blockNumber = fromBlock
|
let blockNumber = fromBlock
|
||||||
const redisKey = `blocknum_${chainId}`
|
const redisKey = `blocknum_${chainId}`
|
||||||
let retryCount = 0;
|
let retryCount = 0
|
||||||
const parseBlocksAndRetry = async (blockNums: number[]) => {
|
const parseBlocksAndRetry = async (blockNums: number[]) => {
|
||||||
let records = await batchEthBlocks(rpc, blockNums)
|
let records = await batchEthBlocks(rpc, blockNums)
|
||||||
if (records.error) {
|
if (records.error) {
|
||||||
@ -31,7 +54,7 @@ export async function getPastBlocks({chainId, rpc, fromBlock, amount}
|
|||||||
let retryNums: number[] = []
|
let retryNums: number[] = []
|
||||||
let maxBlockNumber = 0
|
let maxBlockNumber = 0
|
||||||
for (let i = 0; i < records.length; i++) {
|
for (let i = 0; i < records.length; i++) {
|
||||||
const block = records[i].result;
|
const block = records[i].result
|
||||||
if (block?.hash) {
|
if (block?.hash) {
|
||||||
blocks.push(block)
|
blocks.push(block)
|
||||||
realAmount++
|
realAmount++
|
||||||
@ -40,21 +63,20 @@ export async function getPastBlocks({chainId, rpc, fromBlock, amount}
|
|||||||
if (block) {
|
if (block) {
|
||||||
logger.warn(`block ${blockNumber + i}: ${block}`)
|
logger.warn(`block ${blockNumber + i}: ${block}`)
|
||||||
} else {
|
} else {
|
||||||
logger.warn(`block ${blockNumber + i} is null`)
|
|
||||||
retryNums.push(blockNumber + i)
|
retryNums.push(blockNumber + i)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if (retryNums.length > 0 && ++retryCount < 3) {
|
if (retryNums.length > 0 && ++retryCount < 5) {
|
||||||
logger.info(`${retryCount} retry ${retryNums.length} blocks`)
|
logger.info(`${retryCount} retry: ${retryNums.join(', ')}`)
|
||||||
const retryBlocks = await parseBlocksAndRetry(retryNums)
|
const retryBlocks = await parseBlocksAndRetry(retryNums)
|
||||||
realAmount += retryBlocks
|
realAmount += retryBlocks
|
||||||
}
|
}
|
||||||
return realAmount
|
return realAmount
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
const numsArr = Array.from({length: amount}, (v, k) => k + blockNumber)
|
const numsArr = Array.from({ length: amount }, (v, k) => k + blockNumber)
|
||||||
const realAmount = await parseBlocksAndRetry(numsArr)
|
await parseBlocksAndRetry(numsArr)
|
||||||
if (retryCount > 0) {
|
if (retryCount > 0) {
|
||||||
blocks.sort((a, b) => parseInt(a.number) - parseInt(b.number))
|
blocks.sort((a, b) => parseInt(a.number) - parseInt(b.number))
|
||||||
}
|
}
|
||||||
@ -63,31 +85,39 @@ export async function getPastBlocks({chainId, rpc, fromBlock, amount}
|
|||||||
} catch (e) {
|
} catch (e) {
|
||||||
logger.log(e.message || e)
|
logger.log(e.message || e)
|
||||||
if (e.message && /Too Many Requests/.test(e.message) && amount > 1) {
|
if (e.message && /Too Many Requests/.test(e.message) && amount > 1) {
|
||||||
blocks = await divQueryPassBlocks({chainId, rpc, fromBlock, amount})
|
blocks = await divQueryPassBlocks({ chainId, rpc, fromBlock, amount })
|
||||||
} else if (e.message && /Public RPC Rate Limit Hit, limit will reset in \d+ seconds/.test(e.message)) {
|
} else if (e.message && /Public RPC Rate Limit Hit, limit will reset in \d+ seconds/.test(e.message)) {
|
||||||
const match = e.message.match(/Public RPC Rate Limit Hit, limit will reset in (\d+) seconds/)
|
const match = e.message.match(/Public RPC Rate Limit Hit, limit will reset in (\d+) seconds/)
|
||||||
const seconds = parseInt(match[1])
|
const seconds = parseInt(match[1])
|
||||||
await new Promise(resolve => setTimeout(resolve, seconds * 1000))
|
await new Promise(resolve => setTimeout(resolve, seconds * 1000))
|
||||||
blocks = await getPastBlocks({chainId, rpc, fromBlock, amount})
|
blocks = await getPastBlocks({ chainId, rpc, fromBlock, amount })
|
||||||
}else {
|
} else {
|
||||||
throw e
|
throw e
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return blocks
|
return blocks
|
||||||
}
|
}
|
||||||
|
|
||||||
export function* getPastBlocksIter({chainId, rpc, fromBlock, amount}
|
export function* getPastBlocksIter({
|
||||||
: {chainId: number, rpc: string, fromBlock: number, amount: number}) {
|
chainId,
|
||||||
|
rpc,
|
||||||
|
fromBlock,
|
||||||
|
amount,
|
||||||
|
}: {
|
||||||
|
chainId: number
|
||||||
|
rpc: string
|
||||||
|
fromBlock: number
|
||||||
|
amount: number
|
||||||
|
}) {
|
||||||
logger.info(`chain: ${chainId} from: ${fromBlock} amount: ${amount}`)
|
logger.info(`chain: ${chainId} from: ${fromBlock} amount: ${amount}`)
|
||||||
let remain = amount
|
let remain = amount
|
||||||
while (remain > 0) {
|
while (remain > 0) {
|
||||||
yield getPastBlocks({chainId, rpc, fromBlock, amount: Math.min(MAX_BATCH_AMOUNT, remain)})
|
yield getPastBlocks({ chainId, rpc, fromBlock, amount: Math.min(MAX_BATCH_AMOUNT, remain) })
|
||||||
fromBlock += MAX_BATCH_AMOUNT
|
fromBlock += MAX_BATCH_AMOUNT
|
||||||
remain -= MAX_BATCH_AMOUNT
|
remain -= MAX_BATCH_AMOUNT
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export const buildScriptionFilters = (cfg: IScriptionCfg) => {
|
export const buildScriptionFilters = (cfg: IScriptionCfg) => {
|
||||||
if (cfg.filter) {
|
if (cfg.filter) {
|
||||||
return cfg.filter
|
return cfg.filter
|
||||||
@ -144,7 +174,7 @@ export const buildScriptionFilters = (cfg: IScriptionCfg) => {
|
|||||||
} else if (filter.type === 'utf8_data') {
|
} else if (filter.type === 'utf8_data') {
|
||||||
value = `'0x${utf8ToHex(value)}'`
|
value = `'0x${utf8ToHex(value)}'`
|
||||||
} else if (filter.type === 'hex_data') {
|
} else if (filter.type === 'hex_data') {
|
||||||
value = `'${value.indexOf('0x') === 0 ? value : '0x'+value}'`
|
value = `'${value.indexOf('0x') === 0 ? value : '0x' + value}'`
|
||||||
} else if (filter.type === 'number') {
|
} else if (filter.type === 'number') {
|
||||||
value = parseInt(value)
|
value = parseInt(value)
|
||||||
} else if (filter.type === 'boolean') {
|
} else if (filter.type === 'boolean') {
|
||||||
@ -162,7 +192,7 @@ export const buildScriptionFilters = (cfg: IScriptionCfg) => {
|
|||||||
} else if (op === 'like') {
|
} else if (op === 'like') {
|
||||||
body += new RegExp(value).test(`event.${filter.key}`)
|
body += new RegExp(value).test(`event.${filter.key}`)
|
||||||
} else {
|
} else {
|
||||||
body += `event.${filter.key}${op}${value}`
|
body += `event.${filter.key}${op}${value}`
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -1,39 +1,39 @@
|
|||||||
// format the date to the format we want
|
// format the date to the format we want
|
||||||
export const formatDate = (date: Date): string => {
|
export const formatDate = (date: Date): string => {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear()
|
||||||
const month = (date.getMonth() + 1 + '').padStart(2, '0');
|
const month = (date.getMonth() + 1 + '').padStart(2, '0')
|
||||||
const day = (date.getDate() + '').padStart(2, '0');
|
const day = (date.getDate() + '').padStart(2, '0')
|
||||||
return `${year}${month}${day}`;
|
return `${year}${month}${day}`
|
||||||
};
|
}
|
||||||
|
|
||||||
// get begin of one day
|
// get begin of one day
|
||||||
export const getDayBegin = (date: Date): Date => {
|
export const getDayBegin = (date: Date): Date => {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear()
|
||||||
const month = date.getMonth();
|
const month = date.getMonth()
|
||||||
const day = date.getDate();
|
const day = date.getDate()
|
||||||
return new Date(year, month, day);
|
return new Date(year, month, day)
|
||||||
};
|
}
|
||||||
|
|
||||||
// get begin of n day ago
|
// get begin of n day ago
|
||||||
export const getNDayAgo = (n: number, begin: boolean): Date => {
|
export const getNDayAgo = (n: number, begin: boolean): Date => {
|
||||||
const date = new Date(Date.now() - n * 24 * 60 * 60 * 1000)
|
const date = new Date(Date.now() - n * 24 * 60 * 60 * 1000)
|
||||||
if (begin) {
|
if (begin) {
|
||||||
return getDayBegin(date);
|
return getDayBegin(date)
|
||||||
} else {
|
} else {
|
||||||
return date;
|
return date
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
||||||
// get begin of this month
|
// get begin of this month
|
||||||
export const getMonthBegin = (date: Date): Date => {
|
export const getMonthBegin = (date: Date): Date => {
|
||||||
const year = date.getFullYear();
|
const year = date.getFullYear()
|
||||||
const month = date.getMonth();
|
const month = date.getMonth()
|
||||||
return new Date(year, month, 1);
|
return new Date(year, month, 1)
|
||||||
}
|
}
|
||||||
|
|
||||||
// get formated datestring of yesterday
|
// get formated datestring of yesterday
|
||||||
export const yesterday = () => {
|
export const yesterday = () => {
|
||||||
const date = new Date();
|
const date = new Date()
|
||||||
date.setDate(date.getDate() - 1);
|
date.setDate(date.getDate() - 1)
|
||||||
return date;
|
return date
|
||||||
};
|
}
|
||||||
|
@ -1,8 +1,7 @@
|
|||||||
|
|
||||||
//@ts-ignore
|
//@ts-ignore
|
||||||
import { keccak256, _jsonInterfaceMethodToString, AbiInput } from "web3-utils";
|
import { keccak256, _jsonInterfaceMethodToString, AbiInput } from 'web3-utils'
|
||||||
import web3abi from 'web3-eth-abi';
|
import web3abi from 'web3-eth-abi'
|
||||||
import { IEventCfg } from "interface/IEventCfg";
|
import { IEventCfg } from 'interface/IEventCfg'
|
||||||
|
|
||||||
export const getTopics = (cfg: IEventCfg) => {
|
export const getTopics = (cfg: IEventCfg) => {
|
||||||
// let abi = cfg.abi
|
// let abi = cfg.abi
|
||||||
@ -12,21 +11,21 @@ export const getTopics = (cfg: IEventCfg) => {
|
|||||||
// }
|
// }
|
||||||
// topic = topic.slice(0, -1)
|
// topic = topic.slice(0, -1)
|
||||||
// topic += ')'
|
// topic += ')'
|
||||||
return keccak256(_jsonInterfaceMethodToString(cfg.abi))
|
return keccak256(_jsonInterfaceMethodToString(cfg.abi))
|
||||||
}
|
}
|
||||||
|
|
||||||
export const decodeEvent = (cfg: IEventCfg, eventData: {data: string, topics: string[]}) => {
|
export const decodeEvent = (cfg: IEventCfg, eventData: { data: string; topics: string[] }) => {
|
||||||
const abiInputs = cfg.abi.inputs;
|
const abiInputs = cfg.abi.inputs
|
||||||
let result = web3abi.decodeLog(abiInputs, eventData.data, eventData.topics.slice(1));
|
let result = web3abi.decodeLog(abiInputs, eventData.data, eventData.topics.slice(1))
|
||||||
let decodedData: any = {}
|
let decodedData: any = {}
|
||||||
for (let i = 0; i < abiInputs.length; i++) {
|
for (let i = 0; i < abiInputs.length; i++) {
|
||||||
const input: AbiInput = abiInputs[i];
|
const input: AbiInput = abiInputs[i]
|
||||||
if (input.type === 'tuple[]') {
|
if (input.type === 'tuple[]') {
|
||||||
// @ts-ignore
|
// @ts-ignore
|
||||||
decodedData[input.name] = result[i].map(item => {
|
decodedData[input.name] = result[i].map(item => {
|
||||||
let itemData = {}
|
let itemData = {}
|
||||||
for (let j = 0; j < input.components.length; j++) {
|
for (let j = 0; j < input.components.length; j++) {
|
||||||
const component = input.components[j];
|
const component = input.components[j]
|
||||||
itemData[component.name] = item[j]
|
itemData[component.name] = item[j]
|
||||||
}
|
}
|
||||||
return itemData
|
return itemData
|
||||||
@ -34,7 +33,7 @@ export const decodeEvent = (cfg: IEventCfg, eventData: {data: string, topics: st
|
|||||||
} else if (input.type === 'tuple') {
|
} else if (input.type === 'tuple') {
|
||||||
let itemData = {}
|
let itemData = {}
|
||||||
for (let j = 0; j < input.components.length; j++) {
|
for (let j = 0; j < input.components.length; j++) {
|
||||||
const component = input.components[j];
|
const component = input.components[j]
|
||||||
itemData[component.name] = result[i][j]
|
itemData[component.name] = result[i][j]
|
||||||
}
|
}
|
||||||
decodedData[input.name] = itemData
|
decodedData[input.name] = itemData
|
||||||
|
@ -1,5 +1,5 @@
|
|||||||
import Web3 from 'web3';
|
import Web3 from 'web3'
|
||||||
import { BN } from 'ethereumjs-util';
|
import { BN } from 'ethereumjs-util'
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Converts some token minimal unit to render format string, showing 5 decimals
|
* Converts some token minimal unit to render format string, showing 5 decimals
|
||||||
@ -10,23 +10,17 @@ import { BN } from 'ethereumjs-util';
|
|||||||
* @returns {String} - Number of token minimal unit, in render format
|
* @returns {String} - Number of token minimal unit, in render format
|
||||||
* If value is less than 5 precision decimals will show '< 0.00001'
|
* If value is less than 5 precision decimals will show '< 0.00001'
|
||||||
*/
|
*/
|
||||||
export function renderFromTokenMinimalUnit(
|
export function renderFromTokenMinimalUnit(tokenValue, decimals, decimalsToShow = 5) {
|
||||||
tokenValue,
|
const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals)
|
||||||
decimals,
|
const minimalUnitNumber = parseFloat(minimalUnit)
|
||||||
decimalsToShow = 5
|
let renderMinimalUnit
|
||||||
) {
|
|
||||||
const minimalUnit = fromTokenMinimalUnit(tokenValue || 0, decimals);
|
|
||||||
const minimalUnitNumber = parseFloat(minimalUnit);
|
|
||||||
let renderMinimalUnit;
|
|
||||||
if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) {
|
if (minimalUnitNumber < 0.00001 && minimalUnitNumber > 0) {
|
||||||
renderMinimalUnit = "< 0.00001";
|
renderMinimalUnit = '< 0.00001'
|
||||||
} else {
|
} else {
|
||||||
const base = Math.pow(10, decimalsToShow);
|
const base = Math.pow(10, decimalsToShow)
|
||||||
renderMinimalUnit = (
|
renderMinimalUnit = (Math.round(minimalUnitNumber * base) / base).toString()
|
||||||
Math.round(minimalUnitNumber * base) / base
|
|
||||||
).toString();
|
|
||||||
}
|
}
|
||||||
return renderMinimalUnit;
|
return renderMinimalUnit
|
||||||
}
|
}
|
||||||
/**
|
/**
|
||||||
* Converts token minimal unit to readable string value
|
* Converts token minimal unit to readable string value
|
||||||
@ -36,25 +30,25 @@ export function renderFromTokenMinimalUnit(
|
|||||||
* @returns {string} - String containing the new number
|
* @returns {string} - String containing the new number
|
||||||
*/
|
*/
|
||||||
export function fromTokenMinimalUnit(minimalInput, decimals) {
|
export function fromTokenMinimalUnit(minimalInput, decimals) {
|
||||||
minimalInput = addHexPrefix(Number(minimalInput).toString(16));
|
minimalInput = addHexPrefix(Number(minimalInput).toString(16))
|
||||||
let minimal = safeNumberToBN(minimalInput);
|
let minimal = safeNumberToBN(minimalInput)
|
||||||
const negative = minimal.lt(new BN(0));
|
const negative = minimal.lt(new BN(0))
|
||||||
const base = Web3.utils.toBN(Math.pow(10, decimals).toString());
|
const base = Web3.utils.toBN(Math.pow(10, decimals).toString())
|
||||||
|
|
||||||
if (negative) {
|
if (negative) {
|
||||||
minimal = minimal.mul(new BN(-1));
|
minimal = minimal.mul(new BN(-1))
|
||||||
}
|
}
|
||||||
let fraction = minimal.mod(base).toString(10);
|
let fraction = minimal.mod(base).toString(10)
|
||||||
while (fraction.length < decimals) {
|
while (fraction.length < decimals) {
|
||||||
fraction = "0" + fraction;
|
fraction = '0' + fraction
|
||||||
}
|
}
|
||||||
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1];
|
fraction = fraction.match(/^([0-9]*[1-9]|0)(0*)/)[1]
|
||||||
const whole = minimal.div(base).toString(10);
|
const whole = minimal.div(base).toString(10)
|
||||||
let value = "" + whole + (fraction === "0" ? "" : "." + fraction);
|
let value = '' + whole + (fraction === '0' ? '' : '.' + fraction)
|
||||||
if (negative) {
|
if (negative) {
|
||||||
value = "-" + value;
|
value = '-' + value
|
||||||
}
|
}
|
||||||
return value;
|
return value
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -65,20 +59,20 @@ export function fromTokenMinimalUnit(minimalInput, decimals) {
|
|||||||
* @returns {String} - Number of token minimal unit, in render format
|
* @returns {String} - Number of token minimal unit, in render format
|
||||||
* If value is less than 5 precision decimals will show '< 0.00001'
|
* If value is less than 5 precision decimals will show '< 0.00001'
|
||||||
*/
|
*/
|
||||||
export function renderFromWei(value, decimalsToShow = 5) {
|
export function renderFromWei(value, decimalsToShow = 5) {
|
||||||
let renderWei = '0';
|
let renderWei = '0'
|
||||||
// avoid undefined
|
// avoid undefined
|
||||||
if (value) {
|
if (value) {
|
||||||
const wei = Web3.utils.fromWei(value);
|
const wei = Web3.utils.fromWei(value)
|
||||||
const weiNumber = parseFloat(wei);
|
const weiNumber = parseFloat(wei)
|
||||||
if (weiNumber < 0.00001 && weiNumber > 0) {
|
if (weiNumber < 0.00001 && weiNumber > 0) {
|
||||||
renderWei = '< 0.00001';
|
renderWei = '< 0.00001'
|
||||||
} else {
|
} else {
|
||||||
const base = Math.pow(10, decimalsToShow);
|
const base = Math.pow(10, decimalsToShow)
|
||||||
renderWei = (Math.round(weiNumber * base) / base).toString();
|
renderWei = (Math.round(weiNumber * base) / base).toString()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return renderWei;
|
return renderWei
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -89,7 +83,7 @@ export function fromTokenMinimalUnit(minimalInput, decimals) {
|
|||||||
* @returns {string} - String of the hex token value
|
* @returns {string} - String of the hex token value
|
||||||
*/
|
*/
|
||||||
export function calcTokenValueToSend(value, decimals) {
|
export function calcTokenValueToSend(value, decimals) {
|
||||||
return value ? (value * Math.pow(10, decimals)).toString(16) : 0;
|
return value ? (value * Math.pow(10, decimals)).toString(16) : 0
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -99,11 +93,7 @@ export function calcTokenValueToSend(value, decimals) {
|
|||||||
* @returns {boolean} - True if the string is a valid decimal
|
* @returns {boolean} - True if the string is a valid decimal
|
||||||
*/
|
*/
|
||||||
export function isDecimal(value) {
|
export function isDecimal(value) {
|
||||||
return (
|
return Number.isFinite(parseFloat(value)) && !Number.isNaN(parseFloat(value)) && !isNaN(+value)
|
||||||
Number.isFinite(parseFloat(value)) &&
|
|
||||||
!Number.isNaN(parseFloat(value)) &&
|
|
||||||
!isNaN(+value)
|
|
||||||
);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -113,7 +103,7 @@ export function isDecimal(value) {
|
|||||||
* @returns {Object} - BN instance
|
* @returns {Object} - BN instance
|
||||||
*/
|
*/
|
||||||
export function toBN(value) {
|
export function toBN(value) {
|
||||||
return Web3.utils.toBN(value);
|
return Web3.utils.toBN(value)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -123,20 +113,20 @@ export function toBN(value) {
|
|||||||
* @returns {string} The prefixed string.
|
* @returns {string} The prefixed string.
|
||||||
*/
|
*/
|
||||||
export const addHexPrefix = (str: string) => {
|
export const addHexPrefix = (str: string) => {
|
||||||
if (typeof str !== "string" || str.match(/^-?0x/u)) {
|
if (typeof str !== 'string' || str.match(/^-?0x/u)) {
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str.match(/^-?0X/u)) {
|
if (str.match(/^-?0X/u)) {
|
||||||
return str.replace("0X", "0x");
|
return str.replace('0X', '0x')
|
||||||
}
|
}
|
||||||
|
|
||||||
if (str.startsWith("-")) {
|
if (str.startsWith('-')) {
|
||||||
return str.replace("-", "-0x");
|
return str.replace('-', '-0x')
|
||||||
}
|
}
|
||||||
|
|
||||||
return `0x${str}`;
|
return `0x${str}`
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Wraps 'numberToBN' method to avoid potential undefined and decimal values
|
* Wraps 'numberToBN' method to avoid potential undefined and decimal values
|
||||||
@ -145,8 +135,8 @@ export const addHexPrefix = (str: string) => {
|
|||||||
* @returns {Object} - The converted value as BN instance
|
* @returns {Object} - The converted value as BN instance
|
||||||
*/
|
*/
|
||||||
export function safeNumberToBN(value: number | string) {
|
export function safeNumberToBN(value: number | string) {
|
||||||
const safeValue = fastSplit(value.toString()) || "0";
|
const safeValue = fastSplit(value.toString()) || '0'
|
||||||
return numberToBN(safeValue);
|
return numberToBN(safeValue)
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -157,60 +147,52 @@ export function safeNumberToBN(value: number | string) {
|
|||||||
* @returns {string} - the selected splitted element
|
* @returns {string} - the selected splitted element
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export function fastSplit(value, divider = ".") {
|
export function fastSplit(value, divider = '.') {
|
||||||
value += "";
|
value += ''
|
||||||
const [from, to] = [value.indexOf(divider), 0];
|
const [from, to] = [value.indexOf(divider), 0]
|
||||||
return value.substring(from, to) || value;
|
return value.substring(from, to) || value
|
||||||
}
|
}
|
||||||
|
|
||||||
export function stripHexPrefix(str: string) {
|
export function stripHexPrefix(str: string) {
|
||||||
if (typeof str !== "string") {
|
if (typeof str !== 'string') {
|
||||||
return str;
|
return str
|
||||||
}
|
}
|
||||||
|
|
||||||
return str.slice(0, 2) === "0x" ? str.slice(2) : str;
|
return str.slice(0, 2) === '0x' ? str.slice(2) : str
|
||||||
}
|
}
|
||||||
|
|
||||||
export function numberToBN(arg) {
|
export function numberToBN(arg) {
|
||||||
if (typeof arg === "string" || typeof arg === "number") {
|
if (typeof arg === 'string' || typeof arg === 'number') {
|
||||||
var multiplier = Web3.utils.toBN(1); // eslint-disable-line
|
var multiplier = Web3.utils.toBN(1); // eslint-disable-line
|
||||||
var formattedString = String(arg).toLowerCase().trim();
|
var formattedString = String(arg).toLowerCase().trim()
|
||||||
var isHexPrefixed =
|
var isHexPrefixed = formattedString.substr(0, 2) === '0x' || formattedString.substr(0, 3) === '-0x'
|
||||||
formattedString.substr(0, 2) === "0x" ||
|
|
||||||
formattedString.substr(0, 3) === "-0x";
|
|
||||||
var stringArg = stripHexPrefix(formattedString); // eslint-disable-line
|
var stringArg = stripHexPrefix(formattedString); // eslint-disable-line
|
||||||
if (stringArg.substr(0, 1) === "-") {
|
if (stringArg.substr(0, 1) === '-') {
|
||||||
stringArg = stripHexPrefix(stringArg.slice(1));
|
stringArg = stripHexPrefix(stringArg.slice(1))
|
||||||
multiplier = Web3.utils.toBN(-1);
|
multiplier = Web3.utils.toBN(-1)
|
||||||
}
|
}
|
||||||
stringArg = stringArg === "" ? "0" : stringArg;
|
stringArg = stringArg === '' ? '0' : stringArg
|
||||||
|
|
||||||
if (
|
if (
|
||||||
(!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) ||
|
(!stringArg.match(/^-?[0-9]+$/) && stringArg.match(/^[0-9A-Fa-f]+$/)) ||
|
||||||
stringArg.match(/^[a-fA-F]+$/) ||
|
stringArg.match(/^[a-fA-F]+$/) ||
|
||||||
(isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/))
|
(isHexPrefixed === true && stringArg.match(/^[0-9A-Fa-f]+$/))
|
||||||
) {
|
) {
|
||||||
return Web3.utils.toBN(stringArg).mul(multiplier);
|
return Web3.utils.toBN(stringArg).mul(multiplier)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (
|
if ((stringArg.match(/^-?[0-9]+$/) || stringArg === '') && isHexPrefixed === false) {
|
||||||
(stringArg.match(/^-?[0-9]+$/) || stringArg === "") &&
|
return Web3.utils.toBN(stringArg).mul(multiplier)
|
||||||
isHexPrefixed === false
|
|
||||||
) {
|
|
||||||
return Web3.utils.toBN(stringArg).mul(multiplier);
|
|
||||||
}
|
}
|
||||||
} else if (typeof arg === "object" && arg.toString && !arg.pop && !arg.push) {
|
} else if (typeof arg === 'object' && arg.toString && !arg.pop && !arg.push) {
|
||||||
if (
|
if (arg.toString(10).match(/^-?[0-9]+$/) && (arg.mul || arg.dividedToIntegerBy)) {
|
||||||
arg.toString(10).match(/^-?[0-9]+$/) &&
|
return Web3.utils.toBN(arg.toString(10))
|
||||||
(arg.mul || arg.dividedToIntegerBy)
|
|
||||||
) {
|
|
||||||
return Web3.utils.toBN(arg.toString(10));
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
throw new Error(
|
throw new Error(
|
||||||
"[number-to-bn] while converting number " +
|
'[number-to-bn] while converting number ' +
|
||||||
JSON.stringify(arg) +
|
JSON.stringify(arg) +
|
||||||
" to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported."
|
' to BN.js instance, error: invalid number value. Value must be an integer, hex string, BN or BigNumber instance. Note, decimals are not supported.',
|
||||||
);
|
)
|
||||||
}
|
}
|
||||||
|
@ -107,18 +107,17 @@ export function string62to10(numberCode: string) {
|
|||||||
|
|
||||||
export function hexToUtf8(hexString) {
|
export function hexToUtf8(hexString) {
|
||||||
// Remove any leading "0x" prefix and split into pairs of characters
|
// Remove any leading "0x" prefix and split into pairs of characters
|
||||||
let _hexString = hexString.replace(/^0x/, "");
|
let _hexString = hexString.replace(/^0x/, '')
|
||||||
let buffer = Buffer.from(_hexString, 'hex')
|
let buffer = Buffer.from(_hexString, 'hex')
|
||||||
return buffer.toString('utf8')
|
return buffer.toString('utf8')
|
||||||
}
|
}
|
||||||
|
|
||||||
export function utf8ToHex(utf8String) {
|
export function utf8ToHex(utf8String) {
|
||||||
// Create a Buffer object from the UTF-8 string
|
// Create a Buffer object from the UTF-8 string
|
||||||
const buffer = Buffer.from(utf8String, "utf8");
|
const buffer = Buffer.from(utf8String, 'utf8')
|
||||||
|
|
||||||
// Convert the Buffer object to a hex string
|
// Convert the Buffer object to a hex string
|
||||||
const hexString = buffer.toString("hex");
|
const hexString = buffer.toString('hex')
|
||||||
|
|
||||||
return hexString;
|
return hexString
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
x
Reference in New Issue
Block a user