修改blockdata抓取, 配置从json读取
This commit is contained in:
parent
ba4d63b185
commit
acfee0ee69
19
config/scription_list.json
Normal file
19
config/scription_list.json
Normal file
@ -0,0 +1,19 @@
|
||||
[
|
||||
{
|
||||
"chain": 421614,
|
||||
"rpc": "https://arb-sepolia.g.alchemy.com/v2/mHoYM0SyjeizxvdjShcdOHiCrXOM_mlg",
|
||||
"fromBlock": 5624211,
|
||||
"filters": [{
|
||||
"key": "input",
|
||||
"op": "eq",
|
||||
"type": "utf8_data",
|
||||
"value": "data:,{\"p\":\"cf-20\",\"op\":\"check\"}"
|
||||
},{
|
||||
"key": "to",
|
||||
"op": "eq",
|
||||
"type": "address",
|
||||
"value": "0x50a8e60041a206acaa5f844a1104896224be6f39"
|
||||
}],
|
||||
"dataModel": "CheckIn"
|
||||
}
|
||||
]
|
@ -1,30 +0,0 @@
|
||||
import { CheckIn } from "models/CheckIn"
|
||||
import { utf8ToHex } from "utils/string.util"
|
||||
|
||||
export interface IScriptionCfg {
|
||||
chain: number,
|
||||
rpc?: string,
|
||||
fromBlock: number,
|
||||
filter: (event: any) => boolean,
|
||||
process: (event: any) => Promise<void>,
|
||||
}
|
||||
|
||||
const CHECKIN_DATA_STR = 'data:,{"p":"cf-20","op":"check"}'
|
||||
const CHECKIN_DATA_HEX = '0x'+utf8ToHex(CHECKIN_DATA_STR)
|
||||
const CHECKIN_ADDRESS = '0x50a8e60041a206acaa5f844a1104896224be6f39'
|
||||
|
||||
export const SCRIPTIONS_CFG: IScriptionCfg[] = [
|
||||
{
|
||||
chain: 421614,
|
||||
// rpc: 'https://arbitrum-sepolia.infura.io/v3/25559ac58e714177b31ff48d507e7ac9',
|
||||
rpc: 'https://arb-sepolia.g.alchemy.com/v2/EKR1je8ZGia332kkemNc4mtXQuFskIq3',
|
||||
fromBlock: 5624211,
|
||||
filter: (event: any) => {
|
||||
return ( event.input === CHECKIN_DATA_HEX
|
||||
&& event.to.toLowerCase() === CHECKIN_ADDRESS)
|
||||
},
|
||||
process: async (tx: any) => {
|
||||
return CheckIn.saveEvent(tx)
|
||||
}
|
||||
}
|
||||
]
|
38
src/interface/IScriptionCfg.ts
Normal file
38
src/interface/IScriptionCfg.ts
Normal file
@ -0,0 +1,38 @@
|
||||
export enum FilterEnum {
|
||||
eq = 'eq',
|
||||
ne = 'ne',
|
||||
gt = 'gt',
|
||||
gte = 'gte',
|
||||
lt = 'lt',
|
||||
lte = 'lte',
|
||||
in = 'in',
|
||||
nin = 'nin',
|
||||
like = 'like',
|
||||
nlike = 'nlike',
|
||||
isNull = 'isNull',
|
||||
isNotNull = 'isNotNull'
|
||||
}
|
||||
|
||||
export enum FilterValueTypeEnum {
|
||||
string = 'string',
|
||||
number = 'number',
|
||||
boolean = 'boolean',
|
||||
utf8_data = 'utf8_data',
|
||||
hex_data = 'hex_data',
|
||||
address = 'address'
|
||||
}
|
||||
|
||||
export interface IFilter {
|
||||
key: string,
|
||||
type: string,
|
||||
value: string,
|
||||
op: FilterEnum
|
||||
}
|
||||
export interface IScriptionCfg {
|
||||
chain: number,
|
||||
rpc?: string,
|
||||
fromBlock: number,
|
||||
filters?: IFilter[],
|
||||
filter: (event: any) => boolean,
|
||||
process: (event: any) => Promise<void>,
|
||||
}
|
@ -42,9 +42,9 @@ export class CheckInClass extends BaseModule {
|
||||
event.count = preDayEvent.count + 1
|
||||
}
|
||||
try {
|
||||
await CheckIn.insertOrUpdate({ from: event.from, dataTag: event.dataTag }, event)
|
||||
await CheckIn.insertOrUpdate({ from: event.from, dateTag: event.dateTag }, event)
|
||||
} catch (err) {
|
||||
logger.info('save check record error: ', event.dataTag, event.from, err.message || err)
|
||||
logger.info('save check record error: ', event.dateTag, event.from, err.message || err)
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -3,20 +3,36 @@ import logger from 'logger/logger'
|
||||
import { RedisClient } from 'redis/RedisClient'
|
||||
const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development'
|
||||
dotenv.config({ path: envFile })
|
||||
import {IScriptionCfg, SCRIPTIONS_CFG} from 'config/scriptions_cfg'
|
||||
|
||||
const scriptions = require('../config/scription_list.json')
|
||||
|
||||
import 'common/Extend'
|
||||
import { AllChains, IChain } from 'chain/allchain'
|
||||
import { BlockSyncSvr } from 'service/block.sync.service'
|
||||
import { IScriptionCfg } from 'interface/IScriptionCfg'
|
||||
import { buildScriptionFilters } from 'utils/block.util'
|
||||
import { CheckIn } from 'models/CheckIn'
|
||||
|
||||
let svrs: any[] = []
|
||||
let lock = false
|
||||
|
||||
let eventProcessers = {
|
||||
CheckIn: CheckIn,
|
||||
}
|
||||
|
||||
async function initEventSvrs() {
|
||||
const cfgMap: Map<IChain, IScriptionCfg[]> = new Map();
|
||||
for (let cfg of SCRIPTIONS_CFG) {
|
||||
for (let cfg of scriptions) {
|
||||
if (!cfg.filter && cfg.filters) {
|
||||
cfg.filter = buildScriptionFilters(cfg)
|
||||
}
|
||||
cfg.process = async (event: any) => {
|
||||
let processer = eventProcessers[cfg.dataModel]
|
||||
if (!processer) {
|
||||
logger.error('processer not found: ' + cfg.dataModel)
|
||||
return
|
||||
}
|
||||
await processer.saveEvent(event)
|
||||
}
|
||||
const chainCfg = AllChains.find((chain) => chain.id === cfg.chain)
|
||||
if (!chainCfg) {
|
||||
logger.error('chainCfg not found: ' + cfg.chain)
|
||||
|
@ -1,10 +1,13 @@
|
||||
import { IChain } from "chain/allchain";
|
||||
import { ethBlockNumber, retryEthBlockNumber } from "chain/chain.api";
|
||||
import { IScriptionCfg } from "config/scriptions_cfg";
|
||||
import { retryEthBlockNumber } from "chain/chain.api";
|
||||
import { IScriptionCfg } from "interface/IScriptionCfg";
|
||||
import logger from "logger/logger";
|
||||
import { CheckIn } from "models/CheckIn";
|
||||
import { RedisClient } from "redis/RedisClient";
|
||||
import { getPastBlocksIter } from "utils/block.util";
|
||||
import { formatDate } from "utils/date.util";
|
||||
|
||||
|
||||
export class BlockSyncSvr {
|
||||
chainCfg: IChain
|
||||
scriptionCfgs: IScriptionCfg[] = []
|
||||
@ -66,9 +69,14 @@ export class BlockSyncSvr {
|
||||
}
|
||||
for (let j = 0; j < this.scriptionCfgs.length; j++) {
|
||||
const cfg = this.scriptionCfgs[j];
|
||||
if (cfg.filter(tx)) {
|
||||
await cfg.process(tx)
|
||||
}
|
||||
|
||||
if (cfg.filter && cfg.filter(tx) ) {
|
||||
try {
|
||||
await cfg.process(tx)
|
||||
} catch (err) {
|
||||
logger.error('process tx error: ', err.message || err)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -1,6 +1,8 @@
|
||||
import { batchEthBlocks } from "chain/chain.api";
|
||||
import { IScriptionCfg } from "interface/IScriptionCfg";
|
||||
import logger from "logger/logger";
|
||||
import { RedisClient } from "redis/RedisClient";
|
||||
import { utf8ToHex } from "./string.util";
|
||||
|
||||
const MAX_BATCH_AMOUNT = 500
|
||||
const REQUEST_INTERVAL = 0.5 * 1000
|
||||
@ -84,3 +86,86 @@ export function* getPastBlocksIter({chainId, rpc, fromBlock, amount}
|
||||
remain -= MAX_BATCH_AMOUNT
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
export const buildScriptionFilters = (cfg: IScriptionCfg) => {
|
||||
if (cfg.filter) {
|
||||
return cfg.filter
|
||||
}
|
||||
if (cfg.filters) {
|
||||
let body = ''
|
||||
for (let i = 0; i < cfg.filters.length; i++) {
|
||||
if (i > 0) {
|
||||
body += ' && '
|
||||
}
|
||||
let filter = cfg.filters[i]
|
||||
let value: any = filter.value
|
||||
let op = ''
|
||||
switch (filter.op) {
|
||||
case 'eq':
|
||||
op = '==='
|
||||
break
|
||||
case 'ne':
|
||||
op = '!=='
|
||||
break
|
||||
case 'gt':
|
||||
op = '>'
|
||||
break
|
||||
case 'gte':
|
||||
op = '>='
|
||||
break
|
||||
case 'lt':
|
||||
op = '<'
|
||||
break
|
||||
case 'lte':
|
||||
op = '<='
|
||||
break
|
||||
case 'in':
|
||||
op = 'in'
|
||||
break
|
||||
case 'nin':
|
||||
op = 'nin'
|
||||
break
|
||||
case 'like':
|
||||
op = 'like'
|
||||
break
|
||||
case 'nlike':
|
||||
op = 'nlike'
|
||||
break
|
||||
case 'isNull':
|
||||
body += `!event.${filter.key}`
|
||||
break
|
||||
case 'isNotNull':
|
||||
body += `!!event.${filter.key}`
|
||||
break
|
||||
}
|
||||
if (filter.type === 'address') {
|
||||
value = `'${value.toLowerCase()}'`
|
||||
} else if (filter.type === 'utf8_data') {
|
||||
value = `'0x${utf8ToHex(value)}'`
|
||||
} else if (filter.type === 'hex_data') {
|
||||
value = `'${value.indexOf('0x') === 0 ? value : '0x'+value}'`
|
||||
} else if (filter.type === 'number') {
|
||||
value = parseInt(value)
|
||||
} else if (filter.type === 'boolean') {
|
||||
value = !!value
|
||||
} else {
|
||||
value = `'${value}'`
|
||||
}
|
||||
if (op) {
|
||||
if (op === 'in') {
|
||||
body += `event.${filter.key}.indexOf(${value}) >= 0`
|
||||
} else if (op === 'nin') {
|
||||
body += `!(event.${filter.key}.indexOf(${value}) >= 0)`
|
||||
} else if (op === 'nlike') {
|
||||
body += `!new RegExp(${value}).test(event.${filter.key})`
|
||||
} else if (op === 'like') {
|
||||
body += new RegExp(value).test(`event.${filter.key}`)
|
||||
} else {
|
||||
body += `event.${filter.key}${op}${value}`
|
||||
}
|
||||
}
|
||||
}
|
||||
return new Function('event', `return ${body}`)
|
||||
}
|
||||
}
|
@ -43,4 +43,4 @@ export const decodeEvent = (cfg: IEventCfg, eventData: {data: string, topics: st
|
||||
}
|
||||
}
|
||||
return decodedData
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user