修改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
|
event.count = preDayEvent.count + 1
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await CheckIn.insertOrUpdate({ from: event.from, dataTag: event.dataTag }, event)
|
await CheckIn.insertOrUpdate({ from: event.from, dateTag: event.dateTag }, event)
|
||||||
} catch (err) {
|
} 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'
|
import { RedisClient } from 'redis/RedisClient'
|
||||||
const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development'
|
const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development'
|
||||||
dotenv.config({ path: envFile })
|
dotenv.config({ path: envFile })
|
||||||
import {IScriptionCfg, SCRIPTIONS_CFG} from 'config/scriptions_cfg'
|
const scriptions = require('../config/scription_list.json')
|
||||||
|
|
||||||
|
|
||||||
import 'common/Extend'
|
import 'common/Extend'
|
||||||
import { AllChains, IChain } from 'chain/allchain'
|
import { AllChains, IChain } from 'chain/allchain'
|
||||||
import { BlockSyncSvr } from 'service/block.sync.service'
|
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 svrs: any[] = []
|
||||||
let lock = false
|
let lock = false
|
||||||
|
|
||||||
|
let eventProcessers = {
|
||||||
|
CheckIn: CheckIn,
|
||||||
|
}
|
||||||
|
|
||||||
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_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)
|
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)
|
||||||
|
@ -1,10 +1,13 @@
|
|||||||
import { IChain } from "chain/allchain";
|
import { IChain } from "chain/allchain";
|
||||||
import { ethBlockNumber, retryEthBlockNumber } from "chain/chain.api";
|
import { retryEthBlockNumber } from "chain/chain.api";
|
||||||
import { IScriptionCfg } from "config/scriptions_cfg";
|
import { IScriptionCfg } from "interface/IScriptionCfg";
|
||||||
|
import logger from "logger/logger";
|
||||||
|
import { CheckIn } from "models/CheckIn";
|
||||||
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
|
||||||
scriptionCfgs: IScriptionCfg[] = []
|
scriptionCfgs: IScriptionCfg[] = []
|
||||||
@ -66,9 +69,14 @@ export class BlockSyncSvr {
|
|||||||
}
|
}
|
||||||
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(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 { batchEthBlocks } from "chain/chain.api";
|
||||||
|
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";
|
||||||
|
|
||||||
const MAX_BATCH_AMOUNT = 500
|
const MAX_BATCH_AMOUNT = 500
|
||||||
const REQUEST_INTERVAL = 0.5 * 1000
|
const REQUEST_INTERVAL = 0.5 * 1000
|
||||||
@ -84,3 +86,86 @@ export function* getPastBlocksIter({chainId, rpc, fromBlock, amount}
|
|||||||
remain -= MAX_BATCH_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
|
return decodedData
|
||||||
}
|
}
|
||||||
|
Loading…
x
Reference in New Issue
Block a user