From 2e6f22e9d868d0bd11319e0e48deebb88fbb574e Mon Sep 17 00:00:00 2001 From: hailin Date: Tue, 9 Dec 2025 06:10:36 -0800 Subject: [PATCH] fix(blockchain): read token decimals from contract instead of hardcoding MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The balance display was incorrect because decimals were hardcoded to 18, but USDT uses 6 decimals. Now reads decimals() from the ERC20 contract. 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude Opus 4.5 --- .../src/api/controllers/deposit.controller.ts | 25 ++----------------- .../services/balance-query.service.ts | 4 +++ .../blockchain/evm-provider.adapter.ts | 12 ++++++--- 3 files changed, 15 insertions(+), 26 deletions(-) diff --git a/backend/services/blockchain-service/src/api/controllers/deposit.controller.ts b/backend/services/blockchain-service/src/api/controllers/deposit.controller.ts index 8b2607c9..d3b9ce61 100644 --- a/backend/services/blockchain-service/src/api/controllers/deposit.controller.ts +++ b/backend/services/blockchain-service/src/api/controllers/deposit.controller.ts @@ -78,8 +78,8 @@ export class DepositController { chainType: chainTypeStr, address: addressStr, balance: balance.usdtBalance, - rawBalance: this.toRawBalance(balance.usdtBalance, this.getDecimals(chainTypeStr)), - decimals: this.getDecimals(chainTypeStr), + rawBalance: balance.usdtRawBalance, + decimals: balance.usdtDecimals, }; if (chainTypeStr === ChainTypeEnum.KAVA) { @@ -99,25 +99,4 @@ export class DepositController { return response; } - - private getDecimals(chainType: string): number { - // USDT decimals by chain - switch (chainType) { - case ChainTypeEnum.KAVA: - return 18; // Our test USDT on KAVA uses 18 decimals - case ChainTypeEnum.BSC: - return 18; // Standard USDT on BSC - default: - return 18; - } - } - - private toRawBalance(formattedBalance: string, decimals: number): string { - try { - const value = parseFloat(formattedBalance); - return BigInt(Math.floor(value * Math.pow(10, decimals))).toString(); - } catch { - return '0'; - } - } } diff --git a/backend/services/blockchain-service/src/application/services/balance-query.service.ts b/backend/services/blockchain-service/src/application/services/balance-query.service.ts index ef06f1f6..70db5af5 100644 --- a/backend/services/blockchain-service/src/application/services/balance-query.service.ts +++ b/backend/services/blockchain-service/src/application/services/balance-query.service.ts @@ -8,6 +8,8 @@ export interface BalanceResult { chainType: string; address: string; usdtBalance: string; + usdtRawBalance: string; + usdtDecimals: number; nativeBalance: string; nativeSymbol: string; } @@ -39,6 +41,8 @@ export class BalanceQueryService { chainType: chainType.toString(), address, usdtBalance: usdtBalance.formatted, + usdtRawBalance: usdtBalance.raw.toString(), + usdtDecimals: usdtBalance.decimals, nativeBalance: nativeBalance.formatted, nativeSymbol: config.nativeSymbol, }; diff --git a/backend/services/blockchain-service/src/infrastructure/blockchain/evm-provider.adapter.ts b/backend/services/blockchain-service/src/infrastructure/blockchain/evm-provider.adapter.ts index 6199acee..90becf86 100644 --- a/backend/services/blockchain-service/src/infrastructure/blockchain/evm-provider.adapter.ts +++ b/backend/services/blockchain-service/src/infrastructure/blockchain/evm-provider.adapter.ts @@ -10,7 +10,10 @@ const ERC20_TRANSFER_EVENT_ABI = [ ]; // ERC20 balanceOf ABI -const ERC20_BALANCE_ABI = ['function balanceOf(address owner) view returns (uint256)']; +const ERC20_BALANCE_ABI = [ + 'function balanceOf(address owner) view returns (uint256)', + 'function decimals() view returns (uint8)', +]; export interface TransferEvent { txHash: string; @@ -127,8 +130,11 @@ export class EvmProviderAdapter { ): Promise { const provider = this.getProvider(chainType); const contract = new Contract(tokenContract, ERC20_BALANCE_ABI, provider); - const balance = await contract.balanceOf(address); - return TokenAmount.fromRaw(balance, 18); + const [balance, decimals] = await Promise.all([ + contract.balanceOf(address), + contract.decimals(), + ]); + return TokenAmount.fromRaw(balance, Number(decimals)); } /**