Skip to content

Commit

Permalink
feat: evm smart contract total transfer
Browse files Browse the repository at this point in the history
  • Loading branch information
phamphong9981 committed Oct 1, 2024
1 parent 39eced2 commit 02a0f48
Show file tree
Hide file tree
Showing 5 changed files with 173 additions and 15 deletions.
13 changes: 13 additions & 0 deletions migrations/evm/20240930084943_evm_smart_contract_total_tx_to.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import { Knex } from 'knex';

export async function up(knex: Knex): Promise<void> {
await knex.schema.alterTable('evm_smart_contract', (table) => {
table.integer('total_tx_to').defaultTo(0).index();
});
}

export async function down(knex: Knex): Promise<void> {
await knex.schema.alterTable('evm_smart_contract', (table) => {
table.dropColumn('total_tx_to');
});
}
2 changes: 2 additions & 0 deletions src/models/evm_smart_contract.ts
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@ export class EVMSmartContract extends BaseModel {

last_updated_tx_id!: number;

total_tx_to!: number;

static get tableName() {
return 'evm_smart_contract';
}
Expand Down
1 change: 1 addition & 0 deletions src/services/evm/constant.ts
Original file line number Diff line number Diff line change
Expand Up @@ -295,6 +295,7 @@ export const BULL_JOB_NAME = {
REINDEX_ERC20: 'reindex:erc20',
REFRESH_ERC721_HOLDER_STATISTIC: 'refresh:erc721-holder-statistic',
CRAWL_EVM_ACCOUNT_PUBKEY: 'crawl:evm-account-pubkey',
HANDLE_EVM_SMART_CONTRACT_TX: 'handle:evm-smart-contract-tx',
};

export const MSG_TYPE = {
Expand Down
78 changes: 78 additions & 0 deletions src/services/evm/crawl_contract_evm.service.ts
Original file line number Diff line number Diff line change
Expand Up @@ -328,6 +328,70 @@ export default class CrawlSmartContractEVMService extends BullableService {
});
}

@QueueHandler({
queueName: BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
jobName: BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
})
async handleEvmSmartContractTx() {
const [startBlock, endBlock, blockCheckpoint] =
await BlockCheckpoint.getCheckpoint(
BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
[BULL_JOB_NAME.CRAWL_SMART_CONTRACT_EVM],
config.crawlSmartContractEVM.key
);
this.logger.info(
`Handle evm_smart_contract tx from block ${startBlock} to block ${endBlock}`
);
if (startBlock >= endBlock) {
return;
}
const evmSmartContractTxs = await EVMTransaction.query()
.where('height', '>', startBlock)
.andWhere('height', '<=', endBlock)
.orderBy('height', 'asc')
.orderBy('index', 'asc');
const toAddrs = _.uniq(
evmSmartContractTxs.filter((e) => e.to).map((e) => bytesToHex(e.to))
);
const evmSmartContractsState = _.keyBy(
await EVMSmartContract.query()
.whereIn('address', toAddrs)
.select('address', 'total_tx_to'),
'address'
);
evmSmartContractTxs.forEach((tx) => {
if (!tx.to) return;
const toAddr = bytesToHex(tx.to);
const evmSmartContractState = evmSmartContractsState[toAddr];
if (evmSmartContractsState[toAddr]) {
evmSmartContractState.total_tx_to += 1;
}
});
await knex.transaction(async (trx) => {
if (Object.keys(evmSmartContractsState).length > 0) {
const stringListUpdates = Object.keys(evmSmartContractsState)
.map(
(addr) => `('${addr}', ${evmSmartContractsState[addr].total_tx_to})`
)
.join(',');
await knex
.raw(
`UPDATE evm_smart_contract SET total_tx_to = temp.total_tx_to from (VALUES ${stringListUpdates}) as temp(address, total_tx_to) where temp.address = evm_smart_contract.address`
)
.transacting(trx);
}
if (blockCheckpoint) {
blockCheckpoint.height = endBlock;
await BlockCheckpoint.query()
.insert(blockCheckpoint)
.onConflict('job_name')
.merge()
.returning('id')
.transacting(trx);
}
});
}

async getContractsProxyInfo(
addrs: string[],
bytecodes: _.Dictionary<GetBytecodeReturnType>,
Expand Down Expand Up @@ -452,6 +516,20 @@ export default class CrawlSmartContractEVMService extends BullableService {
},
}
);
await this.createJob(
BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
{},
{
removeOnComplete: true,
removeOnFail: {
count: 3,
},
repeat: {
every: config.crawlSmartContractEVM.millisecondCrawl,
},
}
);
return super._start();
}
}
94 changes: 79 additions & 15 deletions test/unit/services/evm/crawl_contract_evm.spec.ts
Original file line number Diff line number Diff line change
@@ -1,14 +1,35 @@
import { AfterAll, BeforeAll, Describe, Test } from '@jest-decorated/core';
import {
AfterAll,
BeforeAll,
BeforeEach,
Describe,
Test,
} from '@jest-decorated/core';
import { ServiceBroker } from 'moleculer';
import CrawlSmartContractEVMService from '../../../../src/services/evm/crawl_contract_evm.service';
import { hexToBytes } from 'viem';
import knex from '../../../../src/common/utils/db_connection';
import {
BlockCheckpoint,
EVMSmartContract,
EVMTransaction,
EvmInternalTransaction,
} from '../../../../src/models';
import { BULL_JOB_NAME } from '../../../../src/services/evm/constant';
import CrawlSmartContractEVMService from '../../../../src/services/evm/crawl_contract_evm.service';

const evmSmartContract = EVMSmartContract.fromJson({
id: 555,
address: '0xc57dc0ffa86aefbdd1b3f30e825fcae878a155f6',
creator: '0xDF587daaC47ae7B5586E34bCdb23d0b900b18a6C',
created_height: 100,
created_hash:
'0xae4b0793937b440f566ba5bdec4d3728a5c26cfc3233ca3a104ff0963841ac92',
type: EVMSmartContract.TYPES.ERC721,
code_hash:
'0xaf7378ea38b2c744796688746c4234e253647da6d8e7325a36f69c1ac0e53d2c',
total_tx_to: 25,
last_updated_tx_id: 5968,
});
@Describe('Test crawl contract evm')
export default class CrawlContractEvmTest {
broker = new ServiceBroker({ logger: false });
Expand All @@ -21,8 +42,12 @@ export default class CrawlContractEvmTest {
async initSuite() {
this.crawlContractEvmService.getQueueManager().stopAll();
await this.broker.start();
}

@BeforeEach()
async beforeEach() {
await knex.raw(
'TRUNCATE TABLE evm_smart_contract RESTART IDENTITY CASCADE'
'TRUNCATE TABLE evm_smart_contract, block_checkpoint, evm_transaction RESTART IDENTITY CASCADE'
);
}

Expand All @@ -33,20 +58,59 @@ export default class CrawlContractEvmTest {
jest.restoreAllMocks();
}

@Test.only('test handleEvmSmartContractTx')
async testHandleEvmSmartContractTx() {
const blockHeight = 1233;
const blockCheckpoints = [
BlockCheckpoint.fromJson({
job_name: BULL_JOB_NAME.HANDLE_EVM_SMART_CONTRACT_TX,
height: blockHeight - 1,
}),
BlockCheckpoint.fromJson({
job_name: BULL_JOB_NAME.CRAWL_SMART_CONTRACT_EVM,
height: blockHeight,
}),
];
const evmTxs = [
EVMTransaction.fromJson({
height: blockHeight,
to: hexToBytes(evmSmartContract.address as `0x${string}`),
index: 0,
hash: '0xeb6835058be18f6a13be74af2d06abbca46cdbd305aa4d378144d520310e8411',
}),
EVMTransaction.fromJson({
height: blockHeight,
to: hexToBytes(evmSmartContract.address as `0x${string}`),
index: 1,
hash: '0xeb6835058be18f6a13be74af2d06abbca46cdbd305aa4d378144d520310e8411',
}),
EVMTransaction.fromJson({
height: blockHeight,
from: hexToBytes(evmSmartContract.address as `0x${string}`),
index: 2,
hash: '0xeb6835058be18f6a13be74af2d06abbca46cdbd305aa4d378144d520310e8411',
}),
EVMTransaction.fromJson({
height: blockHeight,
to: hexToBytes(evmSmartContract.address as `0x${string}`),
index: 3,
hash: '0xeb6835058be18f6a13be74af2d06abbca46cdbd305aa4d378144d520310e8411',
}),
];
await BlockCheckpoint.query().insert(blockCheckpoints);
await EVMSmartContract.query().insert(evmSmartContract);
await EVMTransaction.query().insert(evmTxs);
await this.crawlContractEvmService.handleEvmSmartContractTx();
const updatedEvmSmartContract = await EVMSmartContract.query()
.first()
.throwIfNotFound();
expect(updatedEvmSmartContract.total_tx_to).toEqual(
evmSmartContract.total_tx_to + evmTxs.length - 1
);
}

@Test('test handleSelfDestruct')
async testHandleSelfDestruct() {
const evmSmartContract = EVMSmartContract.fromJson({
id: 555,
address: '0xC57dC0FFa86AEFbdD1b3F30E825fcAE878A155F6',
creator: '0xDF587daaC47ae7B5586E34bCdb23d0b900b18a6C',
created_height: 100,
created_hash:
'0xae4b0793937b440f566ba5bdec4d3728a5c26cfc3233ca3a104ff0963841ac92',
type: EVMSmartContract.TYPES.ERC721,
code_hash:
'0xaf7378ea38b2c744796688746c4234e253647da6d8e7325a36f69c1ac0e53d2c',
last_updated_tx_id: 5968,
});
await EVMSmartContract.query().insert(evmSmartContract);
jest.spyOn(BlockCheckpoint, 'getCheckpoint').mockResolvedValue([
1,
Expand Down

0 comments on commit 02a0f48

Please sign in to comment.