diff --git a/src/dto/rpc/EvmBasedRpcInterface.ts b/src/dto/rpc/EvmBasedRpcInterface.ts index 4e85edd9d4..328d99881b 100644 --- a/src/dto/rpc/EvmBasedRpcInterface.ts +++ b/src/dto/rpc/EvmBasedRpcInterface.ts @@ -131,6 +131,8 @@ export interface EvmBasedRpcInterface { getTokenDecimals(tokenAddress: string): Promise> + getTokenSymbol(tokenAddress: string): Promise> + getContractAddress(txHash: string): Promise // web3_ methods diff --git a/src/service/address/address.ts b/src/service/address/address.ts index d8f6988569..bce7049530 100644 --- a/src/service/address/address.ts +++ b/src/service/address/address.ts @@ -13,7 +13,7 @@ import { CONFIG, Constant, ErrorUtils, ResponseDto, Utils } from '../../util' import { EvmRpc, GenericRpc, TronRpc } from '../rpc' import { Network, TatumConfig } from '../tatum' import { AddressBalance, AddressTransaction, GetAddressTransactionsQuery } from './address.dto' -import { decodeUInt256 } from '../../util/decode' +import { decodeHexString, decodeUInt256 } from '../../util/decode'; @Service({ factory: (data: { id: string }) => { @@ -124,17 +124,12 @@ export class Address { }) } - private async processTRC20TokenBalanceDetails(tokenBalances: {[key: string]: string}) { - const balances = Object.entries(tokenBalances[0]) - const serializedTokenBalance: Array = [] - for (let i = 0; i < balances.length; i++) { - const asset = await Utils.getRpc(this.id, this.config).triggerConstantContract( - balances[i][0], balances[i][0], 'symbol()', '', { visible: true } - ).then(r => decodeUInt256(r.constant_result[0])) - const decimals = await Utils.getRpc(this.id, this.config).triggerConstantContract( - balances[i][0], balances[i][0], 'decimals()', '', { visible: true } - ).then(r => decodeUInt256(r.constant_result[0])) - const balance = balances[i][1] + private async processTRC20TokenBalanceDetails(tokenBalances: [{[key: string]: string}]) { + const serializedTokenBalance = []; + for (let i = 0; i < tokenBalances.length; i++) { + const asset = await Utils.getRpc(this.id, this.config).triggerConstantContract(Object.keys(tokenBalances[i])[0], Object.keys(tokenBalances[i])[0], 'symbol()', '', { visible: true }).then(r => decodeHexString(r.constant_result[0])) + const decimals = await Utils.getRpc(this.id, this.config).triggerConstantContract(Object.keys(tokenBalances[i])[0], Object.keys(tokenBalances[i])[0], 'decimals()', '', { visible: true }).then(r => decodeUInt256(r.constant_result[0])) + const balance = Object.values(tokenBalances[i])[0]; serializedTokenBalance.push({ asset, decimals, @@ -271,7 +266,7 @@ export class Address { key: string, value: number, }] - trc20: {[key: string]: string} + trc20: [{[key: string]: string}] freeNetLimit: number, bandwidth: number, }>({ @@ -281,7 +276,7 @@ export class Address { { return Object.create({ nativeBalance: r.balance.toString(), - tokenBalances: await this.processTRC20TokenBalanceDetails(r.trc20), + tokenBalances: r.trc20.length > 0 ? await this.processTRC20TokenBalanceDetails(r.trc20) : [], }) }) } diff --git a/src/service/rpc/evm/AbstractEvmRpc.ts b/src/service/rpc/evm/AbstractEvmRpc.ts index e59a154d73..ce853b33dc 100644 --- a/src/service/rpc/evm/AbstractEvmRpc.ts +++ b/src/service/rpc/evm/AbstractEvmRpc.ts @@ -10,6 +10,7 @@ import { TraceType, TxPayload, } from '../../../dto' +import { decodeHexString } from '../../../util/decode'; @Service() export abstract class AbstractEvmRpc implements EvmBasedRpcInterface { @@ -150,6 +151,17 @@ export abstract class AbstractEvmRpc implements EvmBasedRpcInterface { return response } + async getTokenSymbol(tokenAddress: string): Promise> { + const response = await this.rpcCall>('eth_call', [ + { to: tokenAddress, data: '0x95d89b41' }, + 'latest', + ]) + if (response.result) { + response.result = decodeHexString(response.result) + } + return response + } + async getContractAddress(txHash: string): Promise { try { const txReceipt = await this.getTransactionReceipt(txHash) diff --git a/src/util/decode.ts b/src/util/decode.ts index 46c195177f..e4be749453 100644 --- a/src/util/decode.ts +++ b/src/util/decode.ts @@ -1,25 +1,21 @@ -export async function decodeUInt256(uint256Hex: string): Promise { - const hexString = uint256Hex.replace(/^0+/, '') // Remove leading zeros - const byteLength = hexString.length / 2 +export function decodeUInt256(hex: string): number { + const formattedHex = hex.replace(/^0x/, '') // Remove 0x + return Number('0x' + formattedHex) +} + +export function decodeHexString(hex: string): string { + const formattedHex = hex.replace(/^(0x)?0+/, '') // Remove 0x and leading zeros + const byteLength = formattedHex.length / 2 const bytes = [] for (let i = 0; i < byteLength; i++) { - const byte = parseInt(hexString.substr(i * 2, 2), 16) // Get the current byte + const byte = parseInt(formattedHex.substr(i * 2, 2), 16) // Get the current byte bytes.push(byte) } - const firstByte = BigInt(bytes[0]) // Convert the first byte to a BigInt - let decodedData - - if (firstByte < BigInt(10)) { - // If the first byte is in the range of ASCII digits (0-9), decode as number - decodedData = Number('0x' + uint256Hex) - } else { - decodedData = bytes - .map(byte => String.fromCharCode(byte)) - .filter(char => /[a-zA-Z0-9]/.test(char)) - .join('') - } - - return decodedData + return bytes + .map(byte => String.fromCharCode(byte)) + .filter(char => /[a-zA-Z0-9]/.test(char)) + .join('') } +