upgrade eslint, reformat code

This commit is contained in:
CounterFire2023 2024-01-17 19:19:02 +08:00
parent 36621a9206
commit 4b45ccfce3
26 changed files with 578 additions and 992 deletions

View File

@ -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"
} }
} }

View File

@ -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,13 +6,13 @@ 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
} }
@ -20,22 +20,21 @@ const request = async (url: string, options: any) => {
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())
} }

View File

@ -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) {
@ -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

View File

@ -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()
})(); })()

View File

@ -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
} }

View File

@ -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>
} }

View File

@ -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 })
} }
} }

View File

@ -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
@ -53,7 +53,7 @@ export class CheckInClass extends BaseModule {
address: this.from, address: this.from,
day: this.dateTag, day: this.dateTag,
time: this.blockTime, time: this.blockTime,
count: this.count count: this.count,
} }
} }
} }

View File

@ -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 },
}) })

View File

@ -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,7 +28,7 @@ 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()
@ -36,7 +36,6 @@ export class GeneralEventClass extends BaseModule {
@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

View File

@ -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()
} }
} }

View File

@ -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
/** /**
* *
{ {
@ -84,21 +82,22 @@ export class NftStakeClass extends BaseModule {
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 },
}) },
)
} }
} }
} }

View File

@ -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 },
}) })

View File

@ -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()
} }
} }
} }

View File

@ -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) {

View File

@ -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()
})(); })()

View File

@ -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) {
@ -41,7 +40,7 @@ export class BlockSyncSvr {
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)
} }
@ -57,15 +56,15 @@ 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) {

View File

@ -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,14 +33,13 @@ 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)
@ -52,7 +51,7 @@ export class EventBatchSvr {
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)
@ -102,7 +101,7 @@ export class EventBatchSvr {
await this.processEvents(events) await this.processEvents(events)
} }
} }
nextBlock = toBlock + 1; nextBlock = toBlock + 1
for (let cfg of this.eventCfgs) { for (let cfg of this.eventCfgs) {
cfg.fromBlock = nextBlock cfg.fromBlock = nextBlock
const redisKey = this.buildRedisKey(cfg) const redisKey = this.buildRedisKey(cfg)
@ -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)

View File

@ -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') {

View File

@ -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
}; }

View File

@ -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
@ -15,18 +14,18 @@ export const getTopics = (cfg: IEventCfg) => {
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

View File

@ -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.',
); )
} }

View File

@ -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
} }

763
yarn.lock

File diff suppressed because it is too large Load Diff