From 12eb3d1f3be84a43829483fcde76ffa8667ed8f8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Ph=E1=BA=A1m=20Th=C3=A0nh=20Phong?= <49814372+phamphong9981@users.noreply.github.com> Date: Mon, 29 Jul 2024 09:40:30 +0700 Subject: [PATCH] Feat/erc20 track deposit withdraw staging (#866) TG-222 #staging * feat: erc20 track deposit & withdrawal * refactor: review * refactor: log error when decode activity fail * refactor: review --- ci/config.json.ci | 3 +- config.json | 3 +- src/services/evm/erc20.service.ts | 12 +- src/services/evm/erc20_handler.ts | 103 ++++++++++++++++-- .../unit/services/erc20/erc20_handler.spec.ts | 6 +- 5 files changed, 113 insertions(+), 14 deletions(-) diff --git a/ci/config.json.ci b/ci/config.json.ci index ec36fc231..bfa9b0e62 100644 --- a/ci/config.json.ci +++ b/ci/config.json.ci @@ -387,7 +387,8 @@ "key": "erc20", "blocksPerCall": 100, "millisecondRepeatJob": 5000, - "chunkSizeInsert": 1000 + "chunkSizeInsert": 1000, + "wrapExtensionContract": ["0xe974cc14c93fc6077b0d65f98832b846c5454a0b"] }, "erc721": { "key": "erc721", diff --git a/config.json b/config.json index 61a02c3b3..f1fbdbe00 100644 --- a/config.json +++ b/config.json @@ -390,7 +390,8 @@ "key": "erc20", "blocksPerCall": 100, "millisecondRepeatJob": 2000, - "chunkSizeInsert": 1000 + "chunkSizeInsert": 1000, + "wrapExtensionContract": ["0xe974cc14c93fc6077b0d65f98832b846c5454a0b"] }, "erc721": { "key": "erc721", diff --git a/src/services/evm/erc20.service.ts b/src/services/evm/erc20.service.ts index 16187d908..b02d28d29 100644 --- a/src/services/evm/erc20.service.ts +++ b/src/services/evm/erc20.service.ts @@ -144,15 +144,23 @@ export default class Erc20Service extends BullableService { const erc20Activities: Erc20Activity[] = []; erc20Events.forEach((e) => { if (e.topic0 === ERC20_EVENT_TOPIC0.TRANSFER) { - const activity = Erc20Handler.buildTransferActivity(e); + const activity = Erc20Handler.buildTransferActivity(e, this.logger); if (activity) { erc20Activities.push(activity); } } else if (e.topic0 === ERC20_EVENT_TOPIC0.APPROVAL) { - const activity = Erc20Handler.buildApprovalActivity(e); + const activity = Erc20Handler.buildApprovalActivity(e, this.logger); if (activity) { erc20Activities.push(activity); } + } else if (config.erc20.wrapExtensionContract.includes(e.address)) { + const wrapActivity = Erc20Handler.buildWrapExtensionActivity( + e, + this.logger + ); + if (wrapActivity) { + erc20Activities.push(wrapActivity); + } } }); erc20CosmosEvents.forEach((event) => { diff --git a/src/services/evm/erc20_handler.ts b/src/services/evm/erc20_handler.ts index 6946bda2c..7d60ac931 100644 --- a/src/services/evm/erc20_handler.ts +++ b/src/services/evm/erc20_handler.ts @@ -1,15 +1,17 @@ -import { decodeAbiParameters, keccak256, toHex } from 'viem'; -import Moleculer from 'moleculer'; import { Dictionary } from 'lodash'; +import Moleculer from 'moleculer'; +import { decodeAbiParameters, keccak256, toHex } from 'viem'; +import config from '../../../config.json' assert { type: 'json' }; import { Erc20Activity, Event, EventAttribute, EvmEvent } from '../../models'; import { AccountBalance } from '../../models/account_balance'; import { ZERO_ADDRESS } from './constant'; import { convertBech32AddressToEthAddress } from './utils'; -import config from '../../../config.json' assert { type: 'json' }; export const ERC20_ACTION = { TRANSFER: 'transfer', APPROVAL: 'approval', + DEPOSIT: 'deposit', + WITHDRAWAL: 'withdrawal', }; export const ABI_TRANSFER_PARAMS = { FROM: { @@ -42,6 +44,8 @@ export const ABI_APPROVAL_PARAMS = { export const ERC20_EVENT_TOPIC0 = { TRANSFER: keccak256(toHex('Transfer(address,address,uint256)')), APPROVAL: keccak256(toHex('Approval(address,address,uint256)')), + DEPOSIT: keccak256(toHex('Deposit(address,uint256)')), + WITHDRAWAL: keccak256(toHex('Withdrawal(address,uint256)')), }; export class Erc20Handler { // key: {accountId}_{erc20ContractAddress} @@ -60,7 +64,13 @@ export class Erc20Handler { process() { this.erc20Activities.forEach((erc20Activity) => { - if (erc20Activity.action === ERC20_ACTION.TRANSFER) { + if ( + [ + ERC20_ACTION.TRANSFER, + ERC20_ACTION.DEPOSIT, + ERC20_ACTION.WITHDRAWAL, + ].includes(erc20Activity.action) + ) { this.handlerErc20Transfer(erc20Activity); } }); @@ -113,7 +123,10 @@ export class Erc20Handler { } } - static buildTransferActivity(e: EvmEvent) { + static buildTransferActivity( + e: EvmEvent, + logger: Moleculer.LoggerInstance + ): Erc20Activity | undefined { try { const [from, to, amount] = decodeAbiParameters( [ @@ -135,7 +148,8 @@ export class Erc20Handler { tx_hash: e.tx_hash, evm_tx_id: e.evm_tx_id, }); - } catch { + } catch (e) { + logger.error(e); return undefined; } } @@ -209,7 +223,10 @@ export class Erc20Handler { } } - static buildApprovalActivity(e: EvmEvent) { + static buildApprovalActivity( + e: EvmEvent, + logger: Moleculer.LoggerInstance + ): Erc20Activity | undefined { try { const [from, to, amount] = decodeAbiParameters( [ @@ -231,7 +248,77 @@ export class Erc20Handler { tx_hash: e.tx_hash, evm_tx_id: e.evm_tx_id, }); - } catch { + } catch (e) { + logger.error(e); + return undefined; + } + } + + static buildWrapExtensionActivity( + e: EvmEvent, + logger: Moleculer.LoggerInstance + ): Erc20Activity | undefined { + if (e.topic0 === ERC20_EVENT_TOPIC0.DEPOSIT) { + const activity = Erc20Handler.buildWrapDepositActivity(e, logger); + return activity; + } + if (e.topic0 === ERC20_EVENT_TOPIC0.WITHDRAWAL) { + const activity = Erc20Handler.buildWrapWithdrawalActivity(e, logger); + return activity; + } + return undefined; + } + + private static buildWrapDepositActivity( + e: EvmEvent, + logger: Moleculer.LoggerInstance + ): Erc20Activity | undefined { + try { + const [to, amount] = decodeAbiParameters( + [ABI_TRANSFER_PARAMS.TO, ABI_APPROVAL_PARAMS.VALUE], + (e.topic1 + toHex(e.data).slice(2)) as `0x${string}` + ) as [string, bigint]; + return Erc20Activity.fromJson({ + evm_event_id: e.id, + sender: e.sender, + action: ERC20_ACTION.DEPOSIT, + erc20_contract_address: e.address, + amount: amount.toString(), + from: ZERO_ADDRESS, + to: to.toLowerCase(), + height: e.block_height, + tx_hash: e.tx_hash, + evm_tx_id: e.evm_tx_id, + }); + } catch (e) { + logger.error(e); + return undefined; + } + } + + private static buildWrapWithdrawalActivity( + e: EvmEvent, + logger: Moleculer.LoggerInstance + ): Erc20Activity | undefined { + try { + const [from, amount] = decodeAbiParameters( + [ABI_TRANSFER_PARAMS.FROM, ABI_APPROVAL_PARAMS.VALUE], + (e.topic1 + toHex(e.data).slice(2)) as `0x${string}` + ) as [string, bigint]; + return Erc20Activity.fromJson({ + evm_event_id: e.id, + sender: e.sender, + action: ERC20_ACTION.WITHDRAWAL, + erc20_contract_address: e.address, + amount: amount.toString(), + from: from.toLowerCase(), + to: ZERO_ADDRESS, + height: e.block_height, + tx_hash: e.tx_hash, + evm_tx_id: e.evm_tx_id, + }); + } catch (e) { + logger.error(e); return undefined; } } diff --git a/test/unit/services/erc20/erc20_handler.spec.ts b/test/unit/services/erc20/erc20_handler.spec.ts index ee197f667..f1119a634 100644 --- a/test/unit/services/erc20/erc20_handler.spec.ts +++ b/test/unit/services/erc20/erc20_handler.spec.ts @@ -60,7 +60,8 @@ export default class Erc20HandlerTest { toHex(evmEvent.data).slice(2)) as `0x${string}` ) as [string, string, bigint]; const result = Erc20Handler.buildTransferActivity( - EvmEvent.fromJson(evmEvent) + EvmEvent.fromJson(evmEvent), + this.broker.logger ); expect(result).toMatchObject({ evm_event_id: evmEvent.id, @@ -100,7 +101,8 @@ export default class Erc20HandlerTest { sender: 'evmos1u47fy86l8uaz4t0f533d4dctpjuhmm2dh3ezg0', }; const result = Erc20Handler.buildApprovalActivity( - EvmEvent.fromJson(evmEvent) + EvmEvent.fromJson(evmEvent), + this.broker.logger ); const [from, to, amount] = decodeAbiParameters( [