增加stake相关事件, 修改nft交易事件处理逻辑
This commit is contained in:
parent
4b246daf0d
commit
58aa777f33
@ -49,3 +49,97 @@ export const NFT_TRANSFER_EVENT_ABI = {
|
|||||||
"name": "Transfer",
|
"name": "Transfer",
|
||||||
"type": "event"
|
"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"
|
||||||
|
}
|
@ -1,12 +1,13 @@
|
|||||||
import { NFT_TRANSFER_EVENT_ABI } from 'abis/Transfer'
|
import { NFT_REDEEM_EVENT_ABI, NFT_STAKE_EVENT_ABI, NFT_TRANSFER_EVENT_ABI } from 'abis/Transfer'
|
||||||
import { NftTransferEvent } from 'models/NftTransferEvent'
|
import { NftStake } from 'models/NftStake'
|
||||||
|
import { NftHolder } from 'models/NftHolder'
|
||||||
|
|
||||||
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,
|
||||||
}
|
}
|
||||||
@ -18,7 +19,7 @@ export const EVENTS_CFG: IEventCfg[] = [
|
|||||||
event: "Transfer",
|
event: "Transfer",
|
||||||
abi: NFT_TRANSFER_EVENT_ABI,
|
abi: NFT_TRANSFER_EVENT_ABI,
|
||||||
fromBlock: 3549077,
|
fromBlock: 3549077,
|
||||||
eventProcesser: NftTransferEvent,
|
eventProcesser: NftHolder,
|
||||||
},
|
},
|
||||||
{ // candy
|
{ // candy
|
||||||
chain: 421614,
|
chain: 421614,
|
||||||
@ -26,7 +27,7 @@ export const EVENTS_CFG: IEventCfg[] = [
|
|||||||
event: "Transfer",
|
event: "Transfer",
|
||||||
abi: NFT_TRANSFER_EVENT_ABI,
|
abi: NFT_TRANSFER_EVENT_ABI,
|
||||||
fromBlock: 5814045,
|
fromBlock: 5814045,
|
||||||
eventProcesser: NftTransferEvent,
|
eventProcesser: NftHolder,
|
||||||
},
|
},
|
||||||
{ // Explorer
|
{ // Explorer
|
||||||
chain: 421614,
|
chain: 421614,
|
||||||
@ -34,7 +35,7 @@ export const EVENTS_CFG: IEventCfg[] = [
|
|||||||
event: "Transfer",
|
event: "Transfer",
|
||||||
abi: NFT_TRANSFER_EVENT_ABI,
|
abi: NFT_TRANSFER_EVENT_ABI,
|
||||||
fromBlock: 5814491,
|
fromBlock: 5814491,
|
||||||
eventProcesser: NftTransferEvent,
|
eventProcesser: NftHolder,
|
||||||
},
|
},
|
||||||
{ // Gacha
|
{ // Gacha
|
||||||
chain: 421614,
|
chain: 421614,
|
||||||
@ -42,6 +43,22 @@ export const EVENTS_CFG: IEventCfg[] = [
|
|||||||
event: "Transfer",
|
event: "Transfer",
|
||||||
abi: NFT_TRANSFER_EVENT_ABI,
|
abi: NFT_TRANSFER_EVENT_ABI,
|
||||||
fromBlock: 3549613,
|
fromBlock: 3549613,
|
||||||
eventProcesser: NftTransferEvent,
|
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,
|
||||||
},
|
},
|
||||||
]
|
]
|
@ -1,9 +1,8 @@
|
|||||||
import { ZERO_ADDRESS } from 'common/Constants'
|
|
||||||
import { ZError } from 'common/ZError'
|
import { ZError } from 'common/ZError'
|
||||||
import BaseController from 'common/base.controller'
|
import BaseController from 'common/base.controller'
|
||||||
import { role, router } from 'decorators/router'
|
import { role, router } from 'decorators/router'
|
||||||
import { CheckIn } from 'models/CheckIn'
|
import { CheckIn } from 'models/CheckIn'
|
||||||
import { NftTransferEvent } from 'models/NftTransferEvent'
|
import { NftHolder } from 'models/NftHolder'
|
||||||
import { getMonthBegin, getNDayAgo } from 'utils/date.util'
|
import { getMonthBegin, getNDayAgo } from 'utils/date.util'
|
||||||
|
|
||||||
|
|
||||||
@ -65,13 +64,13 @@ class TaskController extends BaseController {
|
|||||||
address = address.toLowerCase()
|
address = address.toLowerCase()
|
||||||
user = user.toLowerCase()
|
user = user.toLowerCase()
|
||||||
|
|
||||||
let records = await NftTransferEvent.find({address, chain, from: user, to: ZERO_ADDRESS}).sort({_id: -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({
|
||||||
address: record.address,
|
address: record.address,
|
||||||
chain: record.chain,
|
chain: record.chain,
|
||||||
user: record.from,
|
user: record.user,
|
||||||
tokenId: record.tokenId,
|
tokenId: record.tokenId,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
39
src/models/FtHolder.ts
Normal file
39
src/models/FtHolder.ts
Normal file
@ -0,0 +1,39 @@
|
|||||||
|
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'],
|
||||||
|
})
|
@ -1,55 +1,67 @@
|
|||||||
import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose'
|
import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose'
|
||||||
import { dbconn } from 'decorators/dbconn'
|
import { dbconn } from 'decorators/dbconn'
|
||||||
import { BaseModule } from './Base'
|
import { BaseModule } from './Base'
|
||||||
|
import { FtHolder } from './FtHolder'
|
||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ address: 1 }, { unique: false })
|
@index({ address: 1 }, { unique: false })
|
||||||
@index({ transactionHash: 1, from: 1, to: 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 },
|
||||||
})
|
})
|
||||||
export class FtTransferEventClass extends BaseModule {
|
export class FtTransferEventClass extends BaseModule {
|
||||||
@prop({ required: true })
|
@prop({ required: true })
|
||||||
public address!: string
|
public address!: string
|
||||||
|
@prop({ required: true })
|
||||||
|
public chain: string
|
||||||
|
@prop({ required: true })
|
||||||
|
public logIndex: number
|
||||||
@prop()
|
@prop()
|
||||||
public event: string
|
public event: string
|
||||||
|
// event hash
|
||||||
@prop({ required: true })
|
@prop({ required: true })
|
||||||
public transactionHash: string
|
public hash: string
|
||||||
@prop()
|
@prop()
|
||||||
public blockNumber: number
|
public blockNumber: number
|
||||||
@prop()
|
@prop()
|
||||||
public blockHash: string
|
public blockHash: string
|
||||||
@prop()
|
@prop()
|
||||||
public removed: boolean
|
public removed: boolean
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public from: string
|
public from: string
|
||||||
@prop()
|
@prop()
|
||||||
public to: string
|
public to: string
|
||||||
@prop()
|
@prop()
|
||||||
public amount: string
|
public amount: string
|
||||||
|
|
||||||
@prop()
|
@prop()
|
||||||
public blockTime: number
|
public blockTime: number
|
||||||
|
@prop({ default: 0 })
|
||||||
|
public version: number
|
||||||
|
|
||||||
public static async saveEvent(event: any) {
|
public static async saveEvent(event: any) {
|
||||||
const amount = event.returnValues?.value
|
const amount = event.returnValues?.value
|
||||||
if (amount == undefined) {
|
if (amount == undefined) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const from = event.returnValues?.from
|
|
||||||
const to = event.returnValues?.to
|
const logIndex = parseInt(event.logIndex || '0')
|
||||||
|
const from = event.decodedData.from.toLowerCase()
|
||||||
|
const to = event.decodedData.to.toLowerCase()
|
||||||
|
const hash = event.hash || event.transactionHash
|
||||||
const data = {
|
const data = {
|
||||||
address: event.address,
|
address: event.address.toLowerCase(),
|
||||||
blockNumber: event.blockNumber,
|
blockNumber: parseInt(event.blockNumber),
|
||||||
blockHash: event.blockHash,
|
|
||||||
removed: event.removed,
|
removed: event.removed,
|
||||||
event: event.event,
|
|
||||||
from,
|
from,
|
||||||
to,
|
to,
|
||||||
transactionHash: event.transactionHash,
|
|
||||||
amount,
|
amount,
|
||||||
blockTime: event.timestamp * 1000
|
$inc: { version: 1 },
|
||||||
}
|
}
|
||||||
return FtTransferEvent.insertOrUpdate({ transactionHash: event.transactionHash, amount, from, to }, data)
|
|
||||||
|
let record = await FtTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
|
await FtHolder.saveData(record)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
60
src/models/GeneralEvent.ts
Normal file
60
src/models/GeneralEvent.ts
Normal file
@ -0,0 +1,60 @@
|
|||||||
|
import { Severity, getModelForClass, index, modelOptions, mongoose, prop } from '@typegoose/typegoose'
|
||||||
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { BaseModule } from './Base'
|
||||||
|
import { NftHolder } from './NftHolder'
|
||||||
|
|
||||||
|
@dbconn()
|
||||||
|
@index({ chain: 1, hash: 1, logIndex: 1}, { unique: true })
|
||||||
|
@modelOptions({
|
||||||
|
schemaOptions: { collection: 'general_event', timestamps: true },
|
||||||
|
options: { allowMixed: Severity.ALLOW },
|
||||||
|
})
|
||||||
|
export class GeneralEventClass extends BaseModule {
|
||||||
|
@prop({ required: true })
|
||||||
|
public address!: string
|
||||||
|
@prop({ required: true })
|
||||||
|
public chain: string
|
||||||
|
@prop({ required: true })
|
||||||
|
public logIndex: number
|
||||||
|
@prop()
|
||||||
|
public event: string
|
||||||
|
// event hash
|
||||||
|
@prop({ required: true })
|
||||||
|
public hash: string
|
||||||
|
@prop()
|
||||||
|
public blockNumber: number
|
||||||
|
@prop()
|
||||||
|
public blockHash: string
|
||||||
|
@prop()
|
||||||
|
public removed: boolean
|
||||||
|
|
||||||
|
@prop({ type: mongoose.Schema.Types.Mixed})
|
||||||
|
public decodedData: any
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public blockTime: number
|
||||||
|
@prop({ default: 0 })
|
||||||
|
public version: number
|
||||||
|
|
||||||
|
|
||||||
|
public static async saveEvent(event: any) {
|
||||||
|
const logIndex = parseInt(event.logIndex || '0')
|
||||||
|
const hash = event.hash || event.transactionHash
|
||||||
|
const data = {
|
||||||
|
address: event.address.toLowerCase(),
|
||||||
|
blockNumber: parseInt(event.blockNumber),
|
||||||
|
removed: event.removed,
|
||||||
|
decodedData: event.decodedData,
|
||||||
|
blockHash: event.blockHash,
|
||||||
|
event: event.event,
|
||||||
|
$inc: { version: 1 },
|
||||||
|
}
|
||||||
|
|
||||||
|
let record = await GeneralEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
|
return record
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const GeneralEvent = getModelForClass(GeneralEventClass, {
|
||||||
|
existingConnection: GeneralEventClass['db'],
|
||||||
|
})
|
@ -2,6 +2,7 @@ import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoos
|
|||||||
import { dbconn } from 'decorators/dbconn'
|
import { dbconn } from 'decorators/dbconn'
|
||||||
import { BaseModule } from './Base'
|
import { BaseModule } from './Base'
|
||||||
import { ZERO_ADDRESS } from 'common/Constants'
|
import { ZERO_ADDRESS } from 'common/Constants'
|
||||||
|
import { GeneralEvent } from './GeneralEvent'
|
||||||
|
|
||||||
@dbconn()
|
@dbconn()
|
||||||
@index({ chain: 1, address: 1, tokenId: 1 }, { unique: true })
|
@index({ chain: 1, address: 1, tokenId: 1 }, { unique: true })
|
||||||
@ -23,29 +24,24 @@ export class NftHolderClass extends BaseModule {
|
|||||||
@prop({default: false})
|
@prop({default: false})
|
||||||
public burn: boolean
|
public burn: boolean
|
||||||
|
|
||||||
|
public static async parseEvent(event: typeof GeneralEvent) {
|
||||||
public static async saveData(event: any) {
|
|
||||||
const address = event.address;
|
const address = event.address;
|
||||||
const chain = event.chain;
|
const chain = event.chain;
|
||||||
const tokenId = event.tokenId;
|
const tokenId = event.decodedData.tokenId;
|
||||||
const blockNumer = event.blockNumber;
|
const blockNumer = event.blockNumber;
|
||||||
const burn = event.to === ZERO_ADDRESS
|
const burn = event.decodedData.to === ZERO_ADDRESS
|
||||||
|
const user = burn ? event.decodedData.from : event.decodedData.to
|
||||||
let record = await NftHolder.findOne({ address, chain, tokenId })
|
let record = await NftHolder.findOne({ address, chain, tokenId })
|
||||||
if (!record) {
|
if (!record) {
|
||||||
record = new NftHolder({ address, chain, tokenId, blockNumber: blockNumer, user: event.to, burn })
|
record = new NftHolder({ address, chain, tokenId, blockNumber: blockNumer, user, burn })
|
||||||
} else {
|
} else {
|
||||||
if (record.blockNumber < blockNumer) {
|
if (record.blockNumber < blockNumer) {
|
||||||
if (burn) {
|
record.user = user
|
||||||
record.burn = true
|
record.burn = burn
|
||||||
} else {
|
|
||||||
record.user = event.to
|
|
||||||
}
|
|
||||||
record.blockNumber = blockNumer
|
record.blockNumber = blockNumer
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
await record.save();
|
await record.save();
|
||||||
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
106
src/models/NftStake.ts
Normal file
106
src/models/NftStake.ts
Normal file
@ -0,0 +1,106 @@
|
|||||||
|
import { getModelForClass, index, modelOptions, prop } from '@typegoose/typegoose'
|
||||||
|
import { dbconn } from 'decorators/dbconn'
|
||||||
|
import { BaseModule } from './Base'
|
||||||
|
import logger from 'logger/logger'
|
||||||
|
import { GeneralEvent } from './GeneralEvent'
|
||||||
|
|
||||||
|
|
||||||
|
const STAKE_EVENT = 'Staked'
|
||||||
|
const REDEEM_EVENT = 'Redeem'
|
||||||
|
|
||||||
|
@dbconn()
|
||||||
|
@index({ chain: 1, nft: 1, tokenId: 1, start: 1 }, { unique: true })
|
||||||
|
@index({ chain:1, user: 1, nft: 1}, {unique: false})
|
||||||
|
@modelOptions({
|
||||||
|
schemaOptions: { collection: 'nft_stake_info', timestamps: true },
|
||||||
|
})
|
||||||
|
export class NftStakeClass extends BaseModule {
|
||||||
|
@prop({ required: true })
|
||||||
|
public chain: string
|
||||||
|
@prop()
|
||||||
|
public blockNumber: number
|
||||||
|
// stake info
|
||||||
|
@prop()
|
||||||
|
public address: string
|
||||||
|
@prop()
|
||||||
|
public user: string
|
||||||
|
@prop()
|
||||||
|
public nft: string
|
||||||
|
@prop()
|
||||||
|
public tokenId: string
|
||||||
|
@prop()
|
||||||
|
public start: number
|
||||||
|
@prop()
|
||||||
|
public stakeTime: number
|
||||||
|
// 1: staked, 2: redeemed
|
||||||
|
@prop()
|
||||||
|
public status: number
|
||||||
|
|
||||||
|
@prop()
|
||||||
|
public redeemTime: number
|
||||||
|
|
||||||
|
@prop({ default: 0 })
|
||||||
|
public version: number
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
*
|
||||||
|
{
|
||||||
|
user: '0x50A8e60041A206AcaA5F844a1104896224be6F39',
|
||||||
|
infos: [
|
||||||
|
{
|
||||||
|
user: '0x50A8e60041A206AcaA5F844a1104896224be6F39',
|
||||||
|
nft: '0x09F0dFFA584B1277D7c4E44265a6b5D03303Fc99',
|
||||||
|
tokenId: '1',
|
||||||
|
start: '1704870143',
|
||||||
|
stakeTime: '2592000'
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
public static async parseEvent(event: typeof GeneralEvent) {
|
||||||
|
const { user, infos } = event.decodedData
|
||||||
|
const isStake = event.event === STAKE_EVENT
|
||||||
|
const blockNumber = event.blockNumber
|
||||||
|
const chain = event.chain
|
||||||
|
if (infos.length === 0) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
for (let info of infos) {
|
||||||
|
let { nft, tokenId, start, stakeTime } = info
|
||||||
|
start = parseInt(start)
|
||||||
|
stakeTime = parseInt(stakeTime)
|
||||||
|
if (isStake) {
|
||||||
|
logger.info(`stake nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
||||||
|
let record = new NftStake({
|
||||||
|
address: event.address,
|
||||||
|
chain,
|
||||||
|
nft,
|
||||||
|
tokenId,
|
||||||
|
user,
|
||||||
|
blockNumber,
|
||||||
|
status: 1,
|
||||||
|
start,
|
||||||
|
stakeTime
|
||||||
|
})
|
||||||
|
await record.save()
|
||||||
|
} else {
|
||||||
|
logger.info(`redeem nft: ${nft}, tokenId: ${tokenId}, user: ${user}, blockNumber: ${blockNumber}`)
|
||||||
|
await NftStake.insertOrUpdate(
|
||||||
|
{ chain, nft, tokenId, start},
|
||||||
|
{
|
||||||
|
status: 2,
|
||||||
|
address: event.address,
|
||||||
|
user,
|
||||||
|
redeemTime: Date.now() / 1000 | 0,
|
||||||
|
blockNumber,
|
||||||
|
$inc: { version: 1 },
|
||||||
|
})
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
export const NftStake = getModelForClass(NftStakeClass, {
|
||||||
|
existingConnection: NftStakeClass['db'],
|
||||||
|
})
|
@ -39,13 +39,13 @@ export class NftTransferEventClass extends BaseModule {
|
|||||||
public version: number
|
public version: number
|
||||||
|
|
||||||
public static async saveEvent(event: any) {
|
public static async saveEvent(event: any) {
|
||||||
const tokenId = event.tokenId || event.value
|
const tokenId = event.decodedData.tokenId || event.decodedData.value
|
||||||
if (!tokenId) {
|
if (!tokenId) {
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
const logIndex = parseInt(event.logIndex || '0')
|
const logIndex = parseInt(event.logIndex || '0')
|
||||||
const from = event.from.toLowerCase()
|
const from = event.decodedData.from.toLowerCase()
|
||||||
const to = event.to.toLowerCase()
|
const to = event.decodedData.to.toLowerCase()
|
||||||
const hash = event.hash || event.transactionHash
|
const hash = event.hash || event.transactionHash
|
||||||
const data = {
|
const data = {
|
||||||
address: event.address.toLowerCase(),
|
address: event.address.toLowerCase(),
|
||||||
@ -58,8 +58,7 @@ export class NftTransferEventClass extends BaseModule {
|
|||||||
$inc: { version: 1 },
|
$inc: { version: 1 },
|
||||||
}
|
}
|
||||||
|
|
||||||
let record = NftTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
await NftTransferEvent.insertOrUpdate({ hash, logIndex, chain: event.chain }, data)
|
||||||
await NftHolder.saveData(record)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -2,11 +2,10 @@ import { IChain } from "chain/allchain";
|
|||||||
import { batchEthLogs, ethBlockNumber } from "chain/chain.api";
|
import { batchEthLogs, ethBlockNumber } from "chain/chain.api";
|
||||||
import { IEventCfg } from "config/events_cfg";
|
import { IEventCfg } from "config/events_cfg";
|
||||||
import logger from "logger/logger";
|
import logger from "logger/logger";
|
||||||
|
import { GeneralEvent } from "models/GeneralEvent";
|
||||||
|
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from "redis/RedisClient";
|
||||||
import { getPastBlocksIter } from "utils/block.util";
|
import { decodeEvent, getTopics } from "utils/event.util";
|
||||||
import { getTopics } from "utils/event.util";
|
|
||||||
import web3abi from 'web3-eth-abi';
|
|
||||||
|
|
||||||
|
|
||||||
export class EventBatchSvr {
|
export class EventBatchSvr {
|
||||||
@ -88,14 +87,18 @@ export class EventBatchSvr {
|
|||||||
const topic = events[0].topics[0]
|
const topic = events[0].topics[0]
|
||||||
const cfg = this.processer.get(address+topic)
|
const cfg = this.processer.get(address+topic)
|
||||||
logger.info(`process events: ${cfg.chain} | ${address} | ${cfg.event} | ${events.length}`)
|
logger.info(`process events: ${cfg.chain} | ${address} | ${cfg.event} | ${events.length}`)
|
||||||
const abiInputs = cfg.abi.inputs;
|
|
||||||
for (const event of events) {
|
for (const event of events) {
|
||||||
let result = web3abi.decodeLog(abiInputs, event.data, event.topics.slice(1));
|
event.chain = this.chainCfg.id + ''
|
||||||
result.chain = this.chainCfg.id + ''
|
event.event = 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)
|
||||||
Object.assign(result, event)
|
event.decodedData = result
|
||||||
// @ts-ignore
|
const record = await GeneralEvent.saveEvent(event)
|
||||||
await cfg.eventProcesser.saveEvent(result)
|
if (cfg.eventProcesser) {
|
||||||
|
// @ts-ignore
|
||||||
|
await cfg.eventProcesser.parseEvent(record)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
const redisKey = this.buildRedisKey(cfg)
|
const redisKey = this.buildRedisKey(cfg)
|
||||||
await new RedisClient().set(redisKey, cfg.fromBlock + '')
|
await new RedisClient().set(redisKey, cfg.fromBlock + '')
|
||||||
|
@ -1,8 +1,6 @@
|
|||||||
import { batchEthBlocks } from "chain/chain.api";
|
import { batchEthBlocks } from "chain/chain.api";
|
||||||
import logger from "logger/logger";
|
import logger from "logger/logger";
|
||||||
import { RedisClient } from "redis/RedisClient";
|
import { RedisClient } from "redis/RedisClient";
|
||||||
import { retry } from "./promise.util";
|
|
||||||
import { parse } from "path";
|
|
||||||
|
|
||||||
const MAX_BATCH_AMOUNT = 500
|
const MAX_BATCH_AMOUNT = 500
|
||||||
const REQUEST_INTERVAL = 0.5 * 1000
|
const REQUEST_INTERVAL = 0.5 * 1000
|
||||||
|
@ -1,13 +1,45 @@
|
|||||||
import { IEventCfg } from "config/events_cfg";
|
import { IEventCfg } from "config/events_cfg";
|
||||||
import { keccak256 } from "web3-utils";
|
//@ts-ignore
|
||||||
|
import { keccak256, _jsonInterfaceMethodToString, AbiInput } from "web3-utils";
|
||||||
|
import web3abi from 'web3-eth-abi';
|
||||||
|
|
||||||
export const getTopics = (cfg: IEventCfg) => {
|
export const getTopics = (cfg: IEventCfg) => {
|
||||||
let abi = cfg.abi
|
// let abi = cfg.abi
|
||||||
let topic = 'Transfer('
|
// let topic = `${abi.name}(`
|
||||||
for (let item of abi.inputs) {
|
// for (let item of abi.inputs) {
|
||||||
topic += item.type + ','
|
// topic += item.type + ','
|
||||||
}
|
// }
|
||||||
topic = topic.slice(0, -1)
|
// topic = topic.slice(0, -1)
|
||||||
topic += ')'
|
// topic += ')'
|
||||||
return keccak256(topic)
|
return keccak256(_jsonInterfaceMethodToString(cfg.abi))
|
||||||
|
}
|
||||||
|
|
||||||
|
export const decodeEvent = (cfg: IEventCfg, eventData: {data: string, topics: string[]}) => {
|
||||||
|
const abiInputs = cfg.abi.inputs;
|
||||||
|
let result = web3abi.decodeLog(abiInputs, eventData.data, eventData.topics.slice(1));
|
||||||
|
let decodedData: any = {}
|
||||||
|
for (let i = 0; i < abiInputs.length; i++) {
|
||||||
|
const input: AbiInput = abiInputs[i];
|
||||||
|
if (input.type === 'tuple[]') {
|
||||||
|
// @ts-ignore
|
||||||
|
decodedData[input.name] = result[i].map(item => {
|
||||||
|
let itemData = {}
|
||||||
|
for (let j = 0; j < input.components.length; j++) {
|
||||||
|
const component = input.components[j];
|
||||||
|
itemData[component.name] = item[j]
|
||||||
|
}
|
||||||
|
return itemData
|
||||||
|
})
|
||||||
|
} else if (input.type === 'tuple') {
|
||||||
|
let itemData = {}
|
||||||
|
for (let j = 0; j < input.components.length; j++) {
|
||||||
|
const component = input.components[j];
|
||||||
|
itemData[component.name] = result[i][j]
|
||||||
|
}
|
||||||
|
decodedData[input.name] = itemData
|
||||||
|
} else {
|
||||||
|
decodedData[input.name] = result[i]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return decodedData
|
||||||
}
|
}
|
Loading…
x
Reference in New Issue
Block a user