diff --git a/src/abis/BEBadge.json b/config/abis/BEBadge.json similarity index 100% rename from src/abis/BEBadge.json rename to config/abis/BEBadge.json diff --git a/src/abis/BEMultiSigWallet.json b/config/abis/BEMultiSigWallet.json similarity index 100% rename from src/abis/BEMultiSigWallet.json rename to config/abis/BEMultiSigWallet.json diff --git a/src/abis/ERC1155.json b/config/abis/ERC1155.json similarity index 100% rename from src/abis/ERC1155.json rename to config/abis/ERC1155.json diff --git a/src/abis/ERC20.json b/config/abis/ERC20.json similarity index 100% rename from src/abis/ERC20.json rename to config/abis/ERC20.json diff --git a/src/abis/ERC721.json b/config/abis/ERC721.json similarity index 100% rename from src/abis/ERC721.json rename to config/abis/ERC721.json diff --git a/src/abis/NftDistributor.json b/config/abis/NftDistributor.json similarity index 100% rename from src/abis/NftDistributor.json rename to config/abis/NftDistributor.json diff --git a/config/event_abis/ERC20_Transfer.json b/config/event_abis/ERC20_Transfer.json new file mode 100644 index 0000000..5d817f6 --- /dev/null +++ b/config/event_abis/ERC20_Transfer.json @@ -0,0 +1,25 @@ +{ + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": false, + "internalType": "uint256", + "name": "value", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" +} \ No newline at end of file diff --git a/config/event_abis/ERC721_Transfer.json b/config/event_abis/ERC721_Transfer.json new file mode 100644 index 0000000..5772984 --- /dev/null +++ b/config/event_abis/ERC721_Transfer.json @@ -0,0 +1,25 @@ +{ + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "from", + "type": "address" + }, + { + "indexed": true, + "internalType": "address", + "name": "to", + "type": "address" + }, + { + "indexed": true, + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + } + ], + "name": "Transfer", + "type": "event" +} \ No newline at end of file diff --git a/config/event_abis/NFT_Redeem.json b/config/event_abis/NFT_Redeem.json new file mode 100644 index 0000000..6d5f131 --- /dev/null +++ b/config/event_abis/NFT_Redeem.json @@ -0,0 +1,46 @@ +{ + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "nft", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "start", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "stakeTime", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct ERC721Staking.Staker[]", + "name": "infos", + "type": "tuple[]" + } + ], + "name": "Redeem", + "type": "event" +} \ No newline at end of file diff --git a/config/event_abis/NFT_Stake.json b/config/event_abis/NFT_Stake.json new file mode 100644 index 0000000..91ca7f7 --- /dev/null +++ b/config/event_abis/NFT_Stake.json @@ -0,0 +1,46 @@ +{ + "anonymous": false, + "inputs": [ + { + "indexed": true, + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "components": [ + { + "internalType": "address", + "name": "user", + "type": "address" + }, + { + "internalType": "address", + "name": "nft", + "type": "address" + }, + { + "internalType": "uint256", + "name": "tokenId", + "type": "uint256" + }, + { + "internalType": "uint64", + "name": "start", + "type": "uint64" + }, + { + "internalType": "uint64", + "name": "stakeTime", + "type": "uint64" + } + ], + "indexed": false, + "internalType": "struct ERC721Staking.Staker[]", + "name": "infos", + "type": "tuple[]" + } + ], + "name": "Staked", + "type": "event" +} \ No newline at end of file diff --git a/config/event_list.json b/config/event_list.json new file mode 100644 index 0000000..b4b9df8 --- /dev/null +++ b/config/event_list.json @@ -0,0 +1,50 @@ +[ + { + "chain": 421614, + "address": "0xCD4bb3402f1a444a1AF10F31946Ed37DaC0eaC4d", + "event": "Transfer", + "abi": "ERC721_Transfer", + "fromBlock": 3549077, + "eventProcesser": "NftHolder" + }, + { + "chain": 421614, + "address": "0x6a673D946a976776fd5F163d9d831b2fEB600015", + "event": "Transfer", + "abi": "ERC721_Transfer", + "fromBlock": 5814045, + "eventProcesser": "NftHolder" + }, + { + "chain": 421614, + "address": "0x7b6399DFbed8Bc46F6A498C6B1040E80c2B5C4bc", + "event": "Transfer", + "abi": "ERC721_Transfer", + "fromBlock": 5814491, + "eventProcesser": "NftHolder" + }, + { + "chain": 421614, + "address": "0xe2E4D5a4045fBFcbCBECAf5b8A94303712d2FA97", + "event": "Transfer", + "abi": "ERC721_Transfer", + "fromBlock": 3549613, + "eventProcesser": "NftHolder" + }, + { + "chain": 421614, + "address": "0xd46fA2E72BA0F54092D0eF6a6e0D1d5660259C7a", + "event": "Staked", + "abi": "NFT_Stake", + "fromBlock": 3549891, + "eventProcesser": "NftStake" + }, + { + "chain": 421614, + "address": "0xd46fA2E72BA0F54092D0eF6a6e0D1d5660259C7a", + "event": "Staked", + "abi": "NFT_Redeem", + "fromBlock": 3549891, + "eventProcesser": "NftStake" + } +] \ No newline at end of file diff --git a/pm2_dev.sh b/pm2_dev.sh index 224198c..9d4892d 100755 --- a/pm2_dev.sh +++ b/pm2_dev.sh @@ -1,3 +1,3 @@ -pm2 start npm --name "chain-client" --log-date-format "YYYY-MM-DD HH:mm:ss" -- run "dev:api" -pm2 start npm --name "chain-scription" --log-date-format "YYYY-MM-DD HH:mm:ss" -- run "dev:scription" -pm2 start npm --name "chain-event" --log-date-format "YYYY-MM-DD HH:mm:ss" -- run "dev:event" \ No newline at end of file +pm2 start ts-node --name "chain:api" --log-date-format "YYYY-MM-DD HH:mm:ss" -- -r ./node_modules/tsconfig-paths/register ./src/api.ts +pm2 start ts-node --name "chain:scriptions" --log-date-format "YYYY-MM-DD HH:mm:ss" -- -r ./node_modules/tsconfig-paths/register ./src/scriptions.ts +pm2 start ts-node --name "chain:event" --log-date-format "YYYY-MM-DD HH:mm:ss" -- -r ./node_modules/tsconfig-paths/register ./src/events.ts \ No newline at end of file diff --git a/src/abis/Transfer.ts b/src/abis/Transfer.ts deleted file mode 100644 index fe3c2f2..0000000 --- a/src/abis/Transfer.ts +++ /dev/null @@ -1,145 +0,0 @@ -export const FT_TRANSFER_EVENT_ABI = { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": false, - "internalType": "uint256", - "name": "value", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" -} - -export const NFT_TRANSFER_EVENT_ABI = { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "from", - "type": "address" - }, - { - "indexed": true, - "internalType": "address", - "name": "to", - "type": "address" - }, - { - "indexed": true, - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - } - ], - "name": "Transfer", - "type": "event" -} - -export const NFT_STAKE_EVENT_ABI = { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "nft", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "start", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "stakeTime", - "type": "uint64" - } - ], - "indexed": false, - "internalType": "struct ERC721Staking.Staker[]", - "name": "infos", - "type": "tuple[]" - } - ], - "name": "Staked", - "type": "event" -} - -export const NFT_REDEEM_EVENT_ABI = { - "anonymous": false, - "inputs": [ - { - "indexed": true, - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "components": [ - { - "internalType": "address", - "name": "user", - "type": "address" - }, - { - "internalType": "address", - "name": "nft", - "type": "address" - }, - { - "internalType": "uint256", - "name": "tokenId", - "type": "uint256" - }, - { - "internalType": "uint64", - "name": "start", - "type": "uint64" - }, - { - "internalType": "uint64", - "name": "stakeTime", - "type": "uint64" - } - ], - "indexed": false, - "internalType": "struct ERC721Staking.Staker[]", - "name": "infos", - "type": "tuple[]" - } - ], - "name": "Redeem", - "type": "event" -} \ No newline at end of file diff --git a/src/api.server.ts b/src/api.server.ts index 6a177e1..2f9fcc6 100644 --- a/src/api.server.ts +++ b/src/api.server.ts @@ -7,9 +7,7 @@ import { IncomingMessage, Server, ServerResponse } from 'http' import { RouterMap } from 'decorators/router' import { mongoose } from '@typegoose/typegoose' import logger from 'logger/logger' -import BlocknumSchedule from 'schedule/blocknum.schedule' import { RedisClient } from 'redis/RedisClient' -import { restartAllUnFinishedTask } from 'service/chain.service' import { PriceSvr } from 'service/price.service' const zReqParserPlugin = require('plugins/zReqParser') @@ -159,7 +157,6 @@ export class ApiServer { self.setErrHandler() self.setFormatSend() self.initSchedules() - restartAllUnFinishedTask() this.server.listen( { port: parseInt(process.env.API_PORT), host: process.env.API_HOST }, (err: any, address: any) => { diff --git a/src/chain/DistributorReactor.ts b/src/chain/DistributorReactor.ts index acacde5..53ccb1d 100644 --- a/src/chain/DistributorReactor.ts +++ b/src/chain/DistributorReactor.ts @@ -1,7 +1,7 @@ import { Contract } from 'web3-eth-contract' import Web3 from 'web3' import { Account } from 'web3-core' -const abi = require('abis/NftDistributor.json').abi +const abi = require('../../config/abis/NftDistributor.json').abi export class DistributorReactor { private web3: Web3 diff --git a/src/chain/ERC20Reactor.ts b/src/chain/ERC20Reactor.ts index 5c4fd84..7a1ff19 100644 --- a/src/chain/ERC20Reactor.ts +++ b/src/chain/ERC20Reactor.ts @@ -7,7 +7,7 @@ import { AllChains } from './allchain' import { HttpRetryProvider } from './HttpRetryProvider' import { CHAIN_GAS_BOOST } from 'common/Constants' -const abiFt = require('abis/ERC20.json').abi +const abiFt = require('../../config/abis/ERC20.json').abi export class ERC20Reactor { private web3: Web3 private contract: Contract diff --git a/src/chain/ERC721Reactor.ts b/src/chain/ERC721Reactor.ts index bb6446b..3c6a845 100644 --- a/src/chain/ERC721Reactor.ts +++ b/src/chain/ERC721Reactor.ts @@ -11,7 +11,7 @@ export const ERC721_INTERFACE_ID = '0x80ac58cd' export const ERC721_METADATA_INTERFACE_ID = '0x5b5e139f' export const ERC721_ENUMERABLE_INTERFACE_ID = '0x780e9d63' -const abiNft = require('abis/BEBadge.json').abi +const abiNft = require('../../config/abis/BEBadge.json').abi export class ERC721Reactor { private web3: Web3 diff --git a/src/chain/WalletReactor.ts b/src/chain/WalletReactor.ts index a2b167a..fdc97c3 100644 --- a/src/chain/WalletReactor.ts +++ b/src/chain/WalletReactor.ts @@ -3,7 +3,7 @@ import Web3 from 'web3' import { Account } from 'web3-core' import { ZERO_BYTES32 } from 'common/Constants' import { generateRandomBytes32 } from 'utils/wallet.util' -const abi = require('abis/BEMultiSigWallet.json').abi +const abi = require('../../config/abis/BEMultiSigWallet.json').abi /** * ["address", "uint256", "bytes", "uint256", "bytes32"], diff --git a/src/chain/chain.api.ts b/src/chain/chain.api.ts index 6834fe1..a148518 100644 --- a/src/chain/chain.api.ts +++ b/src/chain/chain.api.ts @@ -35,7 +35,7 @@ export const ethGetBlockByNumber = async (rpc: string, blockNumber: string) => { } 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) => { diff --git a/src/config/events_cfg.ts b/src/config/events_cfg.ts deleted file mode 100644 index 6110f32..0000000 --- a/src/config/events_cfg.ts +++ /dev/null @@ -1,64 +0,0 @@ -import { NFT_REDEEM_EVENT_ABI, NFT_STAKE_EVENT_ABI, NFT_TRANSFER_EVENT_ABI } from 'abis/Transfer' -import { NftStake } from 'models/NftStake' -import { NftHolder } from 'models/NftHolder' - -export interface IEventCfg { - address: string, - event: string, - abi: any, - fromBlock: number, - eventProcesser?: string, - chain: number, - topic?: string, -} - -export const EVENTS_CFG: IEventCfg[] = [ - { // hero - chain: 421614, - address: "0xCD4bb3402f1a444a1AF10F31946Ed37DaC0eaC4d", - event: "Transfer", - abi: NFT_TRANSFER_EVENT_ABI, - fromBlock: 3549077, - eventProcesser: NftHolder, - }, - { // candy - chain: 421614, - address: "0x6a673D946a976776fd5F163d9d831b2fEB600015", - event: "Transfer", - abi: NFT_TRANSFER_EVENT_ABI, - fromBlock: 5814045, - eventProcesser: NftHolder, - }, - { // Explorer - chain: 421614, - address: "0x7b6399DFbed8Bc46F6A498C6B1040E80c2B5C4bc", - event: "Transfer", - abi: NFT_TRANSFER_EVENT_ABI, - fromBlock: 5814491, - eventProcesser: NftHolder, - }, - { // Gacha - chain: 421614, - address: "0xe2E4D5a4045fBFcbCBECAf5b8A94303712d2FA97", - event: "Transfer", - abi: NFT_TRANSFER_EVENT_ABI, - fromBlock: 3549613, - eventProcesser: NftHolder, - }, - { // Stake - chain: 421614, - address: "0xd46fA2E72BA0F54092D0eF6a6e0D1d5660259C7a", - event: "Staked", - abi: NFT_STAKE_EVENT_ABI, - fromBlock: 3549891, - eventProcesser: NftStake, - }, - { // Stake - chain: 421614, - address: "0xd46fA2E72BA0F54092D0eF6a6e0D1d5660259C7a", - event: "Staked", - abi: NFT_REDEEM_EVENT_ABI, - fromBlock: 3549891, - eventProcesser: NftStake, - }, -] \ No newline at end of file diff --git a/src/events.ts b/src/events.ts index 2096b4f..24d8373 100644 --- a/src/events.ts +++ b/src/events.ts @@ -4,12 +4,12 @@ import { RedisClient } from 'redis/RedisClient' const envFile = process.env.NODE_ENV && process.env.NODE_ENV === 'production' ? `.env.production` : '.env.development' dotenv.config({ path: envFile }) - +const events = require('../config/event_list.json') import 'common/Extend' import { AllChains, IChain } from 'chain/allchain' -import { EVENTS_CFG, IEventCfg } from 'config/events_cfg' import { EventBatchSvr } from 'service/event.batch.service' +import { IEventCfg } from 'interface/IEventCfg' let svrs: any[] = [] let lock = false @@ -17,8 +17,9 @@ let lock = false async function initEventSvrs() { const cfgMap: Map = new Map(); - for (let cfg of EVENTS_CFG) { + for (let cfg of events) { cfg.address = cfg.address.toLowerCase() + cfg.abi = require(`../config/event_abis/${cfg.abi}.json`) const chainCfg = AllChains.find((chain) => chain.id === cfg.chain) if (!chainCfg) { logger.error('chainCfg not found: ' + cfg.chain) diff --git a/src/interface/IEventCfg.ts b/src/interface/IEventCfg.ts new file mode 100644 index 0000000..561daa6 --- /dev/null +++ b/src/interface/IEventCfg.ts @@ -0,0 +1,9 @@ +export interface IEventCfg { + address: string, + event: string, + abi: any, + fromBlock: number, + eventProcesser?: string, + chain: number, + topic?: string, +} \ No newline at end of file diff --git a/src/models/FtHolder.ts b/src/models/FtHolder.ts deleted file mode 100644 index c8e9c8b..0000000 --- a/src/models/FtHolder.ts +++ /dev/null @@ -1,39 +0,0 @@ -import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' -import { dbconn } from 'decorators/dbconn' -import { BaseModule } from './Base' -import { GeneralEvent } from './GeneralEvent' - -@dbconn() -@index({ chain: 1, address: 1, user: 1 }, { unique: true }) -@modelOptions({ - schemaOptions: { collection: 'ft_holder', timestamps: true }, -}) -export class FtHolderClass extends BaseModule { - @prop({ required: true }) - public address!: string - - @prop({ required: true }) - public chain: string - - @prop({ required: true }) - public amount: string - - @prop() - public blockNumber: number - - @prop() - public user: string - - //TODO:: - public static async parseEvent(event: typeof GeneralEvent) { - const address = event.address; - const chain = event.chain; - const amount = event.amount; - const blockNumer = event.blockNumber; - - } -} - -export const FtHolder = getModelForClass(FtHolderClass, { - existingConnection: FtHolderClass['db'], -}) diff --git a/src/models/FtTransferEvent.ts b/src/models/FtTransferEvent.ts index 205edb3..ef2b33a 100644 --- a/src/models/FtTransferEvent.ts +++ b/src/models/FtTransferEvent.ts @@ -1,7 +1,7 @@ import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' import { dbconn } from 'decorators/dbconn' import { BaseModule } from './Base' -import { FtHolder } from './FtHolder' +import { TokenHolder } from './TokenHolder' @dbconn() @index({ address: 1 }, { unique: false }) @@ -61,7 +61,7 @@ export class FtTransferEventClass extends BaseModule { } let record = await FtTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data) - await FtHolder.saveData(record) + await TokenHolder.saveData(record) } } diff --git a/src/models/TokenHolder.ts b/src/models/TokenHolder.ts new file mode 100644 index 0000000..f6c33b8 --- /dev/null +++ b/src/models/TokenHolder.ts @@ -0,0 +1,60 @@ +import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose' +import { dbconn } from 'decorators/dbconn' +import { BaseModule } from './Base' +import { GeneralEvent } from './GeneralEvent' +import { ZERO_ADDRESS } from 'common/Constants' + +@dbconn() +@index({ chain: 1, address: 1, user: 1 }, { unique: true }) +@modelOptions({ + schemaOptions: { collection: 'token_holder', timestamps: true }, +}) +export class TokenHolderClass extends BaseModule { + + @prop({ required: true }) + public chain: string + + @prop({ required: true }) + public address!: string + + @prop() + public user: string + + @prop({ required: true }) + public amount: string + + @prop() + public blockNumber: number + + + public static async parseEvent(event: typeof GeneralEvent) { + const address = event.address.toLowerCase(); + const chain = event.chain; + const blockNumber = event.blockNumber; + let { from, to, value } = event.decodedData + from = from.toLowerCase() + to = to.toLowerCase() + if (from !== ZERO_ADDRESS) { + let record = await TokenHolder.findOne({ address, chain, user: from }) + if (record) { + record.amount = BigInt(record.amount) - BigInt(value) + record.blockNumber = blockNumber + } + await record.save(); + } + if (to !== ZERO_ADDRESS) { + let record = await TokenHolder.findOne({ address, chain, user: to }) + if (!record) { + record = new TokenHolder({ address, chain, user: to, amount: BigInt(value), blockNumber }) + } else { + record.amount = BigInt(record.amount) + BigInt(value) + record.blockNumber = blockNumber + } + await record.save(); + } + } +} + +export const TokenHolder = getModelForClass(TokenHolderClass, { + existingConnection: TokenHolderClass['db'], +}) diff --git a/src/service/event.batch.service.ts b/src/service/event.batch.service.ts index 00115a1..ce434f1 100644 --- a/src/service/event.batch.service.ts +++ b/src/service/event.batch.service.ts @@ -1,12 +1,22 @@ import { IChain } from "chain/allchain"; import { batchEthLogs, ethGetLogs } from "chain/chain.api"; -import { IEventCfg } from "config/events_cfg"; import logger from "logger/logger"; import { GeneralEvent } from "models/GeneralEvent"; import { RedisClient } from "redis/RedisClient"; import { decodeEvent, getTopics } from "utils/event.util"; +import { NftHolder } from 'models/NftHolder' +import { TokenHolder } from 'models/TokenHolder' +import { NftStake } from 'models/NftStake' +import { IEventCfg } from "interface/IEventCfg"; + +let eventProcessers = { + NftHolder: NftHolder, + TokenHolder: TokenHolder, + NftStake: NftStake +} + const MAX_TOPICS = 4 export class EventBatchSvr { @@ -77,7 +87,7 @@ export class EventBatchSvr { } } else if (uninParams.length == 1) { logger.debug(`unin params length 1, use eth_getLogs`) - let result = await ethGetLogs(this.rpc, uninParams[0]) + let result = await ethGetLogs(this.rpc, uninParams[0].params) if (result.error) { throw result.error } @@ -157,9 +167,8 @@ export class EventBatchSvr { event.decodedData = result const record = await GeneralEvent.saveEvent(event) blockNumber = Math.max(parseInt(event.blockNumber, 16), cfg.fromBlock) - if (cfg.eventProcesser) { - // @ts-ignore - await cfg.eventProcesser.parseEvent(record) + if (cfg.eventProcesser && eventProcessers[cfg.eventProcesser]) { + await eventProcessers[cfg.eventProcesser].parseEvent(record) } } return blockNumber diff --git a/src/utils/event.util.ts b/src/utils/event.util.ts index 8173edc..0c1a7b6 100644 --- a/src/utils/event.util.ts +++ b/src/utils/event.util.ts @@ -1,7 +1,8 @@ -import { IEventCfg } from "config/events_cfg"; + //@ts-ignore import { keccak256, _jsonInterfaceMethodToString, AbiInput } from "web3-utils"; import web3abi from 'web3-eth-abi'; +import { IEventCfg } from "interface/IEventCfg"; export const getTopics = (cfg: IEventCfg) => { // let abi = cfg.abi diff --git a/start_dev.json b/start_dev.json index d31cafc..d7f590e 100644 --- a/start_dev.json +++ b/start_dev.json @@ -4,7 +4,7 @@ "name": "chain-client", "script": "npm", "args": "run prod:api", - "cwd": "/data/apps/web_chain_client", + "cwd": "/root/code/web_chain_client", "max_memory_restart": "1024M", "log_date_format": "YYYY-MM-DD HH:mm Z", "watch": false,